fix(claude): stop pre-migration .mcp.json residue outranking a project switch
`mcpctl config claude --project X` writes user scope and never rewrites a checkout's `.mcp.json`. The status line, however, preferred that file unconditionally — so a legacy project-named entry an older mcpctl left behind (`homeautomation` -> `mcpctl mcp -p homeautomation`) kept naming the old project for good, and every switch looked like it had done nothing. Reproduced live: with user scope on `sre`, `mcpctl statusline --directory ~/developer/michalzxc/claude/debug` printed `mcpctl:homeautomation` — a project Claude Code also had in `disabledMcpServers` for that directory, so the line named a server that was not even mounted. Rank the sources by how deliberate each one is instead: a canonical `mcpctl` pin, then user scope, then legacy residue, then the marker. A pin is a decision and still wins; residue is not and no longer does. At every step, skip a server Claude Code has switched off for that directory. `config claude` now also warns when the working directory's `.mcp.json` contradicts the switch, naming the file — the two scopes are merged rather than chosen between, so nothing else would tell you. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019wUmrfkVQR6CKcYKxENq7k
This commit is contained in:
@@ -101,13 +101,48 @@ to open, and re-scoping it would silently change which skills sync into it.
|
||||
⏵⏵ bypass permissions on · ← for agents
|
||||
```
|
||||
|
||||
`mcpctl statusline` resolves the project from a directory-scoped `.mcp.json`
|
||||
first (a repo that pinned itself wins), then the user-scope entry in
|
||||
`.claude.json`, then a `.mcpctl-project` marker up the tree so a checkout that is scoped but not yet
|
||||
wired still reports. It reads the directory from the JSON Claude Code pipes in,
|
||||
so it follows `/cwd` rather than reporting wherever the binary was launched, and
|
||||
prints **nothing** when no project is active — an empty status line beats one
|
||||
saying "none" on every unrelated repo.
|
||||
`mcpctl statusline` reads the directory from the JSON Claude Code pipes in, so it
|
||||
follows `/cwd` rather than reporting wherever the binary was launched, and prints
|
||||
**nothing** when no project is active — an empty status line beats one saying
|
||||
"none" on every unrelated repo.
|
||||
|
||||
It then takes the project from the most deliberate source that names one:
|
||||
|
||||
1. a canonical `mcpctl` entry in that directory's `.mcp.json` — a repo that
|
||||
pinned itself wins, and it is the scope Claude Code itself prefers when both
|
||||
define that server name;
|
||||
2. the user-scope entry in `.claude.json` — what `config claude --project`
|
||||
writes, so a switch takes effect everywhere it is not overridden;
|
||||
3. a **legacy** project-named entry in `.mcp.json` (`homeautomation`,
|
||||
`docmost`, …), left by an mcpctl older than the constant server name;
|
||||
4. a `.mcpctl-project` marker up the tree, so a checkout that is scoped but not
|
||||
yet wired still reports.
|
||||
|
||||
> **Legacy entries rank below user scope on purpose.** They used to outrank it,
|
||||
> which made switching look broken: a user-scope switch never rewrites a
|
||||
> checkout's `.mcp.json`, so the leftover kept naming the old project for good.
|
||||
> A pin is a decision; residue is not.
|
||||
|
||||
A server Claude Code has switched off for that directory (`disabledMcpServers` /
|
||||
`disabledMcpjsonServers`) is skipped at every step — a disabled server is not
|
||||
mounted, so naming its project would be a lie. A `.mcp.json` server that is in
|
||||
neither list is still awaiting its approval prompt and does count, since blanking
|
||||
the status line on a fresh checkout is the more confusing failure.
|
||||
|
||||
### When a directory contradicts a switch
|
||||
|
||||
Claude Code merges the two scopes rather than picking one, so switching in user
|
||||
scope cannot clean up what a directory declares. `config claude` says so rather
|
||||
than reporting plain success:
|
||||
|
||||
```
|
||||
Warning: /path/to/repo/.mcp.json still registers 'homeautomation' for this
|
||||
directory — mounted alongside 'sre', not replaced by it.
|
||||
Re-run with --scope project to retire it, or delete the entry by hand.
|
||||
```
|
||||
|
||||
A canonical entry pinned to another project gets the stronger wording — it
|
||||
*overrides* the switch in that directory rather than sitting beside it.
|
||||
|
||||
### It is never installed over yours
|
||||
|
||||
|
||||
Reference in New Issue
Block a user