C Claude Code Internals
EN | ES

MCP (Model Context Protocol)

MCP connects Claude Code to external tools and services. The implementation spans 23 files in src/services/mcp/ and supports 4 transport types, 2 auth flows, 6 configuration scopes, and a full enterprise policy system.

4 transport types 6 config scopes 23 implementation files 2 auth flows (OAuth + XAA)
i Tool naming convention
Every MCP tool is exposed as mcp__<server>__<tool>. Characters outside [a-zA-Z0-9_-] are replaced with _, capped at 64 characters. This prefix is what you use in allow/deny permission rules.

Architecture

Component File Purpose
Client client.ts (~3,400 lines) Connection, transport creation, tool wrapping, tool calls
Config config.ts (~800 lines) Config loading from all scopes
Auth auth.ts (~1,200 lines) OAuth + XAA authentication flows
XAA xaa.ts (512 lines) Cross-App Access protocol (RFC 8693 + RFC 7523)
Normalization normalization.ts Name normalization for mcp__ prefix
Env Expansion envExpansion.ts ${VAR} and ${VAR:-default} expansion in config
In-Process InProcessTransport.ts Linked transport pair for in-process servers (Chrome, Computer Use)
Elicitation elicitationHandler.ts MCP elicitation request handler (URL-based auth prompts)
MCPTool src/tools/MCPTool/ Base template cloned per MCP tool exposed to the model
McpAuthTool src/tools/McpAuthTool/ Pseudo-tool for servers needing authentication

Configuration scopes (priority order)

Servers are loaded from 6 sources. Higher priority overrides lower when names conflict. The enterprise scope is exclusive: if managed-mcp.json exists, all other scopes are ignored.

# Scope Source Notes
1 claudeai API fetch Claude.ai organization connectors (lowest priority)
2 plugin dynamic Servers from installed plugins
3 user ~/.claude/settings.json Global user config
4 project .mcp.json Walks up directory tree to home
5 local .claude/settings.local.json Private project config (not committed)
6 enterprise managed-mcp.json EXCLUSIVE: all other scopes ignored when this file exists

Project config example (.mcp.json)

{
  "mcpServers": {
    "github": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
    },
    "my-api": {
      "type": "sse",
      "url": "https://example.com/mcp/sse",
      "headers": { "Authorization": "Bearer ${API_TOKEN}" }
    }
  }
}

All config fields support ${VAR} and ${VAR:-default} expansion. Servers in .mcp.json require explicit user approval before connecting (stored in enabledMcpjsonServers in settings).

Transport types

stdio default

Launches a subprocess communicating via stdin/stdout. Most common transport. type field is optional for backwards compatibility.

Stderr is captured for debugging (capped at 64MB). Chrome and Computer Use servers use in-process optimization to avoid ~325MB subprocess overhead.

sse remote

Long-lived EventSource connection. No timeout on the stream; individual HTTP requests have 60s timeout.

Supports OAuth with browser-based flow. Supports headersHelper script for dynamic authentication headers.

http remote

Implements the MCP 2025-03-26 Streamable HTTP protocol. Adds Accept: application/json, text/event-stream automatically.

Supports session management via session ID. Supports OAuth authentication.

ws remote

WebSocket transport. Supports both Bun and Node.js runtimes. Protocol: ["mcp"].

Supports proxy and TLS options. Headers are configurable.

Connection lifecycle

Startup

1

Load configs from all scopes (enterprise takes exclusive control if present)

2

Deduplicate servers by signature (URL for remote, command for stdio)

3

Filter by enterprise allow/deny policies

4

Check project server approval status

5

Connect local servers (concurrency: 3) and remote servers (concurrency: 20) in parallel

6

On auth failure: cache 15 min, create McpAuthTool pseudo-tool

7

On success: fetch tools, commands, and resources in parallel

Tool call flow

1

Model outputs tool_use block with name "mcp__server__tool"

2

ensureConnectedClient() verifies or reconnects if cache was cleared

3

Permission check via standard permission system (passthrough by default)

4

callMCPTool() with timeout, checks isError flag in response

5

processMCPResult(): if output > 100,000 chars, saves to temp file and returns Read instructions

6

On URL elicitation error (-32042): retries up to 3 times

Shutdown

Stdio servers

SIGINT → 100ms → SIGTERM → 400ms → SIGKILL

Total failsafe: 600ms maximum

Remote servers

client.close() → closes transport, rejects pending requests

Authentication

OAuth standard flow SSE + HTTP

Browser-based OAuth with automatic token storage in the OS keychain, keyed by server name and config hash. Auto-refreshes on 401. Supports step-up (403 with scope) and revocation (RFC 7009).

"oauth": {
  "clientId": "my-app",
  "callbackPort": 8080,
  "authServerMetadataUrl": "https://auth.example.com/.well-known/..."
}
XAA (Cross-App Access) enterprise SSO

Enterprise SSO flow using RFC 8693 token exchange and RFC 7523 JWT Bearer grant. Enabled with CLAUDE_CODE_ENABLE_XAA=1. Key benefit: a single browser popup authenticates N MCP servers (cached id_token is reused).

Flow: PRM Discovery → AS Metadata → id_token → ID-JAG → access_token

McpAuthTool (pseudo-tool) needs-auth state

When a server fails to connect due to auth, a pseudo-tool named mcp__<server>__authenticate is created. When the model calls it, the OAuth flow starts, and upon completion the real tools replace the pseudo-tool. Always auto-approved (no user permission prompt). Auth failure is cached for 15 minutes.

Tool exposure

Naming examples

