Build This Now
Build This Now
What Is Claude CodeInstallationNative InstallerFirst Project
speedy_devvkoen_salo
Blog/Handbook/Core/Fix Claude Code MCP

Claude Code MCP Not Working: Fix Failed to Connect

Claude Code MCP not working? A decision tree for the Failed to connect error: read /mcp and claude mcp list, turn on claude --debug=mcp, then run the server by itself to find the real cause.

Want the framework behind these builds?

Get the Claude Code system we use to plan, build, test, and ship production software.

See what we build for companies →
speedy_devvkoen_salo
speedy_devvWritten by speedy_devvPublished Sep 24, 2026Updated Sep 24, 202611 min readHandbook hubCore index

"Failed to connect" in Claude Code means one of two things: the server process never started, or the URL never answered. Everything else is detail. The fastest path to the cause is always the same three steps, in order: read the status detail in /mcp or claude mcp list, turn on claude --debug=mcp, and run the server by itself outside Claude Code.

Most MCP bugs are not MCP bugs. They are a stray console.log, a path with a space in it, a config edited in the wrong file, or a session that was never restarted.

The decision tree in one table

StepCommandWhat it tells you
1. Read the statusclaude mcp list, claude mcp get <name>, or /mcpWhich failure you have, plus the HTTP status or error code
2. Read the logsclaude --debug=mcpThe server's stderr and the handshake, in ~/.claude/debug/
3. Isolate the serverRun the configured command or curl the URLWhether the server works at all without Claude Code

If step 1 names the problem (a 401, a 404, a pending approval), fix it and stop. If it only says ✘ Connection error, skip straight to steps 2 and 3, because that status never carries detail.

Step 1: Read the status before you touch anything

claude mcp list runs from your normal shell and health checks every configured server. claude mcp get <name> shows one server's full config plus an Issue: line when it failed. Inside a session, /mcp shows the same thing with buttons to reconnect or authenticate.

The status strings are specific, and each one points at a different fix:

StatusWhat it actually means
✔ ConnectedWorking. If tools still seem missing, jump to the no-tools section
! Connected · tools fetch failedThe handshake worked, listing tools did not. claude mcp get has the error
! Needs authenticationReachable, wants a browser sign-in or a token
✘ Failed to connectServer did not start or URL did not respond. Detail is appended
✘ Connection errorThe attempt threw an error. No detail is ever appended
⏸ Pending approvalA .mcp.json server you have not approved yet
⊘ Disabled for this projectYou toggled it off in /mcp. Toggle it back on there

On recent versions, Failed to connect carries the HTTP status and any error text the server returned. That detail arrived in Claude Code v2.1.219. If you only see a bare status, run claude update first. A lot of old troubleshooting advice exists only because older versions hid the real error.

claude mcp list also prints configuration warnings, and two of them explain a surprising number of failures:

  • Hidden whitespace. A token pasted with a trailing newline. The warning reads like Leading or trailing whitespace in: headers.Authorization. Claude Code does not trim it for you.
  • Missing environment variable. A ${VAR} reference with no value and no default. The server still loads, with the literal ${VAR} text passed through, which then fails in whatever way that server fails.

Step 2: Turn on the MCP debug log

When the status is vague, restart with MCP debugging on:

claude --debug=mcp

The category filter only binds in the = form. claude --debug mcp with a space turns on debug mode for everything, which is noisier but still works. The log lands in ~/.claude/debug/ as a file named after the session ID. If you want it somewhere predictable:

claude --debug-file /tmp/claude-debug.log

What you are looking for is the server's stderr. A stdio server that crashes on import, cannot find a binary, or throws on a missing environment variable writes that to stderr, and the debug log is where it surfaces.

Step 3: Run the server alone

This is the step people skip, and it is the one that resolves the most cases.

For a stdio server, copy the exact command and arguments from claude mcp get <name> and run them in your terminal:

npx -y @playwright/mcp@latest

Two outcomes, two meanings:

  • It starts and sits there silently. Good. A stdio server talks over stdin and stdout, so a blocked, quiet terminal means it is running and waiting for a client. The server works. The problem is in how Claude Code launches it, so compare what you ran against what claude mcp get shows.
  • It errors. The message usually names what is missing: Node.js, Python, a browser binary, an API key.

If it prints a friendly banner like "Server started on stdio!" before going quiet, you have just found your bug. Read the next section.

For a remote server, check the URL is reachable from your machine:

curl -I https://mcp.example.com/mcp

