Los comandos y paquetes de esta lección se revisaron con la documentación oficial a octubre de 2026. El SDK y Claude Code se actualizan seguido: si un comando no funciona, compáralo con la documentación de MCP en Claude Code y con modelcontextprotocol.io. Versiones actuales: Lo vigente.
Lo esencial
MCP es como un puerto USB para Claude. El USB es un conector estándar: conectas un mouse, una memoria, un micrófono, una impresora, y la computadora los reconoce. MCP es un protocolo estándar: conectas tu CRM, tu base de datos, una API corporativa, el sistema de archivos, y Claude los ve como herramientas. Hoy vas a escribir tu propio servidor MCP: 30 a 50 líneas de código, y Claude gana capacidades nuevas que antes no tenía.
Conceptos clave
- MCP (Model Context Protocol): el estándar abierto de Anthropic para conectar la IA con herramientas externas (modelcontextprotocol.io)
- Servidor MCP: un programa que le ofrece a Claude tools, resources y prompts
- Transportes: stdio (local), HTTP (remoto, el recomendado), SSE (remoto, obsoleto), WebSocket (solo vía configuración JSON)
- Tres tipos de objetos: tools (acciones), resources (datos), prompts (plantillas)
- Tres alcances (scopes): local (por defecto, privado), project (con
.mcp.json, para el equipo), user (todos los proyectos) - Instalación:
claude mcp add(CLI),.mcp.json(archivo) o mediante un plugin - Economía de tokens: aviso cuando pasa de 10,000 tokens, límite por defecto de 25,000 tokens por llamada
- SDK de TypeScript:
@modelcontextprotocol/server/ SDK de Python:pip install "mcp[cli]"(el paquete viejo de TypeScript@modelcontextprotocol/sdktodavía aparece en ejemplos)
Teoría
Arquitectura de MCP
Claude Code (Client)
│
│ Protocolo MCP estándar (JSON-RPC 2.0)
│ por stdio o HTTP (SSE está obsoleto)
▼
MCP Server (tu código)
│
├── tools → funciones que Claude puede llamar
├── resources → datos que Claude puede leer
└── prompts → plantillas para tareas repetitivas
│
▼
Sistema externo (CRM, BD, API, archivos...)Lo clave: un servidor MCP es un programa común y corriente. Arranca cuando Claude Code se abre en el proyecto y sigue activo mientras la sesión está abierta. Claude llama a las tools mediante solicitudes JSON-RPC y el servidor responde con resultados.
Qué se puede hacer con servidores MCP conectados (según la documentación oficial):
- Implementar una función desde el issue tracker: "Haz la función de JIRA ENG-4521 y crea un PR en GitHub"
- Analizar el monitoreo: "Revisa Sentry y muéstrame los errores de las últimas 24 horas"
- Consultar bases de datos: "Encuentra a los usuarios que usaron la función X"
- Integrar diseños: "Actualiza la plantilla según los nuevos diseños de Figma"
- Automatizar: "Crea borradores de correo para estos 10 usuarios"
Tres formas de instalar servidores MCP
Forma 1: servidor HTTP remoto (la recomendada para servicios en la nube)
claude mcp add --transport http notion https://mcp.notion.com/mcpForma 2: servidor SSE remoto (obsoleto, usa HTTP)
claude mcp add --transport sse asana https://mcp.asana.com/sseForma 3: servidor stdio local
claude mcp add --transport stdio --env AIRTABLE_API_KEY=YOUR_KEY airtable \
-- npx -y airtable-mcp-serverImportante: todas las opciones (--transport, --env, --scope) van antes del nombre del servidor. -- separa el nombre del comando de arranque.
Tres alcances (scope)
| Scope | Dónde se guarda | Quién lo ve | Cuándo usarlo |
|---|---|---|---|
local (default) |
~/.claude.json |
Solo tú, solo en este proyecto | Servidores personales, experimentos |
project |
.mcp.json en la raíz del proyecto |
Todo el equipo (vía git) | Herramientas compartidas del proyecto |
user |
~/.claude.json |
Tú, en todos los proyectos | Utilidades personales para todos tus proyectos |
# Agregar con scope project (para el equipo)
claude mcp add --transport http --scope project sentry https://mcp.sentry.dev/mcpSi hay conflicto de nombres, la prioridad es: local > project > user > plugin > claude.ai connectors. Los servidores definidos por el administrador de la organización tienen prioridad sobre todos.
Administrar los servidores
claude mcp list # Lista de todos los servidores
claude mcp get github # Detalles de un servidor
claude mcp remove github # Eliminar un servidor
/mcp # Dentro de Claude Code: estado de los servidores y reconexiónEconomía de tokens de MCP (importante para el negocio)
Cada servidor MCP consume tokens de la ventana de contexto. Esto pesa mucho en el costo:
- Aviso cuando una sola llamada devuelve más de 10,000 tokens
- Límite por defecto: 25,000 tokens por respuesta de una tool de MCP
- Ajustar el límite:
MAX_MCP_OUTPUT_TOKENS=50000 claude - Tiempo de espera al arrancar:
MCP_TIMEOUT=10000 claude(10 segundos)
Reconexión automática: si un servidor HTTP/SSE se desconecta, Claude Code se reconecta solo con espera exponencial (hasta 5 intentos). Los servidores stdio locales no se reconectan solos: reinícialos desde /mcp.
Tres tipos de objetos MCP
Tools (herramientas): acciones que Claude puede ejecutar:
get_contact: obtener un contacto del CRMcreate_task: crear una tareasend_message: enviar un mensajequery_database: ejecutar una consulta a la BD
Resources (recursos): datos que Claude puede leer:
crm://contacts/list: lista de contactosdb://reports/monthly: reporte mensualfile://config/settings: configuración de la aplicación
Prompts (plantillas): instrucciones listas para tareas típicas:
analyze_deal: plantilla para analizar un tratowrite_followup: plantilla de correo de seguimiento
A la mayoría de los proyectos les bastan las tools.
Servidor MCP mínimo: Hello World
Instalamos el SDK:
npm init -y
npm install @modelcontextprotocol/server zod
npm install -D @types/node typescript
mkdir srcEn package.json agrega "type": "module". Al lado pon un tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"types": ["node"],
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}Creamos src/server.ts:
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import { z } from "zod";
// Creamos el servidor
const server = new McpServer({
name: "my-first-mcp",
version: "1.0.0",
});
// Agregamos una tool: una función simple
server.registerTool(
"get_weather", // Nombre de la tool
{
description: "Obtener el clima de una ciudad", // Descripción para Claude
inputSchema: z.object({ // Parámetros (esquema Zod)
city: z.string().describe("Nombre de la ciudad"),
}),
},
async ({ city }) => {
// Aquí va la lógica real: llamada a una API, consulta a la BD, etc.
// Para el ejemplo, un valor fijo
return {
content: [{
type: "text",
text: `Clima en ${city}: +22°C, nublado`
}]
};
}
);
// Conectamos el transporte stdio y arrancamos
const transport = new StdioServerTransport();
await server.connect(transport);Importante: un servidor stdio se comunica con Claude por la salida estándar, así que no puedes imprimir logs con console.log. Para los logs usa console.error.
Compilamos y arrancamos:
npx tsc
node build/server.jsEjemplo real: un servidor MCP para un CRM
Un servidor completo que Claude Code usa para trabajar con un CRM ficticio mediante una API REST:
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import { z } from "zod";
const CRM_API_URL = process.env.CRM_API_URL || "https://api.mycrm.com";
const CRM_API_KEY = process.env.CRM_API_KEY || "";
const server = new McpServer({
name: "crm-mcp-server",
version: "1.0.0",
});
// Tool 1: obtener un contacto por nombre o email
server.registerTool(
"get_contact",
{
description: "Buscar un contacto en el CRM por nombre o dirección de email",
inputSchema: z.object({
query: z.string().describe("Nombre o email a buscar"),
}),
},
async ({ query }) => {
const response = await fetch(
`${CRM_API_URL}/contacts/search?q=${encodeURIComponent(query)}`,
{ headers: { "X-API-Key": CRM_API_KEY } }
);
const data = await response.json();
if (!data.contacts?.length) {
return { content: [{ type: "text", text: `No se encontró el contacto "${query}"` }] };
}
const contact = data.contacts[0];
return {
content: [{
type: "text",
text: JSON.stringify({
id: contact.id,
name: contact.full_name,
email: contact.email,
company: contact.company,
deal_stage: contact.deal_stage,
last_contact: contact.last_contact_date,
}, null, 2)
}]
};
}
);
// Tool 2: crear una tarea
server.registerTool(
"create_task",
{
description: "Crear una tarea en el CRM ligada a un contacto",
inputSchema: z.object({
contact_id: z.string().describe("ID del contacto"),
title: z.string().describe("Nombre de la tarea"),
due_date: z.string().describe("Fecha límite en formato YYYY-MM-DD"),
priority: z.enum(["low", "medium", "high"]).describe("Prioridad de la tarea"),
}),
},
async ({ contact_id, title, due_date, priority }) => {
const response = await fetch(`${CRM_API_URL}/tasks`, {
method: "POST",
headers: {
"X-API-Key": CRM_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ contact_id, title, due_date, priority }),
});
const task = await response.json();
return {
content: [{
type: "text",
text: `Tarea creada. ID: ${task.id}. Fecha límite: ${due_date}. Prioridad: ${priority}.`
}]
};
}
);
// Tool 3: actualizar la etapa del trato
server.registerTool(
"update_deal_stage",
{
description: "Actualizar la etapa del trato de un contacto",
inputSchema: z.object({
contact_id: z.string().describe("ID del contacto"),
stage: z.enum(["lead", "qualified", "proposal", "negotiation", "closed_won", "closed_lost"])
.describe("Nueva etapa del trato"),
note: z.string().optional().describe("Nota sobre el cambio de etapa"),
}),
},
async ({ contact_id, stage, note }) => {
await fetch(`${CRM_API_URL}/contacts/${contact_id}`, {
method: "PATCH",
headers: {
"X-API-Key": CRM_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ deal_stage: stage, stage_note: note }),
});
return {
content: [{
type: "text",
text: `Etapa del trato actualizada: ${stage}${note ? `. Nota: ${note}` : ""}`
}]
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);Registrarlo en el proyecto: .mcp.json
Para que Claude Code arranque tu servidor MCP automáticamente al abrir el proyecto, creas .mcp.json en la raíz del proyecto:
{
"mcpServers": {
"crm": {
"command": "node",
"args": ["./mcp-servers/crm/build/server.js"],
"env": {
"CRM_API_URL": "https://api.mycrm.com",
"CRM_API_KEY": "${CRM_API_KEY}"
}
}
}
}A partir de ahí, cuando abres Claude Code en esa carpeta, el servidor arranca solo. Claude ve las tools get_contact, create_task y update_deal_stage como capacidades integradas.
Variables de entorno en .mcp.json (función oficial):
Se admite la sintaxis ${VAR} y ${VAR:-default} en los campos command, args, env, url y headers:
{
"mcpServers": {
"api-server": {
"type": "http",
"url": "${API_BASE_URL:-https://api.example.com}/mcp",
"headers": {
"Authorization": "Bearer ${API_KEY}"
}
}
}
}Así puedes subir .mcp.json a git sin secretos: cada desarrollador pone sus propias variables de entorno.
Registro por CLI (alternativa):
# Con scope user (disponible en todos los proyectos)
claude mcp add --transport stdio --scope user crm -- node ./server.js
# Agregar desde JSON
claude mcp add-json crm '{"command":"node","args":["./server.js"],"env":{"CRM_API_KEY":"${CRM_API_KEY}"}}'Importar desde Claude Desktop (si ya lo tienes configurado; funciona en macOS y WSL):
claude mcp add-from-claude-desktopPruebas: MCP Inspector
El proyecto MCP ofrece un inspector interactivo para probar servidores sin Claude (requiere Node 22.19 o más nuevo):
npx @modelcontextprotocol/inspector node build/server.jsSe abre una interfaz web donde puedes:
- Ver todas las tools registradas
- Llamar cada tool a mano con parámetros
- Ver qué responde el servidor
- Depurar errores
También hay un modo de consola: npx @modelcontextprotocol/inspector --cli node build/server.js --method tools/list muestra la lista de tools y termina. Es mucho más rápido que probar a través de Claude Code.
Claude Code como servidor MCP
Claude Code puede funcionar él mismo como servidor MCP para otras aplicaciones:
claude mcp serveConexión desde Claude Desktop:
{
"mcpServers": {
"claude-code": {
"type": "stdio",
"command": "claude",
"args": ["mcp", "serve"],
"env": {}
}
}
}Esto les da a otras aplicaciones de IA acceso a las herramientas de Claude Code (Read, Edit, Bash y otras).
Ligar servidores MCP a subagentes
Puedes ligar servidores MCP a un subagente concreto con el campo mcpServers del frontmatter:
---
name: browser-tester
description: Tests features in a real browser using Playwright
mcpServers:
- playwright:
type: stdio
command: npx
args: ["-y", "@playwright/mcp@latest"]
---Los servidores en línea se conectan cuando arranca el subagente y se desconectan cuando termina. La conversación principal no ve esas herramientas, y eso ahorra contexto.
La versión en Python: FastMCP
Si prefieres Python, hay una forma más declarativa: el SDK arma la descripción de la tool a partir de las anotaciones de tipo y el docstring. En la documentación actual la clase se llama MCPServer (en ejemplos viejos aparece FastMCP):
# pip install "mcp[cli]" o uv add "mcp[cli]"
from mcp.server import MCPServer
mcp = MCPServer("crm-server")
@mcp.tool()
def get_contact(query: str) -> str:
"""Buscar un contacto en el CRM por nombre o email"""
# Lógica de búsqueda
return f"Contacto encontrado: {query}"
@mcp.tool()
def create_task(contact_id: str, title: str, due_date: str) -> str:
"""Crear una tarea en el CRM"""
# Lógica para crear la tarea
return f"Tarea creada: {title} para {contact_id}"
if __name__ == "__main__":
mcp.run(transport="stdio")Registro en .mcp.json:
{
"mcpServers": {
"crm-python": {
"command": "python",
"args": ["./mcp-servers/crm_server.py"]
}
}
}Publicar en npm
Si quieres compartir tu servidor MCP o usarlo en varios proyectos:
# package.json
{
"name": "@yourname/mcp-crm",
"version": "1.0.0",
"type": "module",
"bin": { "mcp-crm": "./server.js" },
"main": "./server.js"
}
# Publicación
npm publish --access publicUna vez publicado, cualquiera puede usarlo:
{
"mcpServers": {
"crm": {
"command": "npx",
"args": ["-y", "@yourname/mcp-crm"]
}
}
}Recursos de MCP: menciones con @
Los servidores MCP pueden ofrecer resources que mencionas con @:
Analiza @github:issue://123 y propón una corrección Revisa la documentación @docs:file://api/authentication
Los recursos aparecen en el autocompletado, junto a los archivos, cuando escribes @.
Actualización dinámica de herramientas
Claude Code admite las notificaciones list_changed de los servidores MCP. Si un servidor agrega o quita tools, Claude Code actualiza la lista solo, sin reconectar.
Práctica
Tarea: un servidor MCP para trabajar con archivos de notas locales
- Crea la carpeta
my-notes-mcp/e inicializa el proyecto con los pasos de la sección Hello World de arriba:npm init -y,npm install @modelcontextprotocol/server zod,npm install -D @types/node typescript,"type": "module"ytsconfig.json - Crea
src/server.tscon tres tools:list_notes: lista de archivos en la carpeta~/Notes/(o la que tú quieras)read_note: leer un archivo por su nombrecreate_note: crear un archivo nuevo con una nota
- Compila:
npx tsc - Pruébalo con MCP Inspector:
npx @modelcontextprotocol/inspector node build/server.js - Regístralo en el
.mcp.jsondel proyecto - Reinicia Claude Code y comprueba que aparecieron las tools
- Pídele a Claude: "Crea una nota sobre la reunión de hoy"; debería usar tu tool
- Bonus: agrega una tool
search_notesque busque en el contenido de las notas con grep
Objetivo: escribir un servidor MCP que funcione desde cero, registrarlo y probarlo en Claude Code.
Herramientas y recursos
- @modelcontextprotocol/server:
npm install @modelcontextprotocol/server, el SDK oficial de TypeScript - mcp:
pip install "mcp[cli]", el SDK de Python (claseMCPServer, antesFastMCP) - zod:
npm install zod, tipado de los parámetros de las tools (obligatorio con el SDK de TypeScript) - MCP Inspector:
npx @modelcontextprotocol/inspector, para probar sin Claude - Documentación: modelcontextprotocol.io, la especificación del protocolo
- Página oficial de MCP en Claude Code: https://code.claude.com/docs/en/mcp
- GitHub: github.com/modelcontextprotocol/servers, cientos de servidores listos
- Comandos de CLI:
claude mcp add: agregar un servidorclaude mcp list: lista de servidoresclaude mcp get <name>: detalles de un servidorclaude mcp remove <name>: eliminarclaude mcp add-from-claude-desktop: importar desde Claude Desktopclaude mcp add-json <name> '<json>': agregar desde JSONclaude mcp serve: correr Claude Code como servidor MCP/mcp: estado de los servidores dentro de Claude Code (incluida la autenticación OAuth)
Ideas clave
Un servidor MCP es un programa común en TypeScript o Python. De 30 a 50 líneas de código le dan a Claude herramientas nuevas. La complejidad solo crece con la complejidad de tu lógica, no por el protocolo MCP.
Transportes: stdio (local), HTTP (remoto, el recomendado), SSE (obsoleto), WebSocket (vía configuración JSON). Tres scopes: local (por defecto), project (
.mcp.jsonpara el equipo), user (todos los proyectos).
Economía de tokens: los resultados de las llamadas MCP consumen contexto (las descripciones de las herramientas, por defecto, se cargan cuando hacen falta). Aviso con más de 10,000 tokens de salida. Límite por defecto de 25,000 tokens. Se ajusta con
MAX_MCP_OUTPUT_TOKENS.
Prueba con MCP Inspector antes de conectarlo a Claude: te ahorra tiempo de depuración.
Puedes ligar servidores MCP a subagentes con el campo
mcpServersdel frontmatter: el servidor solo se conecta mientras trabaja el subagente y no llena el contexto de la conversación principal.
.mcp.jsonadmite variables de entorno (${VAR},${VAR:-default}): puedes subirlo a git sin secretos.
Siguiente lección
La marca se guarda solo en este navegador y no se envía a ningún sitio. Mi progreso