MCP Troubleshooting skill for Claude Code

From 'Failed to connect' to a named cause in six rungs. Claude stops restarting servers at random and works a fixed ladder: claude mcp list and get, running the server command by hand with an initialize probe, the MCP Inspector, then filtered debug logs. It checks whole-entry scope precedence, env expansion, auth state and timeouts, and only calls it fixed after one real tool call succeeds in a fresh session.

  • namemcp-troubleshooting
  • size180 lines · ≈2,450 tokens
  • files1
  • licenseMIT
  • updated

What Claude sees before loading it

This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.

description: Diagnoses and fixes Claude Code MCP servers that fail to connect, show no tools, need re-authentication, time out, return oversized or truncated output, or load from the wrong config, by working a fixed ladder from claude mcp list and get through running the server by hand, the MCP Inspector, and debug logs, with scope precedence across local, project .mcp.json, user, plugin, and managed config. Use when /mcp shows Failed or Needs authentication, tools are missing, a server works in one place but not another, or calls hang.

Try asking

  • /mcp says my postgres server failed to connect — figure out why
  • The GitHub MCP server connects but half its tools are missing
  • My MCP server works on my machine but not in CI with claude -p

Use it when

  • /mcp shows Failed to connect, Needs authentication, or Pending approval
  • A server is connected but expected tools are missing
  • A server works in the terminal but not in the IDE, desktop, or CI
  • Tool calls hang, time out, or return truncated output

Skip it when

How to check the result

claude mcp list shows the server connected from the intended scope, and a fresh session lists the tools and completes one real read-only call.

The common mistake it prevents

Fixing the project .mcp.json while a stale local-scope entry with the same name keeps winning, because precedence replaces the whole entry rather than merging fields.

~/.claude/skills/mcp-troubleshooting/SKILL.md

--- name: mcp-troubleshooting description: Diagnoses and fixes Claude Code MCP servers that fail to connect, show no tools, need re-authentication, time out, return oversized or… license: MIT ---

An MCP problem is solved when claude mcp list shows the server connected from the scope you intended, /mcp lists the expected tools, one real tool call succeeds in a fresh session, and you can name the cause in one sentence. Restarting until it works is not a diagnosis.

Symptom to first check

SymptomMost likely causeFirst check
✘ Failed to connect (stdio)Command not on PATH, crashes on start, or logs to stdoutRun the exact command by hand (step 3)
✘ Failed to connect (http)Wrong URL/path (/mcp vs /sse), TLS, proxy, 401/403claude mcp get <name> Issue: line; curl -i <url>
! Needs authenticationOAuth never completed or refresh token rejected/mcp → server → Authenticate / Re-authenticate, or claude mcp login <name>
⏸ Pending approvalProject .mcp.json server not yet approvedStart claude interactively and approve; claude mcp reset-project-choices to re-ask
⊘ Disabled for this projectToggled off earlier/mcp enable <name>
Connected but tool missingTool name over 128 chars, invalid input schema, tool search deferral, deny rule/mcp → server → tools; check debug log for exclusion reasons
Works in terminal, fails in IDE / desktop / CIDifferent PATH, env vars, cwd, or config scopeCompare env, which <cmd>, and the scope table below
Slow first start then finenpx/uvx downloading the packageRaise MCP_TIMEOUT; pin and preinstall the package
Call hangs or abortsTool slow; idle timeout; auto-backgroundedMCP_TOOL_TIMEOUT, per-server timeout, CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT
Output “saved to file” or warning about sizeResult over 50,000 chars or over MAX_MCP_OUTPUT_TOKENS (25,000 default)Narrow the query; raise the limit only if truly needed
${VAR} literally in requestsEnv var unset when Claude Code startedSet it before launch or add ${VAR:-default}

The ladder

Work top to bottom and stop at the first rung that explains the failure.

- [ ] 1. claude mcp list            -> status of every server, from every scope
- [ ] 2. claude mcp get <name>      -> resolved config, scope, Issue: line
- [ ] 3. Run the server command by hand with the same env
- [ ] 4. MCP Inspector against the same command/URL
- [ ] 5. claude --debug=mcp (or --debug-file) and read the server lines
- [ ] 6. Fix the cause; verify in a fresh session with one real call

1–2. List and inspect

claude mcp list                 # health of all servers
claude mcp get github           # scope, command/url, env keys, last error

In a session, /mcp shows status per server, lets you authenticate, reconnect, enable or disable, and drill into the tool list. /mcp reconnect all retries every failed server.

3. Run it yourself

For stdio servers, copy the exact command and args from step 2 and run them in a clean shell with the same env. A healthy stdio server prints nothing and waits for input. Then send an initialize request:

printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}' \
  | npx -y @modelcontextprotocol/server-filesystem /tmp 2>/tmp/mcp-stderr.log | head -c 600; echo
cat /tmp/mcp-stderr.log

Expect one JSON line with "result" and serverInfo. Failure patterns:

What you seeCauseFix
command not found / ENOENTBinary not on the PATH Claude Code inherits (nvm, pyenv, Homebrew paths missing in GUI apps)Use an absolute path in command (which node)
Banner text or logs before the JSONServer writes logs to stdout, corrupting JSON-RPCLog to stderr; for third-party servers, find its quiet/log-file flag
Stack trace about missing envRequired key not setPass -e KEY=... or env in config; check ${VAR} expansion
Hangs with no output for 30+ sPackage download, or waiting on interactive loginPreinstall, pin version; complete the login once outside Claude

