DEV Community

Rulestack
Rulestack

Posted on

Your MCP server is configured but Claude can't see it: reading claude mcp list status

You ran claude mcp add, it printed Added ..., and Claude still acts like the server does not exist. The frustrating part is that nothing failed. The add wrote your configuration exactly as asked, and the reason the tools are missing is somewhere else entirely.

This is a walk through what each status in claude mcp list actually tells you, and the order I check things in when a server is configured but invisible.

First: Added means written, not connected

claude mcp add confirms a successful add by printing an Added ... line, and that line means the configuration was written. It is not a connection test.

The health check happens later:

claude mcp list
Enter fullscreen mode Exit fullscreen mode

That command shows a status next to each server: ✔ Connected, ! Needs authentication, or ✘ Failed to connect. A failure status there means Claude Code could not connect to that server — not that the list command itself failed. People read a red mark and assume their command was wrong, then re-run claude mcp add with slightly different arguments, which writes a second configuration and makes the situation harder to read.

So: Added and ✘ Failed to connect can both be true at once, and they are describing different steps.

The most common cause: scope

MCP servers live at one of three scopes, and the default is the narrow one.

Scope Loads in Shared with team Stored in
Local (default) Current project only No ~/.claude.json
Project Current project only Yes, via version control .mcp.json in project root
User All your projects No ~/.claude.json

Local scope is what you get when you do not pass --scope. A local-scoped server loads only in the project where you added it, stored in ~/.claude.json under that project's path. If you added the server while sitting in ~/scratch and then opened your real repository, the server is not missing — it is scoped to a directory you are no longer in.

# Same server, three very different lifetimes
claude mcp add --transport http stripe --scope local https://mcp.stripe.com
claude mcp add --transport http stripe --scope project https://mcp.stripe.com
claude mcp add --transport http stripe --scope user https://mcp.stripe.com
Enter fullscreen mode Exit fullscreen mode

One naming trap worth knowing before you go looking for files: MCP "local scope" is not .claude/settings.local.json. MCP local-scoped servers live in ~/.claude.json in your home directory. The similarly-named settings file in your project directory is a different mechanism entirely. If you have been grepping your repository for a server you added at local scope, that is why you cannot find it.

⏸ Pending approval — the .mcp.json case

Project-scoped servers are not trusted on sight. A server from .mcp.json that is waiting for you appears in claude mcp list and claude mcp get as:

⏸ Pending approval (run `claude` to approve)
Enter fullscreen mode Exit fullscreen mode

That is the whole fix: start claude interactively in that project and approve it. The status is not an error, and re-adding the server will not clear it.

The version of this that costs people an afternoon involves a cloned repository. As of v2.1.196, claude mcp list and claude mcp get read .mcp.json approvals only from settings files that are not checked into the repository, until you trust the workspace by running claude in it and accepting the trust dialog. That means a cloned repository cannot approve its own servers: enableAllProjectMcpServers or enabledMcpjsonServers committed to the project's .claude/settings.json is ignored in an untrusted folder, and the server sits at ⏸ Pending approval instead of connecting.

Approvals from these sources still apply in an untrusted folder:

  • your user ~/.claude/settings.json
  • managed settings
  • settings passed with --settings

An untracked .claude/settings.local.json also works, but only after you accept a trust dialog for that folder or a parent — Claude Code runs git to check whether the file is tracked, and it only runs that check in a trusted folder. The exception is your own configuration home: your home directory, or a directory whose .claude you have set as CLAUDE_CONFIG_DIR.

If you maintain a template repository that ships an .mcp.json and a committed approval, this is the behaviour that makes it not work for the person who clones it. The approval has to come from their side of the line, not yours.

✘ Rejected and the two lists that look alike

A rejected server shows as:

✘ Rejected (see disabledMcpjsonServers in settings)
Enter fullscreen mode Exit fullscreen mode

A disabledMcpjsonServers entry in any settings file rejects the server, and it wins. If someone added a server name there months ago to quiet a noisy tool, that entry is still doing its job.