Server name Tool name Exposed as
github create_issue mcp__github__create_issue
slack search_public mcp__slack__search_public
My Server! do-thing mcp__My_Server___do_thing

Tool annotations (from MCP spec)

Annotation Property Effect
readOnlyHint isConcurrencySafe, isReadOnly Tool can run in parallel, treated as read-only
destructiveHint isDestructive Higher risk signal in permission prompts
openWorldHint isOpenWorld Tool can reach external systems
_meta.anthropic/searchHint searchHint Tool is deferred (not loaded into context until searched)
_meta.anthropic/alwaysLoad alwaysLoad Tool bypasses deferral, always in context

Tool descriptions are capped at 2048 characters. When output exceeds 100,000 characters, it is saved to a temp file and the model receives instructions to use the Read tool.

Permission system

All MCP tools return checkPermissions() → passthrough. This means in default mode the user is always prompted, in bypassPermissions mode they are auto-approved, and in auto mode the YOLO classifier decides.

User permission rules

{
  "permissions": {
    "allow": [
      "mcp__github__search_code",
      "mcp__slack__*"
    ],
    "deny": [
      "mcp__untrusted_server__*"
    ]
  }
}

Enterprise server policies (managed-mcp.json)

{
  "allowedMcpServers": [
    { "serverName": "github" },
    { "serverUrl": "https://*.company.com/*" },
    { "serverCommand": ["npx", "-y", "@company/mcp-*"] }
  ],
  "deniedMcpServers": [
    { "serverName": "untrusted" }
  ]
}

Denylist always takes precedence over allowlist.

CLI commands

# Add a stdio server (default scope: local)
claude mcp add my-server npx -y @org/mcp-server
  -s, --scope <scope>      # local, user, or project
  -e, --env KEY=VALUE      # environment variables

# Add a remote server (transport auto-detected from URL)
claude mcp add my-api https://api.example.com/mcp
  -t, --transport sse|http
  -H, --header "Key: Value"
  --client-id <id>         # OAuth client ID
  --xaa                    # Enable Cross-App Access

# Add with full JSON config
claude mcp add-json my-server '{"type":"http","url":"https://example.com/mcp"}'

# Other operations
claude mcp remove my-server [-s scope]
claude mcp list
claude mcp get my-server

/mcp slash command

Subcommand Description
/mcp Open MCP settings UI
/mcp enable [name|all] Enable server(s)
/mcp disable [name|all] Disable server(s)
/mcp reconnect <name> Reconnect a specific server (clears cache, fresh connection)

Advanced features

Headers helper script

Set headersHelper to a script path. The script receives the server name and URL as env vars and must output JSON headers. Runs with 10s timeout. Dynamic headers override static ones.

MCP resources

Servers can expose resources (files, data) via ListMcpResourcesTool and ReadMcpResourceTool. Both are deferred tools. Blob resources are base64-decoded and written to disk.

MCP prompts as commands

Server-exposed prompts become slash commands (mcp__server__prompt-name). Arguments are parsed from the prompt's argument schema and passed to client.getPrompt().

Agent-specific servers

Agents can reference existing servers by name (shared) or define inline servers (cleaned up when the agent finishes). Required servers are declared and must be configured.

Server instructions

Servers provide usage instructions during handshake (truncated at 2048 chars). Injected as a system prompt section each turn. Delta mode (feature-gated) avoids cache invalidation.

In-process transport

Chrome and Computer Use servers run in-process via InProcessTransport, avoiding ~325MB subprocess overhead. Uses linked transport pairs with queueMicrotask() delivery.

Key constants and timeouts

Variable / Constant Value Purpose
MCP_TIMEOUT 30,000ms Connection timeout per server
MCP_TOOL_TIMEOUT 100,000,000ms (~27.8h) Tool call timeout (effectively infinite)
MCP_REQUEST_TIMEOUT_MS 60,000ms Per HTTP request timeout (SSE/HTTP)
MAX_MCP_OUTPUT_TOKENS 25,000 Max tokens in tool output
maxResultSizeChars 100,000 chars Output size cap before saving to file
MAX_MCP_DESCRIPTION_LENGTH 2048 chars Tool description cap
MCP_AUTH_CACHE_TTL_MS 15 min needs-auth cache duration
MCP_FETCH_CACHE_SIZE 20 LRU cache size for tools/resources/commands
local concurrency 3 Simultaneous local (stdio) server connections
remote concurrency 20 Simultaneous remote server connections
MAX_ERRORS_BEFORE_RECONNECT 3 Terminal errors before reconnect
MAX_URL_ELICITATION_RETRIES 3 URL elicitation (-32042) retries

MCP_TIMEOUT, MCP_TOOL_TIMEOUT, and MAX_MCP_OUTPUT_TOKENS are in the SAFE_ENV_VARS set: they can be set via managed settings without triggering a security dialog.

Error handling

Error type Handling
Connection timeout (30s) Server marked as failed
Auth error (401) Cached as needs-auth for 15 min, McpAuthTool created
ECONNRESET / ETIMEDOUT / EPIPE Counted: 3 consecutive errors close the transport
ECONNREFUSED / EHOSTUNREACH Terminal error, transport closed immediately
Session expired (404 + -32001) Cache cleared, auto-retry once
URL elicitation (-32042) Show URL dialog to user, retry up to 3 times
Three things most users don't know about MCP
First: servers in .mcp.json need explicit approval before connecting — use enableAllProjectMcpServers: true in settings to bulk-approve. Second: you can wildcard permission rules like mcp__slack__* to allow an entire server without approving each tool. Third: the MCP tool timeout is effectively infinite (~27.8 hours) — if a tool seems stuck, use /mcp reconnect server-name to force a fresh connection.