For HTTP servers: curl -i -X POST <url> -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '<initialize json>'. A 404 usually means the wrong path; 401/403 means auth; HTML means you hit a login page or proxy.

4. MCP Inspector

npx @modelcontextprotocol/inspector                       # UI; enter command or URL
npx @modelcontextprotocol/inspector node build/index.js   # stdio server directly

If the Inspector lists tools and calls succeed, the server is fine and the problem is Claude Code config (scope, env, auth, permissions). If the Inspector fails too, fix the server first; for servers you own, see mcp-server-builder.

5. Debug logs

claude --debug=mcp                          # filter to MCP lines
claude --debug-file /tmp/claude-debug.log   # write everything to a file
grep -iE 'mcp|<server-name>' /tmp/claude-debug.log | tail -50

Look for: spawn errors, exit codes, stderr from the server, schema exclusion reasons (“excluded tool”), unset-variable warnings, and auth status codes.

Scopes and precedence

ScopeStored inShared?
local (default for claude mcp add)~/.claude.json under this project’s pathNo
project.mcp.json at repo rootYes, committed; requires per-user approval
user~/.claude.json top levelNo, all your projects
Pluginthe plugin’s configVia plugin install
claude.ai connectorsyour claude.ai accountPer account
Managedmanaged-mcp.json / managedMcpServersOrg-enforced, outranks the rest

Same name in several scopes: local beats project beats user beats plugin beats connectors, and the whole entry wins — fields never merge. A stale local entry with an old URL silently overrides a fixed .mcp.json. Check with claude mcp get <name> and remove the duplicate: claude mcp remove <name> -s local.

Other config facts that cause confusion:

  • .mcp.json is read from the project root; servers from it need approval in interactive sessions, but claude -p and SDK runs load them without a prompt.
  • In untrusted folders, repository-provided approvals (enableAllProjectMcpServers, enabledMcpjsonServers) are ignored until the folder is trusted.
  • --strict-mcp-config --mcp-config file.json uses only that file — ideal for CI and for proving whether another scope is interfering.
  • Server names allow only letters, digits, -, _. Some names are reserved.
  • A JSON entry with url but no "type": "http" is treated as stdio and fails.
  • For claude mcp add stdio servers, put -- before the server command so its flags are not parsed as Claude Code flags: claude mcp add -e API_KEY=x -s user myserver -- npx -y some-mcp-server --port 0.
  • On native Windows, if spawning npx fails, wrap it: "command": "cmd", "args": ["/c", "npx", "-y", "pkg"].

Env vars and limits

Variable / keyPurpose
MCP_TIMEOUT (ms)Server startup timeout (default 30 s for -p with --mcp-config)
MCP_TOOL_TIMEOUT (ms) / per-server timeoutTool execution timeout; per-server wins (values under 1000 ignored)
CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT (ms)Abort when no response/progress: 5 min remote, 30 min stdio; 0 disables
MAX_MCP_OUTPUT_TOKENSOutput cap (default 25,000); warning above 10,000
ENABLE_TOOL_SEARCHUnset = MCP tools deferred; false loads all upfront; auto:N threshold mode
alwaysLoad: true (server entry)Keep that server’s tools always visible (costs context)
${VAR} / ${VAR:-default}Expansion in command, args, env, url, headers

Tool search is on by default, so tools appear by name only until Claude searches for them; a “missing” tool may just be deferred. Behind a non-first-party ANTHROPIC_BASE_URL, tool search turns off and all tools load upfront.

Remote servers reconnect automatically (five attempts with backoff); stdio servers do not — use /mcp reconnect <name> after fixing a crash.

Gotchas

  • Credential variables such as ANTHROPIC_API_KEY are deliberately read as empty in a remote server’s url and headers. Use a dedicated variable for the server’s token.
  • Editing .mcp.json or ~/.claude.json mid-session does not reconnect servers. Reconnect from /mcp or restart.
  • Oversized outputs waste context even when under the cap. Prefer server-side filters, pagination, or a CLI (gh, aws) over raising MAX_MCP_OUTPUT_TOKENS.
  • headersHelper scripts have a 10-second limit and run only after the project is trusted; test them standalone (./helper.sh | python3 -m json.tool).
  • Never paste real tokens into .mcp.json; commit ${VAR} references and document the variables.

Verification

  1. claude mcp list shows ✔ Connected and claude mcp get <name> shows the intended scope with no Issue: line.
  2. New session: /mcp lists the expected tools; ask Claude to call one read-only tool and confirm a real result.
  3. For CI: claude -p --strict-mcp-config --mcp-config ci-mcp.json --output-format json "<task using the tool>" succeeds.

Output

**Server:** github (http) · **Status before:** ✘ Failed to connect · **After:** ✔ Connected, 41 tools
**Cause:** stale `local`-scope entry with the old /sse URL overrode the fixed project `.mcp.json`
(whole-entry precedence)
**Evidence:** `claude mcp get github` showed scope local, url .../sse; Inspector worked on /mcp
**Fix:** `claude mcp remove github -s local`; project entry now resolves
**Verified:** fresh session, `list_pull_requests` returned 12 PRs

About this skill

Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.

esc