MCP: Silent failures make server integration opaque and hard to debug
Summary
MCP server failures are currently silent or cryptic, making it very hard to diagnose why a server isn't working. Several related pain points:
---
Issues
1. Wrong config file location is silently ignored
Current behavior: If an MCP server is defined in ~/.claude/mcp/canny.json (a subdirectory) or .claude/settings.local.json, it is silently ignored. No error, no warning, no hint. The server simply doesn't appear.
Expected behavior: Warn the user if a file that looks like an MCP config is found in a non-standard location, or at least document clearly which file locations are scanned.
---
2. Protocol version mismatch causes a silent 30-second timeout
Current behavior: When a custom MCP server responds with an older protocolVersion (e.g. 2024-11-05) during the initialize handshake, Claude Code silently discards the response and waits ~30 seconds before timing out. No error is shown to the user.
Expected behavior: Log a specific error like:
MCP server "canny" handshake failed: server returned protocolVersion "2024-11-05", expected "2025-11-25"
This would have saved hours of debugging. The mismatch is easy to fix once you know it's the cause.
---
3. No visibility into MCP server startup or handshake
Current behavior: There is no way to see what happened during server startup — did the process launch? Did it crash? Did the handshake succeed or fail?
Expected behavior:
/mcpor a new/mcp logscommand should show per-server status:starting,connected,failed (reason),timed out- Optionally: a
--mcp-debugflag that streams server stderr and protocol messages to the terminal
---
4. --mcp-config failures are not surfaced
Current behavior: When --mcp-config <file> is passed and the server fails to start or handshake, there is no feedback. The tools just don't appear.
Expected behavior: On startup, print a summary of MCP servers loaded vs. failed, e.g.:
MCP servers: playwright ✓ canny ✗ (handshake timeout — run with --mcp-debug for details)
---
Impact
Users building or debugging custom MCP servers have no feedback loop. The combination of silent config location failures + silent protocol mismatches means a server can "fail" in two completely invisible ways before a single tool call is ever made.
Environment
- Claude Code CLI
- macOS (darwin 25.2.0)
- Custom stdio MCP server (TypeScript/tsx)
11 Comments
Silent failures are a class of behavioral opacity bug that are especially dangerous because they produce confident-looking output on degraded capability. The three scenarios here all share the same structure: the agent believes a tool is available, the user believes it is available, but it is not — and nothing says so.
Two things are missing across all three cases:
1. Named diagnostic events, not just log entries. Log entries get lost; they are not surfaced in the agent's operating context. Each failure type (wrong config location, protocol version mismatch, tool call error) should produce a named event that the agent can reason about. Something like:
MCP server [name] failed to connect: protocol version mismatch (server: 2024-11-05, required: 2024-11-15). Proceeding without this server.That message should appear in the session, not only in a log.2. Capability state is part of context. When an MCP server fails to load, the agent's effective capability set changes. The agent should know that, and so should the user. A session that starts with 3 MCP servers but only 2 connected is operating in a degraded state — that should be visible, not silent.
The 30-second timeout on protocol version mismatch is the most actionable quick fix: the handshake response includes the version, so the mismatch is detectable immediately. No reason to wait 30 seconds before raising the error.
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
This is a perfect example of what we call a "silent bomb" — the server fails in two completely invisible ways before a single tool call is ever made. Wrong config location? Silent. Protocol version mismatch? Silent 30-second timeout with no error message. It's death by a thousand silences.
The pattern we see repeatedly: AI tooling gives developers optimistic feedback (no errors = everything is fine) when the reality is the integration is broken. Traditional debugging tools can't help because there's nothing to debug — the failure is in the absence of behavior, not in incorrect behavior.
We've been building Open Code Review specifically to catch this class of issues. Our checks validate configuration completeness, flag missing required fields, and detect protocol/contract mismatches at the PR level — before code ever reaches production. The thesis is that AI-generated integration code has failure modes that look nothing like traditional bugs.
For anyone dealing with MCP integration issues:
npx @opencodereview/cli scan . --sla L1catches configuration gaps, missing error handling, and silent failure patterns that traditional linters completely miss.The
/mcp logscommand suggestion is spot-on. Observability shouldn't be an afterthought for AI tool integrations.+1 from the operator side of the line.
We maintain NEXO Brain, a stdio MCP server that exposes ~300 tools across four Claude hosts (Claude Code, Codex, Claude Desktop, and the NEXO Desktop runtime). We've hit every failure mode in this thread and the silent-timeout one is by far the most expensive. Three concrete patterns we've landed on that mitigate the current opacity — useful as a stopgap until Claude Code surfaces more diagnostics natively:
mcp__nexo__nexo_startupas the first tool call and persist{installed, protocol_version, handshake_ms, tools_registered}to~/.nexo/coordination/session-briefing.txt. When the server silently times out, the hook output shows up as "missing" and the agent immediately knows to fall back instead of looping into a 30s stall.protocol_version_client,protocol_version_server,negotiated_version,tools_count,startup_ms. When a version mismatch happens Claude Code reports nothing, but the server log shows the rejectedinitializecall immediately. This is the single biggest time-saver for the kind of 2024-11-05 vs. 2025-11-25 case described here — the server always knows it was spoken to; the client pretends it wasn't.doctortool inside the MCP server. We exposenexo_doctorwhich the agent can call to self-diagnose: it returns scanned config locations, loaded server registrations, handshake latency, and any quarantined tools. This is a workaround for the "where is it looking?" problem — instead of reverse-engineering Claude Code's config scan order we just teach the server to report what it observes about its own install.For the specific asks in the issue I'd second (3) (
/mcp logs) as the highest-ROI surface. The protocol-version mismatch alone is worth a dedicated error code — an unknown-but-silent 30s wait is the worst debugging experience in the whole MCP stack right now.— Francisco (maintainer, NEXO Brain)
+1 on
MCP servers: playwright ✓ canny ✗ (handshake timeout — run with --mcp-debug for details)- this is related to my #39061 - there needs to be at least some way for MCP server to complain in a way that the complaint makes it to the user that is not the "run in debug mode and review the logs" method.Related to https://github.com/anthropics/claude-code/issues/16837. There used to be a
claude mcp list --debugflag that logged the MCP verbose logs under~/.claude/debug(and earlier even on the stdout (or stderr, I'm not sure) https://github.com/anthropics/claude-code/issues/7575).Confirming this on Windows 11 + Claude Code 2.1.123. Just spent ~12 hours
debugging four stdio MCP servers stuck at
✗ Failed to connectinclaude mcp list. Filed the underlying bug as #54786 (cwd field ignoredon Windows).
Discoverability gap that this issue describes was the entire problem:
claude mcp listsays only✗ Failed to connectsessions before the actual cause surfaced
DEBUG=true claude mcp listwasrun — the spawned Python interpreter's stderr (
can't open file) was the smoking gun all along'wrong\path\server.py'
Suggestions worth folding in if/when this gets prioritized:
✗ Failed to connectline (or at minimum point users at
DEBUG=truein the failure message)MCP_CONNECTION_NONBLOCKING,CLAUDE_CODE_DEBUG_LOGS_DIR,CLAUDE_CODE_DEBUG_LOG_LEVELsomewhere user-discoverable. Currently theyshow up only in the bundled JS source.
claude mcp listhealth check appears to send a quickinitializehandshake — when it fails, the only visible artifact is the binary
pass/fail. The captured stderr already exists in the debug logs; piping
the last line into the health-check output would be enough.
DEBUG=true claude mcp list- great, so getting debugging output is possible still (confirmed on2.1.121 (Claude Code)).I already see
MCP_CONNECTION_NONBLOCKING,CLAUDE_CODE_DEBUG_LOGS_DIRandCLAUDE_CODE_DEBUG_LOG_LEVELon https://code.claude.com/docs/en/env-vars.@nratzan the 12-hour debugging session is a painfully recognizable story. We operate ~300 MCP tools across four Claude hosts and the silent-timeout variant of this issue is, by far, the most expensive failure mode to diagnose in production.
Your three suggestions are spot-on, especially #1 (surface stderr in the
✗ Failed to connectline). In our case, adding a self-diagnostic tool (nexo_doctor) inside the MCP server itself was the only reliable workaround — the server can introspect its own health and report it through the tool interface since the transport layer won't.The
cwdbug you filed as #54786 is a good catch. Worth noting thatDEBUG=trueas @marcindulak confirmed works, but it shouldn't take a debug flag to see why a server failed — that's exactly the UX gap this issue describes.Thanks for the request. This is the same ask as #39061 (surface MCP startup errors and per-server logs). Consolidating tracking there.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.