In PowerShell, call curl.exe so you hit real curl instead of the Invoke-WebRequest alias. Read the response like this:

  • 404 or 405: the server is up. Many MCP endpoints only answer POST, so a GET getting rejected still proves reachability.
  • 401 or 403: the server is up and wants authentication.
  • Nothing: wrong URL, DNS, VPN, proxy, or firewall. Not an MCP problem.

Failure mode 1: stdout pollution

This is the classic stdio bug, and it is invisible until you know the rule.

In the stdio transport, the server reads JSON-RPC messages on stdin and writes them on stdout, one message per line. The MCP specification says the server MUST NOT write anything to stdout that is not a valid MCP message. It MAY write anything it likes to stderr for logging.

So a single console.log("ready") in a Node server, or a print() in a Python one, puts a line of plain text into the channel where Claude Code expects JSON. The handshake breaks, and you get a failed connection from a server that "works fine when I run it."

The fix is to log to stderr:

// Node: console.log writes to stdout, console.error writes to stderr
console.error("server ready");
import sys
print("server ready", file=sys.stderr)

Watch for indirect polluters too: a dependency that prints a banner on import, or a wrapper script that echoes before exec. Running the server alone (step 3) shows every line it writes. Anything human-readable there is suspect.

Failure mode 2: paths, spaces, and the launch directory

Three separate path bugs look identical from the outside.

Paths with spaces. Everything after -- in claude mcp add is passed to the server untouched, but your shell still splits it first. An unquoted path with a space becomes two arguments before Claude Code ever sees it:

# Broken: the shell splits "My Projects" into two args
claude mcp add docs -- node /Users/me/My Projects/docs-server/index.js

# Fixed: quote the path
claude mcp add docs -- node "/Users/me/My Projects/docs-server/index.js"

Then run claude mcp get docs and confirm the arguments look like what you intended. In a hand-written .mcp.json, keep command as the executable alone and put each argument, spaces included, in its own args element:

{
  "mcpServers": {
    "docs": {
      "command": "node",
      "args": ["/Users/me/My Projects/docs-server/index.js"]
    }
  }
}

Do not put a whole command line like node /path/to/server.js into command and expect it to be split for you.

Relative paths. A relative path in command or args resolves against the directory you launched Claude Code from, not the location of .mcp.json. Start Claude Code from a subfolder and ./server/index.js points somewhere else. Use absolute paths, or reference the project root. Claude Code sets CLAUDE_PROJECT_DIR in the server's environment, but to expand it inside command or args of a .mcp.json entry you need a default, because it is not set in Claude Code's own environment:

{
  "mcpServers": {
    "docs": {
      "command": "node",
      "args": ["${CLAUDE_PROJECT_DIR:-.}/tools/docs-server/index.js"]
    }
  }
}

The missing --. Without the separator, Claude Code tries to parse the server's flags as its own. claude mcp add myserver npx -y some-server is not the same command as claude mcp add myserver -- npx -y some-server. If claude mcp get shows a command that differs from what you typed, this is why. Remove the server and add it again with -- in place.

Slow first start. A stdio server launched through npx can fail its first check while the package downloads. The default startup timeout is 30 seconds. Raise it for that launch:

MCP_TIMEOUT=60000 claude

Failure mode 3: you edited the config and nothing changed

Claude Code reads .mcp.json at session start. If you edit it while a session is running, the running session does not see the change. Exit, start a new session, then run claude mcp list to confirm.

If the server still does not appear after a restart, one of three things is true:

  1. The entry is malformed. Claude Code skips that one entry and loads the rest. claude mcp list prints a parse warning naming the bad field.
  2. You edited the wrong file. Claude Code reads ~/.claude.json and <project>/.mcp.json. It does not read ~/.claude/.mcp.json, ~/.claude/mcp.json, ~/.claude/config/mcp.json, or %APPDATA%\Claude\mcp.json. Every one of those is a file people create by hand and then wonder why nothing happens.
  3. The entry has a url but no type. Claude Code reads an entry with no type as a stdio server, so a remote server copied from another client's docs without "type": "http" gets skipped with a message telling you to add the type. JSON configs using "type": "streamable-http" are fine, since that is accepted as an alias for http.

For project servers, there is also approval. A server in .mcp.json shows ⏸ Pending approval until you run claude interactively and approve it. If you rejected it once, reset your choices:

claude mcp reset-project-choices

Since v2.1.196, approvals committed to the repository's own .claude/settings.json are ignored until you trust the workspace by running claude there and accepting the trust dialog. A freshly cloned repo cannot approve its own servers.

Failure mode 4: the scope mix-up

MCP servers live at three scopes, and the default is the one that surprises people.

