Esqueleto TypeScript, un servidor MCP en 40 líneas
Código mínimo. No es la mejor arquitectura, pero es el punto de entrada más claro.
Qué instalas
mkdir mi-mcp-server && cd mi-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript tsx @types/node
@modelcontextprotocol/sdkes la librería oficial de Anthropic.zodes para validación de inputs (obligatorio, no opcional).tsxte deja ejecutar TypeScript directamente sin build step.
El server mínimo
Archivo src/index.ts:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
const server = new Server(
{ name: "mi-mcp-server", version: "0.1.0" },
{ capabilities: { tools: {} } }
);
const EchoSchema = z.object({
message: z.string(),
});
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "echo",
description: "Devuelve el mensaje pasado.",
inputSchema: {
type: "object",
properties: { message: { type: "string" } },
required: ["message"],
},
},
],
}));
server.setRequestHandler(CallToolRequestSchema, async (req) => {
if (req.params.name === "echo") {
const { message } = EchoSchema.parse(req.params.arguments);
return { content: [{ type: "text", text: `Echo: ${message}` }] };
}
throw new Error(`Unknown tool: ${req.params.name}`);
});
const transport = new StdioServerTransport();
await server.connect(transport);
Eso es. 30 líneas de código, un MCP server totalmente funcional.
Qué hace el código
- Importa el SDK.
- Crea un server con nombre + versión.
- Le dice al cliente: "Tengo una tool llamada
echo." - Cuando el cliente llama
echo, valida el input con Zod y devuelve un eco. - Se conecta vía stdin/stdout con el cliente.
Es la base. Todo MCP server sigue este patrón, por complejo que se vuelva.
Probarlo
En package.json añade este script:
"scripts": { "dev": "tsx src/index.ts" }
Luego configura Claude Code:
claude mcp add mi-server -s user -- npx -y tsx /ruta/al/proyecto/src/index.ts
Reinicia Claude Code, teclea /mcp, deberías ver "mi-server". Teclea:
Usa la tool echo de mi-server con el mensaje "Hola"
Claude llama echo, devuelve "Echo: Hola".
Las dos trampas para principiantes
1. console.log como debug. No funciona en servers stdio. console.log va a stdout, el cliente lo lee como mensaje de protocolo, el server crashea. En su lugar: usa console.error(...), va a stderr y no molesta.
2. Falta de input validation. Si el cliente envía algo mal y no validas, el server crashea. El agente lo ve como "Tool failed", hace cosas raras. Siempre Zod schemas. Siempre.
Qué falta ahora
Esto es el esqueleto. Para un server real necesitas:
- Más tools (en lugar de
echo, algo útil) - Llamadas externas a APIs (con error handling, retry, rate limit)
- Auth si hace falta (API keys, OAuth)
- Tests
- Logging
- Deployment (npm publish o Docker)
Eso viene en las siguientes lecciones. El esqueleto es el cuerpo, no el sprint.
Siguiente lección
Tool design. Cómo escribir tools buenas que el modelo use bien. Es la diferencia entre "mi server existe" y "mi server se usa".