Here is where it gets genuinely confusing. There are two pairs of settings with nearly identical names, and they control different things:

  • enabledMcpjsonServers / disabledMcpjsonServers — approval of servers defined in a project's .mcp.json
  • enabledMcpServers / disabledMcpServers — the per-project on/off toggle recorded in ~/.claude.json when you flip a server in the /mcp panel

The second pair splits by default state rather than by your intent. disabledMcpServers is an opt-out list for servers that default to on. enabledMcpServers is an opt-in list for built-in servers that default to off, such as computer-use. Claude Code consults exactly one of the two lists for each server, so neither overrides the other — adding a regular server to enabledMcpServers does nothing at all, and the entry is ignored rather than flagged.

When a server refuses to come back after you toggled it off, check which of the four keys is holding it, because three of them look plausible and only one is responsible.

Statuses that are not failures

A few displays look wrong and are not.

cached 2h ago · connects on first use · 5 tools. A remote server you have used before can load its tool list from a previous session instead of connecting at startup, then connect the first time Claude calls one of its tools. The tools are available from your first message; there is nothing to do. Set MCP_DISCOVERY_CACHE=0 if you want every server to connect at startup instead. This status needs v2.1.221 or later.

not configured. A remote server whose configuration has an empty url shows this in /mcp, in claude mcp list, and in the plugin manager, and Claude Code does not try to connect. Plugins use empty entries as placeholders for a connector you set up later. The detail view says No URL configured for this server; fill in the url to connect it.

A WebSocket server missing from the list entirely. WebSocket servers do not appear in claude mcp list output. Use claude mcp get <name> or the /mcp panel to check them. If you added one and it vanished, the list command is the wrong instrument, not the configuration.

When the status is ✘ Failed to connect

Since v2.1.219, claude mcp list appends the failure detail to the status line, and claude mcp get shows it on an Issue: line: the HTTP status or error code plus any error text the server returned. Credential-like text is redacted, and the expanded server URL is never included, because it can carry secrets. That is also why a ✘ Connection error status carries no detail — the exception text there can embed the URL.

Two configuration-level causes worth ruling out before you go debugging the server itself:

Invisible whitespace. Claude Code warns when an MCP config value carries hidden leading or trailing whitespace, which usually comes from pasting a token that brought a newline with it. It checks command, url, each args entry, and the values and key names under env and headers, and it names the affected field without echoing the value — for example Leading or trailing whitespace in: headers.Authorization. It does not trim the value. It uses it exactly as written, so the fix is yours to make.

A reserved name. These names belong to Claude Code's built-in servers: workspace, claude-in-chrome, computer-use, Claude Preview, and Claude Browser. A configuration that defines a server with one of these is skipped at load time with a warning to rename it, and claude mcp add rejects the name outright.

For stdio servers, one more syntax point that produces confusing failures: the -- separates Claude's own options, such as --transport, --env, and --scope, from the command and arguments that run your server. Everything after -- is passed through untouched. Leave it out and your server's flags get read as Claude's.

The order I actually check

  1. Am I in the same project where I added it? If it was local scope, that is the whole question.
  2. claude mcp list — is it listed at all? If it is a WebSocket server, use claude mcp get instead.
  3. Is the status ⏸ Pending approval? Run claude in the project and approve. If it stays pending in a cloned repo, the approval needs to come from a file that is not checked in, or from your user settings.
  4. Is it ✘ Rejected? Search every settings file for disabledMcpjsonServers.
  5. Is it ✘ Failed to connect? Read the appended detail, then check for pasted whitespace and reserved names before blaming the server.
  6. Toggled off and won't come back? Check disabledMcpServers in ~/.claude.json — and remember the enabled/disabled pair splits by the server's default state, not by what you meant.

None of these are exotic. The reason they cost time is that the failure is silent by design: a server that is scoped elsewhere, or waiting for approval, is behaving correctly, and correct behaviour does not produce an error message to search for.


Written while building Rulestack — configuration packs for Cursor, Claude Code, and Codex, including a set of forkable mcp.json templates.

I post shorter notes on this kind of thing at @ai-shop.bsky.social on Bluesky.

Top comments (0)