ScopeLoads inStored in
Local (default)Only the project where you added it~/.claude.json, under that project's path
ProjectOnly this project, shared via git.mcp.json at the project root
UserEvery project on your machine~/.claude.json, top level

The trap: you run claude mcp add in one repo, switch to another, and the server is gone. It is not broken. Local scope ties it to the directory you added it from (the repository root, or the exact directory outside a git repo). Add it again in the new project, or use --scope user if you want it everywhere.

The second trap is duplicates. When the same name exists at more than one scope, Claude Code connects once, using the highest-precedence definition: local, then project, then user, then plugin servers, then claude.ai connectors. The whole entry from the winning scope is used. Fields are not merged. So a stale local entry with an old URL silently beats the correct one your team committed to .mcp.json.

claude mcp list warns when the same name points at different endpoints across scopes. Keep the one you want and remove the rest explicitly:

claude mcp remove github --scope local

If remove answers exists in multiple scopes, that is the same problem announcing itself.

Failure mode 5: remote servers returning 401, 403, or 404

Remote failures are usually easier, because the status code tells you the category.

404. Claude Code shows MCP endpoint not found at <origin>. Check the URL in your MCP config. The message deliberately shows only the origin, not the path, so run claude mcp get <name> to see the full URL. The usual cause is a wrong path: /sse where the server wants /mcp, or a missing trailing segment. Remove the server and add it back with the documented endpoint.

401 or 403 on an OAuth server. Claude Code marks it as needing authentication. Open /mcp, pick the server, and sign in. From a plain shell, claude mcp login <name> runs the same flow. Over SSH or on a machine with no browser, it prints the URL for you to open elsewhere, and --no-browser forces that mode.

401 or 403 when you set the header yourself. Here Claude Code does not fall back to OAuth. It reports a failed connection, because the credential to fix is the one you configured. Check, in order:

  • The token has trailing whitespace. claude mcp list flags it.
  • The token is valid, but not for this endpoint or with the scopes the tool needs.
  • The header references a variable Claude Code refuses to expand toward a remote server. In a remote server's url and headers, credential variables such as ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, AWS_BEARER_TOKEN_BEDROCK, and NPM_TOKEN always read as empty, so Bearer ${ANTHROPIC_AUTH_TOKEN} arrives as Bearer and gets a 401. This is on purpose, so a project file cannot ship your credentials to a server it names. Copy the value into a variable with your own name and reference that instead.

Also worth knowing: claude mcp add saves the config without validating credentials. A placeholder token is accepted at add time and fails later, which makes it look like the server broke on its own.

Transient remote failures (5xx, connection refused, timeouts) are retried up to three times on the first connection. Auth and not-found errors are not retried, since only a config change fixes them.

Failure mode 6: connected, but no tools

A green ✔ Connected with nothing usable is its own category.

The server returned an empty list. Select the server in /mcp to see its tools. Zero tools after a clean start usually means a missing environment variable, like an API key the server checks before registering anything. Pass it with --env KEY=value on claude mcp add (with another option, such as --transport stdio, between --env and the server name) or in the entry's env field. Then choose Reconnect in /mcp. If the count stays at zero, read the server's stderr in the --debug=mcp log.

Listing tools failed. That shows as ! Connected · tools fetch failed. claude mcp get <name> has the error.

Individual tools were dropped. Claude Code checks each tool's input schema before sending it to the API, and excludes tools that would make the API reject the request, such as top-level property names outside 1 to 64 characters of letters, digits, _, ., and -. The other tools from that server keep working. The reason is recorded in the server's log, and Claude is told which tools were excluded, so you can simply ask Claude why a tool is missing.

The tools are there, just deferred. Tool search is on by default: only tool names and server instructions load at startup, and full definitions load when Claude searches for them. So a tool can be available without being visible in the way you expected. If a small server's tools should always be in context, set "alwaysLoad": true on that server's entry. Tool search also turns itself off when ANTHROPIC_BASE_URL points at a non-first-party host, which changes how tools load behind a proxy.

What changes in claude -p and CI

Headless runs remove the interactive safety nets, which produces a separate family of "works on my machine" bugs:

  • There is no /mcp panel, so OAuth cannot run there. Sign in once interactively with /mcp or claude mcp login <name>.
  • Project servers from .mcp.json load without an approval prompt. Use disabledMcpjsonServers, or --strict-mcp-config with --mcp-config, to control exactly which servers load.
  • With --mcp-config and -p, Claude Code waits for pending servers up to MCP_TIMEOUT (30 seconds by default) before the first turn.
  • Stdio servers are not reconnected automatically if they die. Remote servers are, with up to five backoff attempts.

