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.
- name
mcp-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
- You are building or changing the server's own code — use mcp-server-builder instead
- MCP tools are working but eating too much context — use context-window-hygiene instead
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.
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
| Symptom | Most likely cause | First check |
|---|---|---|
✘ Failed to connect (stdio) | Command not on PATH, crashes on start, or logs to stdout | Run the exact command by hand (step 3) |
✘ Failed to connect (http) | Wrong URL/path (/mcp vs /sse), TLS, proxy, 401/403 | claude mcp get <name> Issue: line; curl -i <url> |
! Needs authentication | OAuth never completed or refresh token rejected | /mcp → server → Authenticate / Re-authenticate, or claude mcp login <name> |
⏸ Pending approval | Project .mcp.json server not yet approved | Start claude interactively and approve; claude mcp reset-project-choices to re-ask |
⊘ Disabled for this project | Toggled off earlier | /mcp enable <name> |
| Connected but tool missing | Tool 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 / CI | Different PATH, env vars, cwd, or config scope | Compare env, which <cmd>, and the scope table below |
| Slow first start then fine | npx/uvx downloading the package | Raise MCP_TIMEOUT; pin and preinstall the package |
| Call hangs or aborts | Tool slow; idle timeout; auto-backgrounded | MCP_TOOL_TIMEOUT, per-server timeout, CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT |
| Output “saved to file” or warning about size | Result 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 requests | Env var unset when Claude Code started | Set 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 see | Cause | Fix |
|---|---|---|
command not found / ENOENT | Binary 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 JSON | Server writes logs to stdout, corrupting JSON-RPC | Log to stderr; for third-party servers, find its quiet/log-file flag |
| Stack trace about missing env | Required key not set | Pass -e KEY=... or env in config; check ${VAR} expansion |
| Hangs with no output for 30+ s | Package download, or waiting on interactive login | Preinstall, 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
| Scope | Stored in | Shared? |
|---|---|---|
local (default for claude mcp add) | ~/.claude.json under this project’s path | No |
project | .mcp.json at repo root | Yes, committed; requires per-user approval |
user | ~/.claude.json top level | No, all your projects |
| Plugin | the plugin’s config | Via plugin install |
| claude.ai connectors | your claude.ai account | Per account |
| Managed | managed-mcp.json / managedMcpServers | Org-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.jsonis read from the project root; servers from it need approval in interactive sessions, butclaude -pand 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.jsonuses 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
urlbut no"type": "http"is treated as stdio and fails. - For
claude mcp addstdio 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
npxfails, wrap it:"command": "cmd", "args": ["/c", "npx", "-y", "pkg"].
Env vars and limits
| Variable / key | Purpose |
|---|---|
MCP_TIMEOUT (ms) | Server startup timeout (default 30 s for -p with --mcp-config) |
MCP_TOOL_TIMEOUT (ms) / per-server timeout | Tool 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_TOKENS | Output cap (default 25,000); warning above 10,000 |
ENABLE_TOOL_SEARCH | Unset = 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_KEYare deliberately read as empty in a remote server’surlandheaders. Use a dedicated variable for the server’s token. - Editing
.mcp.jsonor~/.claude.jsonmid-session does not reconnect servers. Reconnect from/mcpor restart. - Oversized outputs waste context even when under the cap. Prefer server-side filters,
pagination, or a CLI (
gh,aws) over raisingMAX_MCP_OUTPUT_TOKENS. headersHelperscripts 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
claude mcp listshows✔ Connectedandclaude mcp get <name>shows the intended scope with noIssue:line.- New session:
/mcplists the expected tools; ask Claude to call one read-only tool and confirm a real result. - 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.