Un MCP Server es un programa que expone tools estructuradas a los modelos de IA a través del Model Context Protocol estandarizado. Con el SDK oficial de TypeScript puedes construir un MCP Server funcional en menos de 30 minutos. Este artículo cubre cada paso: desde el setup hasta el deployment.
MCP (Model Context Protocol) se ha convertido en el estándar de facto para la integración de tools de IA desde noviembre de 2024. Ya existen más de 10.000 MCP Servers. Pero la mayoría de desarrolladores usa servers prehechos, y así pierden el potencial de construir tools hechas a medida para sus necesidades exactas.
¿Qué necesito para construir un MCP Server?
Un MCP Server necesita tres cosas: Node.js (versión 18 o superior), el SDK oficial de MCP y un cliente compatible con MCP para testear (Claude Desktop, Claude Code o Cursor).
La estructura básica es sencilla:
mkdir mi-mcp-server && cd mi-mcp-server
npm init -y
npm install @modelcontextprotocol/server @modelcontextprotocol/node zod
npm install -D typescript @types/node
npx tsc --init
El SDK ofrece dos opciones de transport: stdio (server local, lee de stdin/stdout) y Streamable HTTP (server remoto, corre como servicio web). Para empezar recomendamos stdio, funciona de inmediato sin ningún setup de red.
¿Cómo defino tools en el schema MCP?
Las tools son el núcleo de cada MCP Server. Una tool tiene un nombre, una descripción (que el modelo de IA lee para decidir si la usa) y un input schema con validación Zod.
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/node";
import { z } from "zod";
const server = new McpServer({
name: "mi-server",
version: "1.0.0",
});
server.registerTool(
"get_weather",
{
description: "Obtener datos meteorológicos actuales de una ciudad",
inputSchema: z.object({
ciudad: z.string().describe("Nombre de la ciudad"),
}),
},
async ({ ciudad }) => {
const res = await fetch(
`https://wttr.in/${encodeURIComponent(ciudad)}?format=j1`
);
const data = await res.json();
const temp = data.current_condition[0].temp_C;
return {
content: [{ type: "text", text: `${ciudad}: ${temp}°C` }],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
Importante: La descripción de la tool decide si la IA la llama. Escríbela desde la perspectiva del modelo: "Obtener datos meteorológicos actuales de una ciudad" es mejor que "Wrapper de API meteorológica".
¿Cómo conecto el server con Claude Desktop?
Tras el build (npx tsc), el server tiene que quedar registrado en la config de Claude Desktop. El archivo está en:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"mi-server": {
"command": "node",
"args": ["/ruta/a/mi-mcp-server/dist/index.js"]
}
}
}
Reinicia Claude Desktop. Aparece un icono de herramienta en el chat, tu tool ya está disponible. Pruébala con: "¿Qué tiempo hace en Berlín?".
Para Claude Code es aún más simple:
claude mcp add mi-server node /ruta/a/dist/index.js
¿Qué best practices aplican para MCP Servers en producción?
El paso del prototipo a producción requiere cinco medidas:
-
Error handling: Las tools MCP no pueden crashear. Captura cada error y devuelve un mensaje legible. La IA puede trabajar con "API no disponible", con un stack trace no.
-
Validación de input: Zod valida automáticamente, pero define schemas estrictos.
z.string().max(100)en vez dez.string(). Los modelos de IA a veces mandan inputs inesperados. -
Timeouts: Las llamadas a APIs externas necesitan timeouts.
AbortSignal.timeout(10_000)evita requests colgados. -
Logging: Usa
server.sendLoggingMessage()en lugar deconsole.log. Así los logs van al cliente MCP, no al transport stream. -
Rate limiting: Si tu server llama a APIs externas, implementa rate limiting en el lado del server. Los modelos de IA invocan tools de forma más agresiva que los usuarios humanos.
¿Cómo hago el deploy de un MCP Server en remoto?
Para un deployment remoto, cambia de stdio a Streamable HTTP transport. El SDK actual usa NodeStreamableHTTPServerTransport:
import { createServer } from "node:http";
import { randomUUID } from "node:crypto";
import { McpServer } from "@modelcontextprotocol/server";
import {
NodeStreamableHTTPServerTransport,
} from "@modelcontextprotocol/node";
const server = new McpServer({
name: "mi-server",
version: "1.0.0",
});
// Registrar tools aquí (como arriba)
const transports = new Map<string, NodeStreamableHTTPServerTransport>();
createServer(async (req, res) => {
if (req.url === "/mcp" && req.method === "POST") {
const sessionId = req.headers["mcp-session-id"] as string | undefined;
if (sessionId && transports.has(sessionId)) {
await transports.get(sessionId)!.handleRequest(req, res);
return;
}
// Nueva sesión
const transport = new NodeStreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
onsessioninitialized: (sid) => transports.set(sid, transport),
});
transport.onclose = () => {
if (transport.sessionId) transports.delete(transport.sessionId);
};
await server.connect(transport);
await transport.handleRequest(req, res);
}
}).listen(3100);
Los clientes se conectan por URL. En Claude Desktop:
{
"mcpServers": {
"mi-server": {
"url": "https://mi-server.example.com/mcp"
}
}
}
Seguridad: Los MCP Servers remotos necesitan autenticación. El protocolo soporta OAuth 2.1, o puedes usar API Keys en el header Bearer. Sin auth, tu server es públicamente accesible.
Conclusión
Construir tu propio MCP Server te da control total sobre qué tools puede usar tu IA. La barrera de entrada es baja (un paquete npm, un archivo), el potencial es alto. Empieza con una tool sencilla, pruébala en Claude Desktop y amplíala paso a paso.
Lectura adicional: ¿Qué es el protocolo MCP? | Arquitectura de MCP Server en detalle | Instalar MCP Tools: Tutorial
