Fix 'MCP Server Failed to Connect' in Claude Code (2026)

Title card: Fix MCP Server Failed to Connect in Claude Code

Model Context Protocol servers are how Claude Code reaches beyond its built-in tools — into your database, your issue tracker, a browser, your filesystem. When they work, they’re invisible. When they don’t, you get four blunt words in claude mcp list:

✗ Failed to connect

No stack trace. No hint. And because the same server often works perfectly in Claude Desktop or when you run it by hand, the error feels like a Claude Code bug. It usually isn’t. “Failed to connect” is a transport-layer message — it means Claude Code couldn’t even start a conversation with the server, let alone call a tool. That narrows the problem enormously, and it’s what makes this fixable in minutes once you know the decision tree.

This guide is that decision tree. It’s built from the official Claude Code MCP troubleshooting docs, the open bug reports (like issue #1611), and the failure modes that actually show up in day-to-day use. If you just want the fast path, start with the 60-second triage. If you want to understand why it broke — so it stays fixed — read on.

New to MCP itself? Read What is MCP (Model Context Protocol)? first, then come back here to debug it.

The 60-second triage

Before anything else, run these two commands in your shell (not inside a claude session):

claude mcp list          # status of every configured server
claude mcp get <name>    # the exact command / URL + scope Claude Code stored

Then branch on what you see:

Status in claude mcp listWhat it meansJump to
✓ ConnectedHealthy — your problem is elsewhere
✗ Failed to connectServer didn’t start / URL didn’t respondstdio · HTTP
✗ Connection errorThe connection attempt threwstdio · HTTP
! Needs authenticationReachable, but requires sign-in / tokenAuth
! Connected · tools fetch failedConnected but couldn’t list toolsNo tools
⏸ Pending approvalProject server you haven’t approved yetApproval
(nothing / “No MCP servers”)Wrong scope or wrong file pathScope

The single highest-value move in all of MCP debugging: take the command from claude mcp get and run it yourself. Everything below is a structured way of doing exactly that.

The mental model: transport before tools

An MCP connection has two layers, and “Failed to connect” is always the lower one:

   Your prompt


 ┌───────────────┐
 │  TOOL LAYER   │   "search issues", "query db"  ← works only if the layer below is up
 └───────────────┘

       │  ← "Failed to connect" lives HERE
 ┌───────────────┐
 │ TRANSPORT     │   stdio: a subprocess on your machine
 │ LAYER         │   http/sse: a request to a URL
 └───────────────┘

So the first question is never “why won’t the tool run?” It’s “can Claude Code even reach the server?” And that splits cleanly by transport type:

                   ┌─────────────────────────────┐
                   │  claude mcp get <name>       │
                   │  type = ?                    │
                   └─────────────┬───────────────┘

             ┌───────────────────┴────────────────────┐
             ▼                                         ▼
      type: "stdio"                            type: "http" / "sse"
   (command + args)                            (a url)
             │                                         │
   Run the command                          curl -I <url>
   directly in a shell                             │
             │                          ┌───────────┼────────────┐
     ┌───────┴────────┐                 ▼           ▼            ▼
     ▼                ▼             404 / 405   401 / 403   no response
  starts &         errors:       URL path is  authenticate  URL / network
  waits            missing Node,  wrong        (/mcp or      is wrong
  → config bug     browser, key,               --header)
  (see below)      PATH

Find your branch, then jump to it. If claude mcp get won’t even show your server, you’re in the scope / path branch, not this one.

Fix a local (stdio) server

A stdio server is a program Claude Code launches as a subprocess and talks to over standard input/output — filesystem, Playwright, most npx-based servers. “Failed to connect” here means the process didn’t start, or died on launch.

Step 1 — Run the exact stored command by hand

Copy the command and args from claude mcp get <name> and run them directly:

npx -y @playwright/mcp@latest

Two outcomes, two very different diagnoses:

  • It starts and just waits for input (no prompt returns, no error). The server is healthy. Your problem is that Claude Code is storing or launching a different command than the one you just ran. Go to Step 2.
  • It errors immediately. The message names what’s missing — Node.js not found, a browser not installed, a bad package name, a missing API key. Fix that, then re-check claude mcp list.

Step 2 — The missing -- separator (the #1 stdio mistake)

When you add a stdio server, everything after -- is the command Claude Code runs. Omit the separator and Claude Code mis-parses the pieces — storing the wrong command, or treating your args as its own flags:

# ❌ WRONG — no separator, args get swallowed
claude mcp add fs npx -y @modelcontextprotocol/server-filesystem /data

# ✅ RIGHT — everything after -- is the server's launch command
claude mcp add fs -- npx -y @modelcontextprotocol/server-filesystem /data

Run claude mcp get fs. If the stored command doesn’t match what you typed, remove and re-add it with the --:

claude mcp remove fs
claude mcp add fs -- npx -y @modelcontextprotocol/server-filesystem /data

Step 3 — PATH and Node/npx problems

Claude Code inherits a PATH that may not match your interactive shell — especially with Node installed via nvm, fnm, asdf, Homebrew, or Volta. Symptoms: the command works in your terminal but “Failed to connect” in Claude Code, and running it by hand is fine.

Fixes, in order of preference:

  1. Pin the package version so npx doesn’t stall resolving “latest”: use @playwright/mcp@latest (or a fixed version), never a bare package name.
  2. Use an absolute path to the binary or interpreter if PATH is the issue:
    claude mcp add fs -- /usr/local/bin/node /abs/path/to/server.js /data
  3. Check your Node version. npx-based servers need Node 18+. nvm’s default may be older than the version you think you’re on — verify with node -v in the same environment Claude Code launches from. (This site’s build notes the same trap: the machine default can lag behind the version you use interactively.)

Step 4 — Windows: wrap npx in cmd /c

On Windows, npx (and most npm-installed tools) is a .cmd shim, not a real executable, and Claude Code can’t always spawn it directly. Route it through the command interpreter:

# ❌ Fails on Windows — npx is a .cmd shim
claude mcp add fs -- npx -y @modelcontextprotocol/server-filesystem C:\data

# ✅ Works — run it through cmd /c
claude mcp add fs -- cmd /c npx -y @modelcontextprotocol/server-filesystem C:\data

The equivalent in .mcp.json:

{
  "mcpServers": {
    "fs": {
      "type": "stdio",
      "command": "cmd",
      "args": ["/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "C:\\data"]
    }
  }
}

Step 5 — Missing environment variables

If the server connects but shows no tools — or exits right after launch — it’s usually missing a required env var (an API key). See The server connects but no tools appear.

Fix a remote (HTTP) server

A remote server lives at a URL (--transport http or sse). Here “Failed to connect” means the HTTP request didn’t get a usable response. Diagnose it with one command:

curl -I https://mcp.sentry.dev/mcp

PowerShell: use curl.exe, not curl. Plain curl is an alias for Invoke-WebRequest and won’t behave like real curl.

Read the status code like a map:

curl resultDiagnosisFix
404 or 405Server is up, your URL path is wrong. Many MCP endpoints answer only POST, so a 404/405 still proves reachability.Compare to the server’s documented endpoint; claude mcp remove <name> and re-add with the correct URL.
401 or 403Server is up, you must authenticate.OAuth or token
200Reachable and open. Problem is elsewhere (client version, headers).Update Claude Code; re-add.
No responseURL is wrong or the network/VPN/firewall is blocking it.Verify the URL; test from another network.

As of Claude Code v2.1.191, a 404 now surfaces a precise message when you select the server in /mcpMCP endpoint not found at <url>. Check the URL in your MCP config. — with the exact URL it tried. Older versions show a generic Error POSTing to endpoint, which is why upgrading (claude update) is a good early move.

Authentication (401 / 403 and “Needs authentication”)

Two auth shapes, two fixes:

OAuth (browser sign-in) — Sentry, Linear, Notion and similar:

/mcp                     # inside a claude session
# → select the server → choose "Authenticate" → approve in browser

If the browser doesn’t open, copy the URL printed in the terminal and open it manually.

Static token — GitHub and similar. Pass it at add time:

claude mcp add --transport http github https://api.githubcopilot.com/mcp \
  --header "Authorization: Bearer <YOUR_TOKEN>"

“Connection timed out at startup”

This is a distinct status: the server is correct, but it didn’t finish starting inside the default 30-second startup window — classic for an npx server downloading its package on first run.

Raise the limit with MCP_TIMEOUT (milliseconds):

# macOS / Linux
MCP_TIMEOUT=60000 claude

# PowerShell — set on the same line
$env:MCP_TIMEOUT = "60000"; claude

Two things worth knowing:

  • MCP_TIMEOUT is startup only. For slow tool calls after connection, add a per-server "timeout" (ms) inside that server’s .mcp.json entry — e.g. "timeout": 600000 for a 10-minute tool.
  • Warm the cache first. Running the server’s command once by hand downloads the package, so the next Claude Code startup is fast and may not need the bumped timeout at all.

”No MCP servers configured”

You added a server, but /mcp or claude mcp list shows nothing. This is never a connection failure — it’s Claude Code looking in the wrong place. Two causes.

Cause 1 — Wrong scope

claude mcp add defaults to local scope, which ties the server to the exact directory you ran it from. Open Claude Code in a different project and it’s gone. The three scopes:

ScopeFileVisible to
local~/.claude.json (under this project’s entry)Only you, only this project (default)
project.mcp.json in the repo rootEveryone who clones the repo
user~/.claude.json (top-level mcpServers key)Only you, all projects

Precedence when the same name exists in more than one: local › project › user.

Want a server everywhere? Add it at user scope:

claude mcp add --scope user --transport http docs https://code.claude.com/docs/mcp

Cause 2 — Wrong file path

Claude Code reads only ~/.claude.json and <project>/.mcp.json. It ignores hand-written configs at paths people commonly guess:

✗ ~/.claude/mcp.json
✗ ~/.claude/config/mcp.json
✗ ~/.claude/.mcp.json
✗ %APPDATA%\Claude\mcp.json      ← that's Claude *Desktop*, a different app

On Windows, ~/.claude.json is %USERPROFILE%\.claude.json. If you’ve set CLAUDE_CONFIG_DIR, Claude Code reads .claude.json from inside that directory instead.

Migrating from Claude Desktop? Don’t copy claude_desktop_config.json by hand — run claude mcp add-from-claude-desktop (macOS/WSL).

.mcp.json changes that never take effect

.mcp.json is read at session start. Edit it, and you must exit and restart the session — a live /mcp reconnect won’t pick up new entries.

If a server still won’t appear after restart:

  • Run /mcp and look for a parse warning — Claude Code skips malformed entries and names the bad field. A trailing comma or unquoted value is the usual culprit.
  • If you previously rejected a project-scoped server at the approval prompt, that “no” is remembered. Reset it:
    claude mcp reset-project-choices
  • A ⏸ Pending approval status just means you haven’t approved a project server yet — run /mcp, select it, approve.

Environment-variable expansion in .mcp.json

You don’t have to hard-code secrets. .mcp.json expands ${VAR} inside the command, args, env, url and headers fields, with ${VAR:-default} for a fallback:

{
  "mcpServers": {
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp",
      "headers": { "Authorization": "Bearer ${GITHUB_MCP_TOKEN}" }
    },
    "db": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@some/db-mcp"],
      "env": { "DATABASE_URL": "${DATABASE_URL:-postgres://localhost:5432/dev}" }
    }
  }
}

A “Failed to connect” that only happens on a teammate’s machine is often an unset variable here — the reference expands to empty, and the server rejects the bad URL or missing key.

The server connects but no tools appear

A green ✓ Connected with an empty tool list (or ! Connected · tools fetch failed) means the transport is fine but the server registered nothing — nearly always a missing required env var, usually an API key. The server starts, sees no key, and exposes zero tools rather than crashing.

Supply the variable:

# at add time
claude mcp add mydb --env DATABASE_URL=postgres://... -- npx -y @some/db-mcp

# or in .mcp.json
"env": { "DATABASE_URL": "postgres://..." }

Then restart the session and check /mcp. If it’s an HTTP server, tools fetch failed more often points at auth or a wrong endpoint — run claude mcp get <name> for the detail.

The macOS keychain / AbortError case (issue #1611)

A stubborn variant, tracked in issue #1611: a server that works perfectly in Claude Desktop and when run by hand, but fails only in the Claude Code CLI on macOS, with symptoms like AbortError: child process aborted, a keychain lookup failure (security find-generic-password ... "Claude Code" — item not found), and empty log directories under ~/Library/Caches/claude-cli-nodejs/.

If this is you:

  1. Update firstclaude update. Platform-integration bugs like this get patched; check whether your version still reproduces it.
  2. Prefer a fully-qualified command over relying on inherited environment — absolute node path plus absolute server path, so the subprocess can’t inherit a broken PATH or shell state.
  3. Check the cache/log directory exists and is writable~/Library/Caches/claude-cli-nodejs/. A missing or permission-locked cache dir has been part of the repro.
  4. Try user scope in ~/.claude.json rather than a project .mcp.json, to rule out project-approval and per-directory state as a factor.

This is the one class of failure where “it’s not you, it’s a known bug” can genuinely be true — so confirm your version against the issue before spending an hour on your own config.

Common mistakes checklist

  • ❌ Forgetting -- before a stdio command → wrong command stored. (Verify with claude mcp get.)
  • ❌ Adding a server at local scope, then opening a different project and wondering where it went.
  • ❌ Editing ~/.claude/mcp.json — a path Claude Code never reads.
  • ❌ On Windows, calling npx without a cmd /c wrapper.
  • ❌ Confusing Claude Code (CLI) config with Claude Desktop (claude_desktop_config.json) — different apps, different files.
  • ❌ Assuming a 404 from curl means “broken.” For MCP endpoints it usually means “reachable, wrong path.”
  • ❌ Not restarting the session after editing .mcp.json.
  • ❌ Committing a real token into project .mcp.json instead of using ${VAR} expansion.

Best practices to prevent this

  • Pin versions. @latest or a fixed version beats a bare package name — no surprise resolution stalls at startup.
  • Prefer --scope user for personal tools you always want, and --scope project (committed .mcp.json) for team tools. Use local scope only for throwaway experiments.
  • Keep secrets out of config via ${VAR:-default} expansion; document required vars in your README.
  • Add servers one at a time and run claude mcp list after each — a batch that “all fails” is harder to bisect.
  • Prune servers you don’t use. Every connected server loads its tool definitions into every session’s context window, so dead servers cost you tokens and attention on every turn. claude mcp remove <name> when you’re done.

Quick-reference: symptom → fix

SymptomMost likely causeFirst command to run
✗ Failed to connect (stdio)Bad command / PATH / missing --claude mcp get <name>, then run the command by hand
✗ Failed to connect (http)Wrong URL or authcurl -I <url>
! Needs authenticationOAuth / token required/mcp → Authenticate, or --header
Connection timed out at startupnpx download > 30sMCP_TIMEOUT=60000 claude
No MCP servers configuredWrong scope or file pathclaude mcp add --scope user ...
Connected but no toolsMissing API-key env var--env KEY=value / .mcp.json env
Works in Desktop, fails in CLI (macOS)Known platform bugclaude update; see #1611
Edited .mcp.json, nothing changedSession not restartedExit and relaunch claude

Keep learning: official sources and talks

Primary sources (current as of July 2026):

Useful searches (try on YouTube — Anthropic and developer channels):

  • “Claude Code MCP server failed to connect fix”
  • “claude mcp add stdio vs http transport”
  • “MCP server Windows npx cmd /c Claude Code”
  • “Claude Code MCP_TIMEOUT startup timeout”

Go deeper on this site: What is MCP? · The rise of AI agents · Fixing context window exceeded errors · What is context engineering? · How large language models actually work


The reason “Failed to connect” feels mysterious is that it collapses a dozen distinct problems into one message. But every one of those problems answers to the same reflex: reproduce the connection outside Claude Code. Run the stdio command in a terminal; curl the HTTP URL. The moment you do, the vague four-word error becomes a specific one — missing Node, wrong path, 404, expired token — and specific errors have specific fixes. Get that reflex into your fingers and MCP stops being a black box. It’s just a subprocess, or an HTTP request, that hasn’t told you what it needs yet.

Next: How to Fix Context Window Exceeded Errors (ChatGPT, Claude, Cursor)