claude-code/learn/11-mcp-deep-dive.md

291 lines
12 KiB
Markdown

# 11. MCP Deep Dive
> The Model Context Protocol subsystem -- transports, tool wrapping, authentication, and server lifecycle.
---
## What is MCP?
MCP (Model Context Protocol) is an open protocol for connecting AI models to external tools and data sources. Claude Code is both an **MCP client** (connecting to external MCP servers) and can act as an **MCP server** (exposing its own tools to other systems).
```mermaid
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#4a9eff', 'primaryBorderColor': '#4a9eff'}}}%%
graph TB
subgraph ClaudeCode["Claude Code"]
CLIENT["MCP Client<br/>Connects to external servers"]:::client
SERVER["MCP Server Mode<br/>Exposes tools to other systems"]:::server
TOOLS["42 Built-in Tools<br/>+ MCP tools merged in"]:::tools
end
subgraph External["External MCP Servers"]
STDIO_S["stdio servers<br/>Local processes"]:::external
SSE_S["SSE/HTTP servers<br/>Remote endpoints"]:::external
WS_S["WebSocket servers<br/>Persistent connections"]:::external
CLAUDE_AI["claude.ai proxy<br/>Managed connectors"]:::external
IDE_S["IDE servers<br/>Editor integration"]:::external
end
CLIENT --> STDIO_S
CLIENT --> SSE_S
CLIENT --> WS_S
CLIENT --> CLAUDE_AI
CLIENT --> IDE_S
STDIO_S --> TOOLS
SSE_S --> TOOLS
WS_S --> TOOLS
CLAUDE_AI --> TOOLS
IDE_S --> TOOLS
classDef client fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
classDef server fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
classDef tools fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
classDef external fill:#333,stroke:#888,color:#aaa,stroke-width:1px,stroke-dasharray: 5 5
```
---
## Transport Types
Claude Code supports **6 transport types** for connecting to MCP servers:
```mermaid
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#17a2b8', 'primaryBorderColor': '#17a2b8'}}}%%
flowchart TD
subgraph Local["Local Transports"]
STDIO["stdio<br/>Spawn local process<br/>Communicate via stdin/stdout<br/>Most common type"]:::local
SDK_T["sdk<br/>In-process transport<br/>SDK-managed placeholder<br/>Tool calls route to SDK"]:::local
end
subgraph Remote["Remote Transports"]
SSE["sse<br/>Server-Sent Events<br/>Legacy remote protocol<br/>OAuth authentication"]:::remote
HTTP["http<br/>Streamable HTTP<br/>Modern remote protocol<br/>Per-request timeouts"]:::remote
WS["ws<br/>WebSocket<br/>Persistent bidirectional<br/>TLS/proxy support"]:::remote
CLAUDEAI["claudeai-proxy<br/>Via claude.ai OAuth<br/>Managed connectors<br/>Auto token refresh"]:::remote
end
subgraph IDE["IDE Transports"]
SSE_IDE["sse-ide<br/>IDE SSE connection<br/>No authentication"]:::ide
WS_IDE["ws-ide<br/>IDE WebSocket<br/>Auth token in header"]:::ide
end
classDef local fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
classDef remote fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
classDef ide fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px
```
---
## Connection Lifecycle
```mermaid
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#28a745', 'actorTextColor': '#e0e0e0', 'actorBorder': '#28a745', 'signalColor': '#28a745', 'noteBkgColor': '#16213e', 'noteTextColor': '#e0e0e0', 'activationBkgColor': '#1b3a1b', 'activationBorderColor': '#28a745'}}}%%
sequenceDiagram
participant H as useManageMCPConnections
participant C as connectToServer (memoized)
participant T as Transport Layer
participant S as External Server
H->>H: getAllMcpConfigs - merge all sources
H->>H: Batch servers (local=3, remote=20)
loop For each server
H->>C: connectToServer(name, config)
activate C
alt Auth cached as needed
C-->>H: return needs-auth
else Server disabled
C-->>H: return disabled
else Policy blocked
C-->>H: return not allowed
end
C->>T: Create transport (stdio/sse/http/ws)
T->>S: Connect
activate S
alt auth required (401/403)
S-->>C: Auth error
C-->>H: return needs-auth
else connection timeout
S-->>C: Timeout (30s default)
C-->>H: return error
else success
S-->>T: Connected
deactivate S
C->>S: tools/list
S-->>C: Available tools
C->>C: Wrap as MCPTool objects
C-->>H: return connected + tools
end
deactivate C
end
H->>H: Update AppState.mcp
```
### Batched Connections
Servers are connected in parallel batches to avoid overwhelming the system:
- **Local (stdio) servers**: batch size of 3 (spawning processes is heavy)
- **Remote servers**: batch size of 20 (network connections are lighter)
The batch sizes are configurable via `MCP_SERVER_CONNECTION_BATCH_SIZE` and `MCP_REMOTE_SERVER_CONNECTION_BATCH_SIZE` environment variables.
---
## Tool Wrapping
Each MCP tool is wrapped in a `MCPTool` object that implements the standard `Tool` interface:
```mermaid
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#ffc107', 'primaryBorderColor': '#ffc107'}}}%%
flowchart LR
MCP_TOOL["MCP Server Tool<br/>name, inputSchema,<br/>description"]:::raw
WRAP["MCPTool wrapper<br/>Implements Tool interface<br/>name: mcp__server__tool"]:::wrap
subgraph Lifecycle["Tool Call Lifecycle"]
VALIDATE["Validate input<br/>via JSON Schema"]:::step
PERM["Check permissions<br/>Same system as built-in"]:::step
CALL["Forward to MCP server<br/>tools/call RPC"]:::step
RESULT["Process result<br/>Truncate if needed<br/>Handle images"]:::step
end
MCP_TOOL --> WRAP --> VALIDATE --> PERM --> CALL --> RESULT
classDef raw fill:#333,stroke:#888,color:#e0e0e0,stroke-width:1px
classDef wrap fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
classDef step fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
```
### Naming Convention
MCP tools are named `mcp__<server>__<tool>`:
- `mcp__slack__send_message`
- `mcp__github__create_issue`
- `mcp__ide__getDiagnostics`
The double-underscore separators prevent ambiguity. In SDK no-prefix mode, tools keep their original names.
### Description Capping
MCP tool descriptions are capped at **2,048 characters**. OpenAPI-generated servers have been observed dumping 15-60KB of endpoint docs into descriptions, which wastes context tokens.
---
## Configuration Sources
MCP server definitions come from multiple sources, merged with priority rules:
```mermaid
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#dc3545', 'primaryBorderColor': '#dc3545'}}}%%
flowchart TD
subgraph Sources["MCP Config Sources"]
ENT["Enterprise managed-mcp.json<br/>Exclusive control if present"]:::ent
GLOBAL["Global ~/.claude/settings.json<br/>mcpServers field"]:::user
LOCAL[".claude/settings.local.json<br/>Local project overrides"]:::local
PROJECT[".mcp.json at project root<br/>Committed to repo"]:::proj
PLUGIN["Plugin MCP servers<br/>From installed plugins"]:::plugin
CLAUDEAI["claude.ai connectors<br/>Fetched at startup"]:::claudeai
CLI["--mcp-config flag<br/>Runtime override"]:::cli
SDK_CFG["SDK mcp_set_servers<br/>Programmatic control"]:::sdk
end
MERGE["getAllMcpConfigs<br/>Merge + dedup + policy filter"]:::merge
POLICY{"Enterprise policy<br/>allowedMcpServers?<br/>deniedMcpServers?"}:::check
ALLOWED["Allowed servers<br/>proceed to connect"]:::allow
BLOCKED["Blocked servers<br/>silently dropped"]:::deny
Sources --> MERGE --> POLICY
POLICY -->|"allowed"| ALLOWED
POLICY -->|"denied"| BLOCKED
classDef ent fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px
classDef user fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
classDef local fill:#3d2b00,stroke:#fd7e14,color:#e0e0e0,stroke-width:2px
classDef proj fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px
classDef plugin fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
classDef claudeai fill:#1a1a4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
classDef cli fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
classDef sdk fill:#333,stroke:#888,color:#e0e0e0,stroke-width:1px
classDef merge fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
classDef check fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
classDef allow fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
classDef deny fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px
```
### Deduplication
Plugin and claude.ai connector servers are deduplicated against manually-configured servers:
- **Signature-based**: Same `command+args` (stdio) or same `url` (remote) = same server
- **Manual wins**: If a user manually configured a server, the plugin/connector duplicate is suppressed
- **First-loaded wins**: Between plugins, the first one loaded takes priority
---
## Authentication
Remote MCP servers may require OAuth authentication:
```mermaid
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#dc3545', 'primaryBorderColor': '#dc3545'}}}%%
flowchart TD
CONNECT["Connect to remote server"]:::start
AUTH{"Auth required?"}:::check
PROVIDER["ClaudeAuthProvider<br/>OAuth flow per server"]:::auth
TOKENS["Retrieve/refresh tokens"]:::auth
OK["Connection established"]:::success
NEEDS["Status: needs-auth<br/>Cached for 15 min"]:::needsauth
CALL["MCP tool call"]:::start
CALL_AUTH{"401 on call?"}:::check
RETRY["Refresh token<br/>retry once"]:::auth
MCE["McpAuthError<br/>Update status to needs-auth"]:::error
CONNECT --> AUTH
AUTH -->|"no"| OK
AUTH -->|"yes"| PROVIDER --> TOKENS
TOKENS -->|"success"| OK
TOKENS -->|"failure"| NEEDS
CALL --> CALL_AUTH
CALL_AUTH -->|"no"| OK
CALL_AUTH -->|"yes"| RETRY
RETRY -->|"success"| OK
RETRY -->|"failure"| MCE
classDef start fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
classDef check fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
classDef auth fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
classDef success fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
classDef needsauth fill:#3d2b00,stroke:#fd7e14,color:#e0e0e0,stroke-width:2px
classDef error fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px
```
### Session Expiry Handling
MCP servers return HTTP 404 + JSON-RPC error code `-32001` when a session expires. Claude Code detects this, clears the connection cache, and reconnects automatically (`McpSessionExpiredError`).
---
## Key Files
- `src/services/mcp/client.ts` (3,349 lines) -- Connection manager, tool wrapping, transport creation
- `src/services/mcp/config.ts` (1,579 lines) -- Config merging, policy filtering, deduplication
- `src/services/mcp/types.ts` -- Type definitions for server configs and connections
- `src/services/mcp/auth.ts` -- OAuth provider and step-up authentication
- `src/services/mcp/useManageMCPConnections.ts` -- React hook managing connection lifecycle
- `src/tools/MCPTool/MCPTool.ts` -- MCPTool wrapper implementing the Tool interface
---
**Previous:** [<- Configuration](./10-configuration-and-system-prompt.md) | **Next:** [Data Flow Walkthrough ->](./12-data-flow-walkthrough.md)