[BUG] MCP tools incorrectly mapped when connect multiple servers
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
Summary
When Claude Code initializes multiple MCP servers concurrently at startup, some servers receive responses intended for other servers. This causes tools to be mapped to incorrect server namespaces. The issue only occurs during initial startup - reconnecting via /mcp command resolves the mapping correctly.
Environment
- Claude Code version: 2.1.9
- MCP SDK version: 1.24.0 (
@modelcontextprotocol/sdk) - Platform: macOS (Darwin 25.1.0, arm64)
- MCP Transport: SSE (Server-Sent Events)
- Number of MCP servers: 21
- MCP Gateway: Custom gateway (OpenAPI-to-MCP proxy)
Problem Description
Observed Behavior
After a fresh start of Claude Code with 21 SSE-type MCP servers configured, some servers receive initialize responses intended for other servers:
From debug logs (~/.claude/debug/*.txt):
2024-XX-XXT05:39:15.826Z [DEBUG] MCP server "server-A": Starting connection...
2024-XX-XXT05:39:15.826Z [DEBUG] MCP server "server-B": Starting connection...
2024-XX-XXT05:39:15.828Z [DEBUG] MCP server "server-C": Starting connection...
2024-XX-XXT05:39:15.828Z [DEBUG] MCP server "server-D": Starting connection...
2024-XX-XXT05:39:15.940Z [DEBUG] MCP server "server-B": Connection established with capabilities: {"serverVersion":{"name":"server-A",...}} ❌ WRONG!
2024-XX-XXT05:39:15.940Z [DEBUG] MCP server "server-A": Connection established with capabilities: {"serverVersion":{"name":"server-A",...}} ✓ Correct
2024-XX-XXT05:39:15.977Z [DEBUG] MCP server "server-D": Connection established with capabilities: {"serverVersion":{"name":"server-D",...}} ✓ Correct
2024-XX-XXT05:39:16.014Z [DEBUG] MCP server "server-C": Connection established with capabilities: {"serverVersion":{"name":"server-D",...}} ❌ WRONG!
Key Observations
- Timing correlation: Servers that start connection at the exact same millisecond are affected
server-Aandserver-Bboth started at05:39:15.826Zserver-Candserver-Dboth started at05:39:15.828Z
- Response routing error: The
initializeresponse from one server is delivered to another server's connection
- Unique
serverInfo.namedoesn't help: Even after configuring each MCP server to return a uniqueserverInfo.name, the problem persists because the entire response is routed to the wrong connection
Expected Behavior
Each MCP server connection should receive its own initialize response, regardless of concurrent connections.
Workaround
Running /mcp command to reconnect all servers immediately fixes the tool mapping.
Root Cause Analysis
Hypothesis 1: MCP Gateway Bug
The MCP gateway may have a race condition when handling concurrent SSE connections:
- When multiple
/sseendpoints are requested simultaneously - The
initializeresponses may be routed to incorrect SSE streams
Evidence against: External concurrent testing (50+ rounds) could not reproduce the issue.
Hypothesis 2: Claude Code Client Bug
Claude Code's MCP client may have a race condition when:
- Multiple SSE connections are established simultaneously
- Responses arriving at the same time may be associated with wrong connections
Evidence for:
- The issue only occurs during Claude Code startup (high concurrency)
- Manual reconnection (
/mcp) always works correctly (lower concurrency) - Affected server pairs always start at the exact same millisecond
Hypothesis 3: MCP SDK Bug
The @modelcontextprotocol/sdk SSE transport may have issues with concurrent connections sharing some state.
Steps to Reproduce
Prerequisites
- Set up 10+ MCP servers accessible via SSE transport
- Configure all servers in Claude Code
Reproduction Steps
- Configure Claude Code with 10+ MCP servers in
~/.claude.json:
``json``
{
"projects": {
"/path/to/project": {
"mcpServers": {
"server-1": { "type": "sse", "url": "http://gateway/mcp/server-1/sse" },
"server-2": { "type": "sse", "url": "http://gateway/mcp/server-2/sse" },
"server-3": { "type": "sse", "url": "http://gateway/mcp/server-3/sse" },
// ... 10+ servers
}
}
}
}
- Start Claude Code fresh (not resuming a session)
- Check debug logs at
~/.claude/debug/*.txtfor "Connection established" entries
- Look for mismatches where
MCP server "X"showsserverVersion.name: "Y"(where X ≠ Y)
- Expected: Each server's
serverVersion.namematches its configured name
Actual: Some servers show another server's serverVersion.name
- Run
/mcpto reconnect - all mappings become correct
Detailed Debug Log Analysis
Connection Timeline
Time (ms) Event
─────────────────────────────────────────────────────────────
826 server-A: Starting connection
826 server-B: Starting connection ← Same millisecond!
828 server-C: Starting connection
828 server-D: Starting connection ← Same millisecond!
...
940 server-B: Connected (got server-A's response) ❌
940 server-A: Connected (correct) ← Same millisecond!
...
977 server-D: Connected (correct)
1014 server-C: Connected (got server-D's response) ❌
Pattern
Servers that start connection at the exact same millisecond are at risk of receiving each other's responses.
Suggested Investigation
- Check SSE connection isolation: Verify that each
SSEClientTransportinstance maintains completely isolated state
- Check response correlation: Verify that JSON-RPC response IDs are correctly matched to their originating connections
- Add connection identifiers: Consider adding unique connection IDs to debug logs to trace response routing
- Test with artificial delays: Add small random delays between MCP server connections to see if it prevents the issue
Potential Fix
// Instead of starting all connections simultaneously:
await Promise.all(servers.map(s => s.connect()));
// Consider sequential or batched initialization:
for (const server of servers) {
await server.connect();
await sleep(10); // Small delay between connections
}
// Or use a semaphore to limit concurrency:
const semaphore = new Semaphore(5);
await Promise.all(servers.map(async s => {
await semaphore.acquire();
try {
await s.connect();
} finally {
semaphore.release();
}
}));
Impact
- Severity: High - Tools are registered under wrong server namespaces
- Frequency: Occurs on most fresh starts with 10+ MCP servers
- Workaround available: Yes -
/mcpreconnect fixes the issue
Additional Information
Test Environment
- 21 MCP servers configured
- All using SSE transport via custom gateway
- Each server returns unique
serverInfo.name - Each server has distinct tools
Files for Reference
- Debug logs:
~/.claude/debug/*.txt - MCP config:
~/.claude.json→projects.*.mcpServers
Related Components
- MCP SDK:
@modelcontextprotocol/sdkv1.24.0 - Transport:
SSEClientTransport - Protocol version: 2024-11-05
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