Claude Code MCP FAQs

Why does my MCP server say Failed to connect in Claude Code?

Either the process never started or the URL never answered. Run claude mcp get <name> and read the Issue: line for the status code or error. Then run the configured command yourself for a stdio server, or curl -I the URL for a remote one.

Do I need to restart Claude Code after editing .mcp.json?

Yes. .mcp.json is read at session start. Exit, start a new session, and run claude mcp list to check for a parse warning on the entry you changed.

Why is my MCP server connected but showing no tools?

It started but returned an empty tool list, usually because a required environment variable is missing. Add it with --env or the entry's env field, then Reconnect from /mcp. If it says tools fetch failed, claude mcp get has the error.

Where are Claude Code MCP debug logs?

Run claude --debug=mcp. The log, including your server's stderr, goes to ~/.claude/debug/ under the session ID. Use --debug-file <path> to choose the location.

Why does my remote MCP server return 401 even though I set the token?

Look for hidden whitespace (flagged by claude mcp list), a header that references a protected credential variable Claude Code reads as empty, or a token that is not valid for that endpoint. A header you configured yourself never falls back to OAuth.

For the broader picture of how Claude Code decides what loads into a session, see Claude Code session context, and for how MCP servers fit next to skills and plugins, Claude Code plugins explained.

Posted by @speedy_devv

Continue in Core

  • 1M Context Window in Claude Code
    Anthropic flipped the 1M token context window on for Opus 4.6 and Sonnet 4.6 in Claude Code. No beta header, no surcharge, flat pricing, and fewer compactions.
  • AGENTS.md vs CLAUDE.md Explained
    Two context files, one codebase. How AGENTS.md and CLAUDE.md differ, what each one does, and how to use both without duplicating anything.
  • Why a Hidden Line of Text Can Hijack Your AI Browser
    AI browsers read the whole web page — including text hidden from you. That's the door behind prompt injection, OWASP's #1 AI security risk in 2026. Here's how the attack works, in plain English.
  • AI Research for Builders: The Latest Breakthroughs, Explained Monthly
    A monthly digest of the latest AI research — agents, reasoning, efficiency, and models — with every claim traced to its source and translated into what it means if you build with AI.
  • 15 AI Research Breakthroughs (July 2026)
    The latest AI research, explained: OpenAI shipped GPT-5.6, Anthropic shipped Claude Opus 5, Moonshot open-weighted Kimi K3, and three separate results showed an agent benchmark score measures your whole evaluation setup, not just your model. What each finding means if you build with AI, with every vendor self-report flagged.
  • 15 AI Research Breakthroughs (June 2026)
    The latest AI research, explained: DeepSeek shipped DSpark and a million-token V4, open coding models closed the gap, AI disproved an 80-year-old math conjecture, and inference costs kept dropping. What each finding means if you build with AI.

More from Handbook

  • Agent Fundamentals
    Five ways to build specialist agents in Claude Code: Task sub-agents, .claude/agents YAML, custom slash commands, CLAUDE.md personas, and perspective prompts.
  • Agent Harness Engineering
    The harness is every layer around your AI agent except the model itself. Learn the five control levers, the constraint paradox, and why harness design determines agent performance more than the model does.
  • Agent Patterns
    Orchestrator, fan-out, validation chain, specialist routing, progressive refinement, and watchdog. Six orchestration shapes to wire Claude Code sub-agents with.
  • Agent Teams Best Practices
    Battle-tested patterns for Claude Code Agent Teams. Context-rich spawn prompts, right-sized tasks, file ownership, delegate mode, and v2.1.33-v2.1.45 fixes.

Want the framework behind these builds?

Get the Claude Code system we use to plan, build, test, and ship production software.

See what we build for companies →
speedy_devvkoen_salo

On this page

The decision tree in one table
Step 1: Read the status before you touch anything
Step 2: Turn on the MCP debug log
Step 3: Run the server alone
Failure mode 1: stdout pollution
Failure mode 2: paths, spaces, and the launch directory
Failure mode 3: you edited the config and nothing changed
Failure mode 4: the scope mix-up
Failure mode 5: remote servers returning 401, 403, or 404
Failure mode 6: connected, but no tools
What changes in claude -p and CI
Claude Code MCP FAQs
Why does my MCP server say Failed to connect in Claude Code?
Do I need to restart Claude Code after editing .mcp.json?
Why is my MCP server connected but showing no tools?
Where are Claude Code MCP debug logs?
Why does my remote MCP server return 401 even though I set the token?

Want the framework behind these builds?

Get the Claude Code system we use to plan, build, test, and ship production software.

See what we build for companies →