MCP (Model Context Protocol)
MCP conecta Claude Code con herramientas y servicios externos. La implementación abarca 23 archivos en src/services/mcp/ y soporta 4 tipos de transporte, 2 flujos de autenticación, 6 ámbitos de configuración y un sistema completo de políticas enterprise.
mcp__<servidor>__<herramienta>.
Los caracteres fuera de [a-zA-Z0-9_-] se reemplazan por _,
con límite de 64 caracteres. Este prefijo es el que se usa en las reglas de permisos allow/deny.
Arquitectura
| Componente | Archivo | Propósito |
|---|---|---|
| Client | client.ts (~3.400 líneas) | Conexión, creación de transporte, encapsulado y llamadas a herramientas |
| Config | config.ts (~800 líneas) | Carga de configuración desde todos los ámbitos |
| Auth | auth.ts (~1.200 líneas) | Flujos de autenticación OAuth + XAA |
| XAA | xaa.ts (512 líneas) | Protocolo Cross-App Access (RFC 8693 + RFC 7523) |
| Normalization | normalization.ts | Normalización de nombres para el prefijo mcp__ |
| Env Expansion | envExpansion.ts | Expansión de ${VAR} y ${VAR:-default} en la config |
| In-Process | InProcessTransport.ts | Par de transportes enlazados para servidores en proceso (Chrome, Computer Use) |
| Elicitation | elicitationHandler.ts | Gestor de solicitudes de elicitación MCP (prompts de auth por URL) |
| MCPTool | src/tools/MCPTool/ | Plantilla base clonada por cada herramienta MCP expuesta al modelo |
| McpAuthTool | src/tools/McpAuthTool/ | Herramienta pseudo para servidores que necesitan autenticación |
Ámbitos de configuración (por prioridad)
Los servidores se cargan desde 6 fuentes. Mayor prioridad sobreescribe a menor cuando hay conflicto de nombres.
El ámbito enterprise es exclusivo: si existe managed-mcp.json,
el resto de ámbitos se ignoran.
| # | Ámbito | Fuente | Notas |
|---|---|---|---|
| 1 | claudeai | API fetch | Conectores de organización de Claude.ai (prioridad más baja) |
| 2 | plugin | dynamic | Servidores de plugins instalados |
| 3 | user | ~/.claude/settings.json | Configuración global del usuario |
| 4 | project | .mcp.json | Recorre el árbol de directorios hasta home |
| 5 | local | .claude/settings.local.json | Config privada del proyecto (no se sube al repo) |
| 6 | enterprise | managed-mcp.json | EXCLUSIVO: todos los demás ámbitos se ignoran cuando existe este archivo |
Ejemplo de config de proyecto (.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}" }
}
}
}
Todos los campos soportan expansión de ${VAR} y
${VAR:-default}.
Los servidores en .mcp.json requieren aprobación explícita del usuario antes de conectar
(almacenada en enabledMcpjsonServers en settings).
Tipos de transporte
Lanza un subproceso que se comunica por stdin/stdout. El transporte más común. El campo type es opcional por compatibilidad hacia atrás.
El stderr se captura para depuración (límite 64MB). Los servidores Chrome y Computer Use usan optimización en proceso para evitar ~325MB de overhead.
Conexión EventSource persistente. Sin timeout en el stream; las peticiones HTTP individuales tienen 60s de timeout.
Soporta OAuth con flujo en navegador. Soporta headersHelper para cabeceras de autenticación dinámicas.
Implementa el protocolo MCP Streamable HTTP 2025-03-26. Añade Accept: application/json, text/event-stream automáticamente.
Soporta gestión de sesión via ID de sesión. Soporta autenticación OAuth.
Transporte WebSocket. Soporta Bun y Node.js. Protocolo: ["mcp"].
Soporta opciones de proxy y TLS. Las cabeceras son configurables.
Ciclo de vida de la conexión
Arranque
Cargar configs de todos los ámbitos (enterprise tiene control exclusivo si existe)
Deduplicar servidores por firma (URL para remotos, comando para stdio)
Filtrar por políticas enterprise de allow/deny
Verificar el estado de aprobación de servidores del proyecto
Conectar servidores locales (concurrencia: 3) y remotos (concurrencia: 20) en paralelo
En fallo de auth: cachear 15 min, crear herramienta pseudo McpAuthTool
En éxito: obtener herramientas, comandos y recursos en paralelo
Flujo de llamada a herramienta
El modelo emite un bloque tool_use con nombre "mcp__servidor__herramienta"
ensureConnectedClient() verifica o reconecta si la caché fue limpiada
Verificación de permisos via el sistema estándar (passthrough por defecto)
callMCPTool() con timeout, verifica el flag isError en la respuesta
processMCPResult(): si la salida supera 100.000 chars, guarda en archivo temporal y devuelve instrucciones para usar Read
En error de elicitación de URL (-32042): reintenta hasta 3 veces
Apagado
Servidores stdio
SIGINT → 100ms → SIGTERM → 400ms → SIGKILL
Tiempo máximo total: 600ms
Servidores remotos
client.close() → cierra transporte, rechaza peticiones pendientes
Autenticación
OAuth en el navegador con almacenamiento automático del token en el keychain del SO, usando nombre del servidor y hash de config como clave. Se refresca automáticamente en 401. Soporta step-up (403 con scope) y revocación (RFC 7009).
"oauth": {
"clientId": "my-app",
"callbackPort": 8080,
"authServerMetadataUrl": "https://auth.example.com/.well-known/..."
}
Flujo SSO empresarial usando intercambio de tokens RFC 8693 y JWT Bearer grant RFC 7523.
Se activa con CLAUDE_CODE_ENABLE_XAA=1.
Ventaja clave: un único popup del navegador autentica N servidores MCP (el id_token se reutiliza).
Flujo: Descubrimiento PRM → Metadatos AS → id_token → ID-JAG → access_token
Cuando un servidor falla al conectar por auth, se crea una herramienta pseudo llamada
mcp__<servidor>__authenticate.
Al invocarla el modelo inicia el flujo OAuth y al completarse las herramientas reales reemplazan a la pseudo.
Siempre se aprueba automáticamente (sin prompt al usuario). El fallo de auth se cachea 15 minutos.
Exposición de herramientas
Ejemplos de nombres
| Nombre del servidor | Nombre de herramienta | Expuesto como |
|---|---|---|
| github | create_issue | mcp__github__create_issue |
| slack | search_public | mcp__slack__search_public |
| My Server! | do-thing | mcp__My_Server___do_thing |
Anotaciones de herramientas (especificación MCP)
| Anotación | Propiedad | Efecto |
|---|---|---|
| readOnlyHint | isConcurrencySafe, isReadOnly | Puede ejecutarse en paralelo, tratada como solo lectura |
| destructiveHint | isDestructive | Señal de mayor riesgo en los prompts de permiso |
| openWorldHint | isOpenWorld | La herramienta puede acceder a sistemas externos |
| _meta.anthropic/searchHint | searchHint | Herramienta diferida (no se carga en contexto hasta que se busca) |
| _meta.anthropic/alwaysLoad | alwaysLoad | Herramienta omite la diferida, siempre en contexto |
Las descripciones de herramientas tienen un límite de 2.048 caracteres.
Cuando la salida supera 100.000 caracteres, se guarda en un archivo temporal
y el modelo recibe instrucciones para usar la herramienta Read.
Sistema de permisos
Todas las herramientas MCP devuelven checkPermissions() → passthrough.
En modo default siempre se pregunta al usuario,
en bypassPermissions se aprueban automáticamente,
en modo auto decide el clasificador YOLO.
Reglas de permiso del usuario
{
"permissions": {
"allow": [
"mcp__github__search_code",
"mcp__slack__*"
],
"deny": [
"mcp__untrusted_server__*"
]
}
} Políticas enterprise de servidores (managed-mcp.json)
{
"allowedMcpServers": [
{ "serverName": "github" },
{ "serverUrl": "https://*.company.com/*" },
{ "serverCommand": ["npx", "-y", "@company/mcp-*"] }
],
"deniedMcpServers": [
{ "serverName": "untrusted" }
]
} La lista de denegación siempre tiene prioridad sobre la de permiso.
Comandos CLI
# Agregar servidor stdio (scope por defecto: local)
claude mcp add my-server npx -y @org/mcp-server
-s, --scope <scope> # local, user o project
-e, --env KEY=VALUE # variables de entorno
# Agregar servidor remoto (transporte detectado automáticamente desde la URL)
claude mcp add my-api https://api.example.com/mcp
-t, --transport sse|http
-H, --header "Key: Value"
--client-id <id> # Client ID para OAuth
--xaa # Habilitar Cross-App Access
# Agregar con config JSON completa
claude mcp add-json my-server '{"type":"http","url":"https://example.com/mcp"}'
# Otras operaciones
claude mcp remove my-server [-s scope]
claude mcp list
claude mcp get my-server Comando /mcp
| Subcomando | Descripción |
|---|---|
| /mcp | Abrir la interfaz de configuración de MCP |
| /mcp enable [nombre|all] | Habilitar servidor(es) |
| /mcp disable [nombre|all] | Deshabilitar servidor(es) |
| /mcp reconnect <nombre> | Reconectar un servidor específico (limpia caché, conexión nueva) |
Características avanzadas
Script de cabeceras dinámicas (headersHelper)
Apunta a un script en headersHelper. El script recibe el nombre y URL del servidor como variables de entorno y debe devolver JSON con las cabeceras. Tiempo límite: 10s. Las cabeceras dinámicas sobreescriben las estáticas.
Recursos MCP
Los servidores pueden exponer recursos (archivos, datos) via ListMcpResourcesTool y ReadMcpResourceTool. Ambas son herramientas diferidas. Los recursos blob se decodifican de base64 y se escriben en disco.
Prompts MCP como comandos
Los prompts expuestos por los servidores se convierten en comandos de barra (mcp__servidor__nombre-prompt). Los argumentos se analizan desde el esquema del prompt y se pasan a client.getPrompt().
Servidores específicos de agente
Los agentes pueden referenciar servidores existentes por nombre (compartidos) o definir servidores inline (se limpian al terminar el agente). Los servidores requeridos deben estar configurados o el agente no arranca.
Instrucciones de servidor
Los servidores proveen instrucciones de uso durante el handshake (límite 2.048 chars). Se inyectan en el system prompt cada turno. El modo delta (feature-gated) evita invalidar la caché.
Transporte en proceso
Los servidores Chrome y Computer Use se ejecutan en proceso via InProcessTransport, evitando ~325MB de overhead de subproceso. Usa pares de transportes enlazados con entrega por queueMicrotask().
Constantes y timeouts clave
| Variable / Constante | Valor | Propósito |
|---|---|---|
| MCP_TIMEOUT | 30.000ms | Timeout de conexión por servidor |
| MCP_TOOL_TIMEOUT | 100.000.000ms (~27,8h) | Timeout de llamada a herramienta (efectivamente infinito) |
| MCP_REQUEST_TIMEOUT_MS | 60.000ms | Timeout por petición HTTP (SSE/HTTP) |
| MAX_MCP_OUTPUT_TOKENS | 25.000 | Máximo de tokens en la salida de una herramienta |
| maxResultSizeChars | 100.000 chars | Límite de tamaño de salida antes de guardar en archivo |
| MAX_MCP_DESCRIPTION_LENGTH | 2.048 chars | Límite de descripción de herramienta |
| MCP_AUTH_CACHE_TTL_MS | 15 min | Duración de la caché needs-auth |
| MCP_FETCH_CACHE_SIZE | 20 | Tamaño de caché LRU para herramientas/recursos/comandos |
| concurrencia local | 3 | Conexiones simultáneas a servidores locales (stdio) |
| concurrencia remota | 20 | Conexiones simultáneas a servidores remotos |
| MAX_ERRORS_BEFORE_RECONNECT | 3 | Errores terminales antes de reconectar |
| MAX_URL_ELICITATION_RETRIES | 3 | Reintentos de elicitación de URL (-32042) |
MCP_TIMEOUT, MCP_TOOL_TIMEOUT y MAX_MCP_OUTPUT_TOKENS están en el conjunto SAFE_ENV_VARS: pueden configurarse via managed settings sin disparar un diálogo de seguridad.
Gestión de errores
| Tipo de error | Tratamiento |
|---|---|
| Timeout de conexión (30s) | Servidor marcado como failed |
| Error de auth (401) | Cacheado como needs-auth 15 min, creada McpAuthTool |
| ECONNRESET / ETIMEDOUT / EPIPE | Contados: 3 errores consecutivos cierran el transporte |
| ECONNREFUSED / EHOSTUNREACH | Error terminal, transporte cerrado de inmediato |
| Sesión expirada (404 + -32001) | Caché limpiada, reintento automático una vez |
| Elicitación de URL (-32042) | Mostrar diálogo de URL al usuario, reintentar hasta 3 veces |
.mcp.json necesitan aprobación explícita antes de conectar — usa
enableAllProjectMcpServers: true en settings para aprobarlos todos de golpe.
Segundo: puedes usar comodín en las reglas de permiso como mcp__slack__* para permitir
un servidor entero sin aprobar cada herramienta por separado. Tercero: el timeout de herramienta MCP es efectivamente infinito
(~27,8 horas) — si una herramienta parece bloqueada, usa /mcp reconnect nombre-servidor para forzar una conexión nueva.