Estructura mínima de un servidor MCP: SDK, stdio y arranque limpio
Monta el esqueleto correcto de un servidor MCP en TypeScript: proyecto mínimo, SDK oficial, transporte `stdio`, registro de primitivas y reglas básicas para no romper la comunicación.
Después de entender qué expone un servidor MCP, toca construir uno sin ruido innecesario. La meta de esta lección no es hacer un servidor complejo, sino dejar una base mantenible y fácil de ampliar.
El error habitual al empezar es intentar resolver todo a la vez: transporte, lógica, validación, recursos, despliegue y cliente. Eso vuelve opaca la arquitectura antes de que exista criterio.
Un arranque limpio de servidor MCP en TypeScript necesita muy pocas piezas: un proyecto con `type: module`, el SDK oficial, una instancia de `McpServer`, un transporte y al menos una primitiva registrada.
En local, `stdio` sigue siendo la mejor puerta de entrada porque reduce la complejidad de red y te obliga a entender una verdad importante del protocolo: si contaminas `stdout`, rompes JSON-RPC.
- Empieza con una estructura pequeña y explícita.
- La documentación oficial de MCP propone una base TypeScript muy directa: `@modelcontextprotocol/sdk`, `zod`, `typescript` y `@types/node`. No hace falta añadir framework ni servidor HTTP si tu objetivo inicial es local.
- En esta fase, el objetivo no es impresionar con la pila, sino conseguir un binario o script que el host pueda arrancar sin ambigüedad.
- Una buena señal es que puedes explicar cada archivo del proyecto en una frase: `package.json`, `tsconfig.json` y un `src/index.ts` con la lógica principal.
- Tu configuración debe dejar claro cómo se construye y cómo se ejecuta.
Base del proyecto
Empieza con una estructura pequeña y explícita.
La documentación oficial de MCP propone una base TypeScript muy directa: `@modelcontextprotocol/sdk`, `zod`, `typescript` y `@types/node`. No hace falta añadir framework ni servidor HTTP si tu objetivo inicial es local.
En esta fase, el objetivo no es impresionar con la pila, sino conseguir un binario o script que el host pueda arrancar sin ambigüedad.
Una buena señal es que puedes explicar cada archivo del proyecto en una frase: `package.json`, `tsconfig.json` y un `src/index.ts` con la lógica principal.
Configuración mínima
Tu configuración debe dejar claro cómo se construye y cómo se ejecuta.
En `package.json`, conviene declarar `type: module` y un script de build. En `tsconfig.json`, la guía oficial usa `target: ES2022`, `module: Node16` y `moduleResolution: Node16` para que la resolución moderna de imports no te juegue una mala pasada.
No es una elección arbitraria. Estas decisiones reducen errores de ejecución cuando pasas del código fuente a un servidor que el host va a lanzar como proceso.
Instancia y transporte
La forma más clara de arrancar es `McpServer` más `StdioServerTransport`.
Con el SDK oficial, lo habitual es importar `McpServer` desde `@modelcontextprotocol/sdk/server/mcp.js` y `StdioServerTransport` desde `@modelcontextprotocol/sdk/server/stdio.js`.
Esa combinación deja un servidor local muy fácil de razonar: el host lo arranca como subproceso, se comunican por `stdin` y `stdout`, y tú te concentras en registrar primitivas.
La arquitectura mínima es esta: crear servidor, registrar al menos una capacidad y conectar el transporte.
Logs y arranque
En `stdio`, registrar mal es romper el protocolo.
La documentación oficial advierte algo crítico: no uses `console.log()` en servidores MCP por `stdio`, porque escribe en `stdout` y puede corromper los mensajes JSON-RPC.
Si necesitas logs en local, usa `stderr` o `console.error`. Esa diferencia parece pequeña hasta que el host deja de entender tu servidor y el fallo parece misterioso.
También conviene mantener el arranque libre de mensajes extra: sin banners, sin prints y sin salidas cosméticas.
- Correcto: logs a `stderr`.
- Incorrecto: mensajes de depuración por `stdout`.
- Correcto: un único punto de entrada claro.
- Incorrecto: mezclar setup, tests y arranque en el mismo archivo.
Caso guiado
Imagina que quieres crear un servidor MCP para un equipo interno. Antes de pensar en APIs complejas, lo más útil es conseguir un servidor mínimo que arranque, aparezca en un cliente compatible y responda a una tool simple.
Ese primer éxito no es trivial. Valida la cadena completa: proyecto, build, proceso, transporte y contrato básico de respuesta. Si esa cadena falla, cualquier recurso o schema avanzado que añadas encima será mucho más difícil de depurar.
Por eso, en una fase temprana, un servidor pequeño pero estable vale más que una demo ambiciosa e inestable.
Práctica
Crea un servidor mínimo con nombre propio, una tool de prueba y arranque por `stdio`. La evidencia observable de logro es simple: el servidor construye sin errores y su tool devuelve una respuesta textual coherente.
La validación correcta no es solo que compile. Debes poder explicar por qué usaste `stdio`, dónde harías logging y qué parte de la estructura ampliarías después.