Cap 6: MCP Servers

Por: Artiko
claude-codemcpserversintegraciones

Qué es MCP

El Model Context Protocol (MCP) es un estándar abierto para conectar herramientas de IA con fuentes de datos externas. Con MCP, Claude Code puede leer documentos en Google Drive, actualizar tickets en Jira, interactuar con Slack, o usar tooling personalizado.

Tipos de transporte

stdio (local)

El servidor MCP se ejecuta como proceso local. Claude Code se comunica via stdin/stdout:

{
  "mcpServers": {
    "context7": {
      "command": "npx",
      "args": ["-y", "@upstash/context7-mcp@latest"]
    }
  }
}

HTTP (remoto)

El servidor MCP se accede via HTTP/SSE:

{
  "mcpServers": {
    "mi-servidor": {
      "url": "https://mi-servidor.com/mcp",
      "headers": {
        "Authorization": "Bearer ${MCP_TOKEN}"
      }
    }
  }
}

Configuración

Archivo .mcp.json (proyecto)

{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@anthropic-ai/mcp-playwright@latest"]
    },
    "context7": {
      "command": "npx",
      "args": ["-y", "@upstash/context7-mcp@latest"]
    }
  }
}

Via CLI

# Agregar servidor
claude mcp add playwright npx @anthropic-ai/mcp-playwright@latest

# Listar servidores
claude mcp list

# Eliminar servidor
claude mcp remove playwright

Scopes

# Agregar a scope global (usuario)
claude mcp add --scope user context7 npx -y @upstash/context7-mcp@latest

# Agregar a scope proyecto
claude mcp add --scope project playwright npx @anthropic-ai/mcp-playwright@latest

OAuth

Algunos servidores MCP requieren autenticación OAuth:

{
  "mcpServers": {
    "google-drive": {
      "url": "https://mcp-google.com/sse",
      "oauth": {
        "clientId": "${GOOGLE_CLIENT_ID}",
        "scopes": ["drive.readonly"]
      }
    }
  }
}

Servers recomendados

Context7 — Documentación actualizada

Consulta documentación de cualquier librería en tiempo real:

{
  "context7": {
    "command": "npx",
    "args": ["-y", "@upstash/context7-mcp@latest"]
  }
}

Playwright — Automatización de navegador

Testing y automatización web:

{
  "playwright": {
    "command": "npx",
    "args": ["@anthropic-ai/mcp-playwright@latest"]
  }
}

Chrome DevTools — Debugging web

Inspección y debugging de páginas web en Chrome:

{
  "chrome-devtools": {
    "command": "npx",
    "args": ["@anthropic-ai/mcp-chrome-devtools@latest"]
  }
}

Excalidraw — Diagramas

Crear y editar diagramas:

{
  "excalidraw": {
    "command": "npx",
    "args": ["@anthropic-ai/mcp-excalidraw@latest"]
  }
}

Permisos para herramientas MCP

Las herramientas MCP siguen el patrón mcp__servidor__herramienta:

{
  "permissions": {
    "allow": [
      "mcp__context7__*",
      "mcp__playwright__browser_navigate"
    ],
    "deny": [
      "mcp__*__delete_*"
    ]
  }
}

Wildcard patterns

MCP en sub-agents

Puedes restringir qué servidores MCP tiene disponible un agente:

<!-- .claude/agents/web-tester.md -->
---
mcpServers: ["playwright", "chrome-devtools"]
---

Diagnóstico

Si un servidor MCP no funciona:

# Verificar estado
claude mcp list

# Modo debug
claude --debug "mcp"

Crear un MCP Server desde cero

El examen pregunta sobre implementación, no solo consumo.

Estructura mínima en TypeScript

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

const server = new Server(
  { name: "mi-servidor", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "get_weather",
      description: "Obtiene el clima de una ciudad",
      inputSchema: {
        type: "object",
        properties: { city: { type: "string" } },
        required: ["city"],
      },
    },
    {
      name: "search_products",
      description: "Busca productos por nombre",
      inputSchema: {
        type: "object",
        properties: { query: { type: "string" }, limit: { type: "number" } },
        required: ["query"],
      },
    },
  ],
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  if (name === "get_weather") {
    const city = args?.city as string;
    // Llamada real a API de clima
    const data = await fetch(`https://wttr.in/${city}?format=3`);
    const text = await data.text();
    return { content: [{ type: "text", text }] };
  }

  if (name === "search_products") {
    const { query, limit = 5 } = args as { query: string; limit?: number };
    const results = await searchDB(query, limit);
    return { content: [{ type: "text", text: JSON.stringify(results) }] };
  }

  return {
    isError: true,
    content: [{ type: "text", text: `Tool desconocida: ${name}` }],
  };
});

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch(console.error);

package.json mínimo:

{
  "name": "mi-mcp-server",
  "version": "1.0.0",
  "type": "module",
  "main": "dist/index.js",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0"
  },
  "devDependencies": {
    "typescript": "^5.0.0"
  }
}

Tipos de respuesta

TipoEstructuraCuándo usar
Éxitocontent: [{ type: "text", text: "..." }]Operación completada
Error manejadoisError: true, content: [...]Error esperado (API caída, input inválido)
Throwthrow new Error(...)Error de protocolo o bug interno

Usar isError: true en lugar de throw permite que Claude razone sobre el error y decida qué hacer (reintentar, cambiar estrategia, informar al usuario). Con throw, Claude recibe un error de protocolo sin contexto.

Debugging de MCP servers

# Logs verbosos del protocolo MCP
MCP_DEBUG=1 claude

# Probar el server manualmente antes de integrar
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | node dist/index.js

# Ver qué servidores están conectados
claude mcp list

Errores comunes:

ErrorCausaSolución
Timeout en startupServer tarda en iniciar o crasheaRevisar logs, verificar dependencias
JSON malformadoOutput extra en stdout (console.log)Usar console.error para logs, nunca console.log
Tool no encontradaNombre incorrecto en tools/listVerificar que el nombre coincide exactamente
Permission deniedServer sin permisos de ejecuciónchmod +x dist/index.js

Security model

Cada MCP server corre en su propio proceso aislado. Claude se comunica via protocolo MCP, no tiene acceso directo al filesystem ni variables de entorno del server.

flowchart LR
  C["Claude Code"]
  P["MCP Protocol\n(JSON-RPC)"]
  S["Server Process"]
  E["Variables de entorno"]
  D["Base de datos"]
  A["APIs externas"]

  C <--> P <--> S
  E --> S
  D --> S
  A --> S

  style C fill:#1e3a5f,color:#fff
  style S fill:#2d5a27,color:#fff
  style P fill:#4a4a4a,color:#fff

OAuth en MCP

sequenceDiagram
  participant U as Usuario
  participant CC as Claude Code
  participant S as MCP Server
  participant O as OAuth Provider

  U->>CC: Usa tool que requiere auth
  CC->>U: Redirige a pantalla OAuth
  U->>O: Autoriza acceso
  O->>CC: Retorna token
  CC->>CC: Almacena token en credentials store
  CC->>S: Request con Authorization header
  S->>S: Valida token
  S->>CC: Respuesta de la tool

Siguiente: Settings