Hasta hoy has sido inquilino: instalas MCPs que otros escribieron y tu IA los usa. Bien. Pero hay un momento en el que eso se queda corto — tienes una API interna, una base de datos, un script que solo tú entiendes, y quieres que Claude lo toque directamente, sin copiar-pegar salidas al chat. Ahí es donde dejas de consumir y empiezas a publicar. Un servidor MCP es el enchufe estándar: describes tus herramientas una vez, y cualquier IA que hable el protocolo las invoca como si fueran suyas. FastMCP es el framework de TypeScript que envuelve el SDK oficial de MCP (el que abrió Anthropic) y te quita el boilerplate de encima — el mismo salto que Express te dio sobre el módulo http de Node. En esta guía pasamos de un tool 'add' sobre stdio en quince minutos, a tools con esquemas Zod que se validan solos, resources que exponen datos, prompts reutilizables, y por último producción de verdad: autenticación, HTTP streaming, sesiones y edge. Al final tendrás un MCP propio que Claude Code invoca en serio — no una demo, una tubería tuya conectada a tu mundo.
Llevas semanas con la IA conectada a herramientas. Le pides que abra un navegador y lo hace. Le pides que lea un repo y lo lee. Todo eso son MCPs que otros escribieron — enchufes estándar que alguien publicó y tú instalaste. Funciona porque hablan un idioma común: el Model Context Protocol, el estándar abierto para que cualquier IA descubra e invoque herramientas externas sin acoplarse a ninguna.
El momento llega cuando lo que necesitas no existe como MCP. Tienes un endpoint interno que devuelve el estado de tus pedidos. Una hoja de cálculo que solo tú sabes leer. Un script de 40 líneas que calcula algo específico de tu dominio. Hoy, cada vez que quieres que la IA lo use, haces el baile del recadero: ejecutas tú, copias la salida, la pegas en el chat, esperas. Funciona una vez. A la décima, te preguntas por qué la IA no lo hace sola.
Este recurso cierra un arco. En agent-browser aprendiste a darle a tu IA una capacidad externa para consumir el mundo — ver páginas, hacer clic, extraer. Aquí giras la cámara: aprendes a publicar tus propias capacidades para que otras IAs las consuman. De usuario a autor. Es el mismo protocolo, visto desde el otro lado del cable.
Cuando abres el SDK oficial de MCP por primera vez, la sensación es la de abrir el capó de un coche de carreras: todo está ahí, todo es correcto, y nada es obvio. Tienes que registrar handlers a mano, mapear los request del protocolo, serializar respuestas en el formato exacto que espera el cliente, gestionar el ciclo de conexión, parsear los parámetros de entrada y validarlos por tu cuenta. Para exponer un solo tool que suma dos números, escribes decenas de líneas de plomería antes de llegar a la línea que hace la suma.
¿De dónde sale ese dolor? De que el SDK oficial es de bajo nivel a propósito. Es la base sobre la que se construye, no la ergonomía final — igual que el módulo http de Node es correcto pero nadie escribe un servidor web con él directamente, usa Express. El SDK te da acceso total al protocolo; el precio es que tú cargas con todo el boilerplate. Está bien para quien construye frameworks. Es hostil para quien solo quiere enchufar su API.
FastMCP existe para borrar exactamente ese boilerplate. Es un framework de TypeScript que envuelve el SDK oficial de MCP — no lo reemplaza, se para encima de él — y te da una API declarativa: describes el tool, su esquema y su función, y FastMCP se encarga del resto (registro, serialización, ciclo de conexión, validación). El tool de sumar pasa de esa maraña de plomería a poco más de una docena de líneas. Y son las que importan: las que de verdad hacen la suma.
Framework de TypeScript para construir servidores MCP sin boilerplate. Se para sobre el SDK oficial de MCP (@modelcontextprotocol/sdk, el que abrió Anthropic) y añade tools con validación por esquema (Zod y otros Standard Schema), resources, prompts, autenticación, HTTP streaming, sesiones y un CLI de desarrollo. La forma más rápida de pasar de idea a servidor MCP que Claude invoca de verdad.
Publicar un servidor MCP no es algo que hagas una vez y olvides. Es un reflejo que se activa en momentos concretos. Aprende a reconocerlos y sabrás siempre cuándo vale la pena el rato de construirlo.
FastMCP se instala como cualquier paquete de npm. Corre sobre Node moderno con TypeScript. Un proyecto nuevo mínimo cabe en una carpeta con tres archivos.
# En una carpeta nueva para tu servidor mkdir mi-mcp && cd mi-mcp npm init -y # FastMCP + Zod (para los esquemas de entrada) + tsx (para correr TS sin compilar) npm install fastmcp zod npm install -D tsx typescript
a es un número, email es un string con formato de correo, edad es un entero opcional. FastMCP toma ese esquema y hace dos cosas gratis: (1) le dice al cliente qué parámetros espera tu tool, para que la IA los rellene bien; y (2) valida la entrada antes de que llegue a tu código. Si la IA manda basura, la rechaza el esquema, no tu función. Es un guardia en la puerta que no tienes que escribir. (FastMCP también acepta otros validadores del mismo estándar — ArkType, Valibot — pero Zod es el camino más trillado.)Empezamos por el 'hola mundo' del MCP: un servidor con un solo tool que suma dos números. Es deliberadamente tonto — la gracia no es la suma, es ver el cable completo: definir el servidor, registrar un tool con su esquema, arrancarlo, y que Claude lo invoque. Una vez tienes este esqueleto, todo lo demás son variaciones.
// server.ts
import { FastMCP } from "fastmcp";
import { z } from "zod";
const server = new FastMCP({
name: "Mi Servidor",
version: "1.0.0",
});
server.addTool({
name: "add",
description: "Suma dos números",
parameters: z.object({
a: z.number(),
b: z.number(),
}),
execute: async (args) => {
return String(args.a + args.b);
},
});
server.start({
transportType: "stdio",
});Léelo de arriba abajo, porque este patrón se repite en todo lo que construyas. new FastMCP crea el servidor con nombre y versión — eso es lo que Claude verá en la lista de conexiones. addTool registra una herramienta: un nombre (con el que la IA la invoca), una descripción (que la IA lee para decidir cuándo usarla — escríbela bien, es marketing dirigido a la máquina), los parameters como esquema Zod, y execute, la función que corre de verdad. Devuelves un string y FastMCP lo envuelve en el formato del protocolo por ti.
transportType: "stdio" significa que el servidor habla por la entrada y salida estándar — el mismo canal por el que un programa de terminal recibe y emite texto. Es el transporte más simple: el cliente (Claude Code) lanza tu servidor como un subproceso y conversa con él por ese tubo. Cero red, cero puertos, cero configuración. Perfecto para herramientas locales tuyas. Cuando quieras exponerlo por internet, cambiarás este transporte por HTTP — pero para empezar, stdio es todo lo que necesitas.Antes de enchufarlo a Claude, pruébalo aislado. FastMCP trae un CLI que arranca tu servidor y te deja hablar con él, más el Inspector oficial de MCP para verlo en una interfaz visual. Es tu bucle de desarrollo: cambias el código, lo pruebas aquí, y solo cuando funciona lo conectas al cliente real.
# Arranca tu servidor en modo desarrollo (interactúas con los tools) npx fastmcp dev server.ts # Ábrelo en el MCP Inspector (interfaz visual para inspeccionar tools/resources/prompts) npx fastmcp inspect server.ts
npx fastmcp dev como tu banco de trabajo: ahí ves el error crudo, la salida exacta, el esquema tal cual lo publica tu servidor. Solo cuando el tool hace lo que quieres, das el paso de conectarlo al cliente. Igual que no despliegas a producción para probar un if — pruebas local primero.Aquí es donde deja de ser un ejercicio y se vuelve real. Un cliente MCP (Claude Desktop, Claude Code) lanza tu servidor como subproceso y le habla por stdio. Solo necesita saber cómo arrancarlo: qué comando y qué argumentos. Eso vive en un archivo de configuración con la lista de servidores MCP.
{
"mcpServers": {
"mi-servidor": {
"command": "npx",
"args": ["tsx", "/ruta/absoluta/a/mi-mcp/server.ts"]
}
}
}command es el ejecutable que lanza tu servidor y args sus argumentos — aquí usamos npx tsx para correr el TypeScript directamente sin compilar. Usa ruta absoluta: el cliente no sabe desde qué carpeta lo lanzas. Una vez guardado y reiniciado el cliente, tu servidor aparece en la lista, y cuando le pidas a Claude "suma 128 y 45", verás cómo invoca tu tool add en lugar de calcularlo de cabeza. Ese momento — el primer tool tuyo que la IA llama sola — es el que engancha.
./server.ts) en args. El cliente lanza el subproceso desde su directorio de trabajo, no el tuyo, así que ./server.ts apunta a la nada y el servidor no arranca — normalmente en silencio. Si tu MCP no aparece o falla al conectar, revisa esto primero: ruta absoluta, siempre.El tool add usaba Zod para lo mínimo. Pero el verdadero poder aparece con tools reales, donde la entrada tiene forma y reglas. Imagina un tool que crea un usuario: el email debe tener formato de correo, la edad debe ser un entero positivo, el rol solo puede ser uno de una lista. Con Zod, describes todo eso y FastMCP rechaza la entrada inválida antes de que toque tu código. Nunca escribes un if (!email.includes('@')). El esquema es el guardia.
server.addTool({
name: "crear_usuario",
description: "Crea un usuario en el sistema con validación completa",
parameters: z.object({
nombre: z.string().min(2),
email: z.string().email(),
edad: z.number().int().positive().optional(),
rol: z.enum(["admin", "editor", "lector"]),
}),
execute: async (args) => {
// Si llegaste aquí, args YA está validado: email es email, rol es válido.
const usuario = await miBaseDeDatos.insertar(args);
return `Usuario ${usuario.id} creado con rol ${args.rol}`;
},
});execute, args viene tipado en TypeScript, con autocompletado. Un solo bloque de código que valida, documenta y tipa. Esto es lo que el SDK oficial te obligaba a escribir tres veces a mano.Un tool es un verbo — la IA lo ejecuta para que algo pase. Pero a veces no quieres una acción, quieres dar contexto: un archivo de logs, un documento de configuración, el contenido de un README. Para eso existen los resources. Son datos que tu servidor expone y que la IA puede leer cuando los necesita, identificados por una URI. Piensa en tools como los botones de un mando, y en resources como las pantallas que muestran información.
server.addResource({
uri: "file:///logs/app.log",
name: "Logs de la Aplicación",
mimeType: "text/plain",
async load() {
const contenido = await fs.readFile("/var/log/app.log", "utf-8");
return { text: contenido };
},
});La uri es el identificador único del resource — el cliente lo usa para pedirlo. mimeType le dice a la IA de qué tipo es el contenido (texto plano, JSON, markdown), para que lo interprete bien. Y load es la función que trae el contenido cuando alguien lo pide — perezosa por diseño: no lees el archivo hasta que hace falta. Para datos binarios devuelves { blob } en base64 en lugar de { text }. Un resource es la forma limpia de decir "IA, aquí tienes datos frescos cuando los necesites", sin gastarlos en el prompt hasta el momento exacto.
El tercer ingrediente del protocolo son los prompts. Si tienes una plantilla que usas todo el rato — 'genera un mensaje de commit a partir de este diff', 'resume esta reunión con este formato' — un prompt del MCP la empaqueta con un nombre y unos argumentos, y cualquier cliente puede invocarla. Dejas de copiar-pegar la misma instrucción larga: la ofreces como una capacidad más de tu servidor.
server.addPrompt({
name: "git-commit",
description: "Genera un mensaje de commit a partir de un diff",
arguments: [
{
name: "changes",
description: "El diff de git o una descripción de los cambios",
required: true,
},
],
load: async (args) => {
return `Genera un mensaje de commit conciso para estos cambios:\n${args.changes}`;
},
});Todo lo anterior corría por stdio: local, tuyo, un subproceso en tu máquina. Perfecto para empezar. Pero cuando quieres que otros usen tu MCP — tu equipo, un cliente, tú mismo desde varios sitios — necesitas exponerlo por internet. Ahí entra el transporte HTTP streaming: en lugar de un tubo local, tu servidor escucha en un puerto y responde por HTTP con streaming de eventos. El código de tus tools no cambia ni una línea; solo cambias cómo arranca.
server.start({
transportType: "httpStream",
httpStream: {
port: 8080,
endpoint: "/mcp", // opcional, por defecto es /mcp
},
});Ese es el interruptor completo. Mismos tools, mismos resources, mismos prompts — ahora servidos por HTTP en el puerto 8080, endpoint /mcp. Un cliente remoto apunta a https://tu-dominio/mcp y enchufa. Aquí es donde tu MCP deja de ser una herramienta personal y se vuelve un servicio. Y donde aparece la pregunta que separa un juguete de un producto: ¿quién puede llamarlo?
Un MCP por HTTP sin autenticación es una puerta abierta: cualquiera que sepa la URL invoca tus tools, toca tu base de datos, gasta tus recursos. FastMCP resuelve esto con una opción authenticate: una función que corre en cada conexión, inspecciona la petición (headers, API key, token), y decide si pasa o no. Si pasa, devuelve un objeto de sesión que estará disponible dentro de cada tool.
const server = new FastMCP({
name: "Mi Servidor",
version: "1.0.0",
authenticate: (request) => {
const apiKey = request.headers["x-api-key"];
if (apiKey !== process.env.MCP_API_KEY) {
throw new Response(null, { status: 401, statusText: "Unauthorized" });
}
return { id: 1, role: "user" }; // esto se vuelve la sesión
},
});
server.addTool({
name: "sayHello",
execute: async (args, { session }) => {
return `Hola, usuario ${session.id}!`; // session viene de authenticate
},
});authenticate. No son rivales: un MCP de producción es tubería con candado. Sin candado, tu tubería es un grifo público que cualquiera abre.process.env.MCP_API_KEY. La clave jamás se escribe literal en el código ni se sube a git — es la misma regla de oro del recurso de secretos: fuera del repo, en una variable de entorno o un gestor de secretos. Un MCP con la key hardcodeada es un MCP con la puerta pintada de 'cerrado' pero sin cerradura de verdad. Cualquiera que vea el código, entra.Los tools reales no son instantáneos. Un tool que procesa un archivo grande, llama a una API lenta o genera contenido tarda segundos. FastMCP te da, dentro de execute, un contexto con herramientas para que ese tool respire: reportar progreso, emitir contenido en streaming, loguear, y acceder a la sesión del usuario autenticado.
server.addTool({
name: "procesar_lote",
description: "Procesa un lote reportando progreso en vivo",
parameters: z.object({ total: z.number() }),
annotations: { streamingHint: true },
execute: async (args, { streamContent, reportProgress, log, session }) => {
log.info(`Iniciando lote para ${session.id}`);
await streamContent({ type: "text", text: "Empezando..." });
await reportProgress({ progress: 50, total: 100 });
// ... trabajo real ...
return "Completado";
},
});reportProgress({ progress, total }) le dice al cliente 'voy por el 50%', y la IA (y el humano) ven una barra en vez de un silencio angustioso. streamContent emite salida a trozos según se genera, en lugar de esperar al final — la diferencia entre ver el texto aparecer y mirar un spinner. log deja rastro estructurado para depurar. Y session es el usuario que authenticate validó, así cada tool sabe quién lo está llamando. Con estos, tus tools dejan de ser cajas negras que tardan y se vuelven procesos observables.
Aquí está el único prompt que necesitas guardar. En vez de escribir el servidor a mano, se lo describes a tu IA — que ya tiene todo el contexto de FastMCP de esta guía delante — y ella genera el esqueleto completo, probado, listo para conectar. Rellena los corchetes con tu caso real y déjala construir. Un solo prompt, no cinco: este arranca el proyecto entero.
Actúa como ingeniero senior de MCP. Vamos a construir un servidor MCP en TypeScript con FastMCP (el framework que envuelve el SDK oficial de MCP, el que abrió Anthropic; se instala con `npm install fastmcp zod`).
MI SERVIDOR:
- Nombre: [nombre de tu servidor, ej. "CRM Interno"]
- Qué expone: [describe en 2 líneas qué herramientas/datos quieres que la IA toque]
LOS TOOLS QUE NECESITO (acciones que la IA ejecutará):
1. [nombre_tool] — [qué hace] — entradas: [campos y sus tipos/reglas]
2. [nombre_tool] — [qué hace] — entradas: [campos y sus tipos/reglas]
RESOURCES (datos que la IA leerá, si aplica):
- [nombre] — [qué datos expone, ej. logs, config]
MODO: [empieza en "stdio local" para desarrollo | luego migro a "httpStream con autenticación por API key" para producción]
CONSTRUYE:
1. server.ts completo: `new FastMCP` con nombre y versión, cada tool con `addTool` (name, description clara orientada a que la IA sepa cuándo usarlo, parameters como esquema Zod que VALIDE de verdad — .email(), .int().positive(), .enum() donde toque, y .optional() en lo opcional, execute tipado).
2. Los resources con `addResource` (uri, name, mimeType, load perezoso que devuelva {text} o {blob}) si los pedí.
3. El `server.start` con el transporte que elegí (stdio o httpStream con port/endpoint).
4. Si pedí producción: la opción `authenticate` que lee la API key de `process.env` (NUNCA hardcodeada), lanza `new Response(null, {status: 401})` si no cuadra y devuelve la sesión, más un tool que use `session`.
5. El bloque JSON de `mcpServers` para conectarlo a Claude Code, con `npx tsx` y ruta ABSOLUTA.
6. Los comandos exactos: instalar, probar con `npx fastmcp dev server.ts`, e inspeccionar con `npx fastmcp inspect server.ts`.
RESTRICCIONES: cero secretos en el código; descripciones de tools escritas PARA que la IA decida bien cuándo invocarlas; valida toda entrada con Zod, no con ifs a mano. Explícame cada tool en una línea antes del código.npx fastmcp dev. Cuando eso funcione y Claude lo invoque de verdad, vuelve al mismo prompt cambiando el MODO a producción con autenticación. Así construyes en capas verificables en lugar de escupir un servidor gigante sin probar. Y aplica el hábito de siempre: primero un commit del esqueleto que funciona, luego lo demás.Publicar un MCP tiene dos caminos según dónde estés parado, y conviene saber cuándo tomar cada uno.
server.ts completo con Zod, transporte y config.mcpServers con la ruta correcta y los comandos de fastmcp dev para probar antes de enchufar.npx fastmcp inspect server.ts abre el Inspector visual — ahí ves tus tools, resources y prompts como los ve un cliente.Retrocede y mira el camino. Empezaste consumiendo MCPs que otros escribieron — tu IA usaba herramientas ajenas. Aprendiste con agent-browser a darle una capacidad para ver y tocar el mundo. Y aquí, con FastMCP, giraste la cámara: ahora publicas tus propias capacidades para que cualquier IA las consuma. Un tool add en quince minutos se convirtió en tools con Zod, resources, prompts, autenticación y HTTP. Cerraste el arco usuario→autor. Ya no eres inquilino del ecosistema MCP: eres propietario de un enchufe.
Únete a 4.200+ creadores. Sin tarjeta. Construye tu primera app con IA en minutos.