claude mcp add --scope user writes to a location claude mcp list does not read
claude mcp add --scope user writes to a location claude mcp list does not read — user-scope stdio/http MCP servers silently invisible
Summary
claude mcp add --scope user <name> ... reports success and writes the entry to ~/.claude.json → mcpServers.<name>, but the entry is then invisible to claude mcp list, claude mcp get <name>, and the chat REPL's MCP loader. Tools from these servers never become available, even after fully quitting and relaunching Claude Code.
The same servers, defined in a project-scope .mcp.json, load correctly. Only user scope is affected.
Key point — write/read inconsistency
This is the core of the bug. The CLI's documented user-scope flow is end-to-end broken in 2.1.122:
claude mcp add --scope user X ...explicitly reports success:
````
Added stdio MCP server X with command: ... to user config
File modified: /Users/<user>/.claude.json
- The file is in fact modified —
~/.claude.json→mcpServers.Xis present and JSON-valid afterwards. - 2.1.122 then refuses to surface the entry through any read path.
claude mcp list,claude mcp get X, and the chat REPL's MCP loader all behave as if it does not exist. No warning, no error, no log.
The bug is squarely between the write path of claude mcp add and the read paths of claude mcp list / claude mcp get / chat REPL loader. Same code path is hit by:
- The minimal probe in the Reproduction section below (
mcp-probe-test) - Any setup script that uses
claude mcp add --scope user(e.g. the common patternenvsubst < template.json | python3 register.py --scope userthat calls the CLI for each entry)
Setup scripts using the documented CLI are correct; they cannot register working user-scope MCPs in this version regardless of how they are written.
Environment
- Claude Code: 2.1.122 (also reproduced on 2.1.121, 2.1.119)
- Platform: macOS, Darwin 25.4.0, arm64
- Shell: zsh
- File:
~/.claude.jsonis JSON-valid, owned by current user, mode 0600
Reproduction
- Start with no user-scope MCP entries (or any state).
- Register a new HTTP MCP via the official CLI:
```
claude mcp add --scope user mcp-probe-test --transport http https://example.com/mcp
`
CLI replies:
``
Added HTTP MCP server mcp-probe-test with URL: https://example.com/mcp to user config
File modified: /Users/<user>/.claude.json
- Confirm the write landed:
````
$ python3 -c "import json; print(json.load(open('~/.claude.json'.replace('~','/Users/<user>'))).get('mcpServers',{}).get('mcp-probe-test'))"
{'type': 'http', 'url': 'https://example.com/mcp'}
- Try to read it back via the same CLI:
```
$ claude mcp list 2>&1 | grep -i probe
(no output)
$ claude mcp get mcp-probe-test
No MCP server found with name: "mcp-probe-test". Configured servers: claude.ai Adobe ...
```
- Quit Claude Code (IDE extension, fully — not just reload window). Relaunch. The deferred-tool list still does not include any
mcp__mcp-probe-test__*tools. The chat REPL's/mcpslash command also does not list it.
Expected
Either:
- The entry written by
claude mcp add --scope usershould be visible toclaude mcp list,claude mcp get, and the chat REPL. - OR
claude mcp add --scope usershould fail with a clear error if the target schema/location for user-scope has changed in this version.
Actual
The write path and read path operate on different sources. Writes succeed silently to ~/.claude.json. Reads enumerate only:
- Cloud MCPs (
claude.ai *) - Plugin MCPs (
plugin:*)
User-scope entries in ~/.claude.json → mcpServers are silently ignored.
Scope
Affects all entry types in mcpServers, including:
{"type": "stdio", "command": "...", "args": [...], "env": {...}}(e.g.uvx snowflake-labs-mcp ...){"type": "http", "url": "..."}(probed withhttps://example.com/mcptest entry above)- Mixed entries (some with
env: {}added by CLI, some without — both equally invisible)
Additional observations
claude mcp add --scope user X ...correctly detects an existing entry as a duplicate (says"MCP server X already exists in user config"), proving the read path on the write side does see~/.claude.json→mcpServers. So the inconsistency is specifically between the add-conflict-check path and the list/get/load paths.- Project-scope
.mcp.jsonin the project root works as expected; the same server config that is invisible at user scope showsStatus: ✓ Connectedwhen placed at project scope. - Disassembly suggests the loader runs each entry through a Zod
safeParseand silently drops failures (binary contains the stringhas invalid mcpServers item:and code shaped likeif ($.success) O[T] = $.data). I can't determine without source what schema field is failing, but it's failing for every entry — including ones the CLI itself just wrote — so this is not a user-config issue. - File permissions on
~/.claude.jsonare 0600 owned by the running user (binary contains the suggestion stringCheck ~/.claude.json permissionsbut perms appear correct). - Reproduces from a trusted directory (
hasTrustDialogAccepted: true) and a non-trusted directory equally.
Workaround
Move definitions to project-scope .mcp.json. Loses the cross-project benefit of user-scope and conflicts with setup scripts that delete project-root .mcp.json to avoid placeholder leaks.
Severity
Medium-high. The CLI's write surface for user-scope MCPs has been silently no-op for the read path across at least three minor versions, with no surfaced error. This breaks any workflow that expects claude mcp add --scope user to actually register a server and any setup script that relies on it.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