Complete "Fundamentos y arquitectura de MCP" primero para desbloquear esta lección.Ir a la lección anterior

Objetivo y arquitectura

En esta lección crearás un servidor MCP local que expone una capacidad estructurada: calcular una cotización de envío a partir del peso del paquete, la zona de destino y el nivel de servicio. La capacidad se implementa como una herramienta MCP porque un cliente la invoca activamente con argumentos y recibe un resultado. Al finalizar, un cliente compatible con MCP podrá descubrir la herramienta, inspeccionar su esquema de entrada, validar una llamada y recibir tanto texto legible para personas como datos estructurados.

El servidor utilizará TypeScript, Node.js 20 o posterior y el paquete oficial de servidor MCP para TypeScript. Se comunicará mediante el transporte stdio. En este modelo, un cliente MCP inicia el servidor como un proceso secundario e intercambia mensajes JSON-RPC mediante la entrada y la salida estándar. Este transporte es adecuado para aplicaciones de IA locales, hosts de línea de comandos y herramientas de desarrollo. Un servidor compartido desplegado de forma remota normalmente utilizaría Streamable HTTP.

El flujo de solicitudes es sencillo. El cliente inicia el proceso, completa el handshake de inicialización de MCP, enumera las herramientas disponibles y llama a la herramienta seleccionada. El SDK valida los argumentos proporcionados antes de ejecutar tu handler. Tu handler realiza el cálculo de negocio y devuelve un resultado MCP. Esta separación es importante: MCP define el descubrimiento y la comunicación, mientras que el código de tu aplicación se encarga de las reglas de envío.

diagrama de flujo que muestra una aplicación de IA que contiene un cliente MCP, una conexión JSON-RPC mediante stdio, un servidor MCP, la validación del esquema de entrada, el handler de la cotización de envío y un resultado estructurado que vuelve a la aplicación

Configuración del proyecto

Crea un proyecto nuevo e instala el SDK del servidor, Zod, TypeScript y el ejecutor tsx. El paquete se configura como un módulo ES porque el SDK actual utiliza importaciones de módulos ES. El ejecutor tsx ejecuta directamente el punto de entrada de TypeScript, lo que permite centrarse en MCP en este primer ejercicio en lugar de en las herramientas de compilación.

bash
mkdir shipping-mcp-server
cd shipping-mcp-server
npm init -y
npm pkg set type=module
npm install @modelcontextprotocol/server zod
npm install --save-dev typescript tsx @types/node
mkdir src

Añade scripts a package.json para que el comando de inicio sea estable. El script del servidor ejecuta el proceso stdio, mientras que el script inspect inicia MCP Inspector y le indica cómo iniciar ese mismo proceso.

json
{
"name": "shipping-mcp-server",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "tsx src/index.ts",
"inspect": "npx @modelcontextprotocol/inspector npx tsx src/index.ts"
},
"dependencies": {
"@modelcontextprotocol/server": "^2.0.0",
"zod": "^4.0.0"
},
"devDependencies": {
"@types/node": "^24.0.0",
"tsx": "^4.0.0",
"typescript": "^6.0.0"
}
}

Las versiones exactas de las dependencias resueltas pueden ser más recientes cuando las instales. El detalle arquitectónico importante es que los proyectos de servidor nuevos utilizan el paquete de servidor específico en lugar del paquete SDK monolítico anterior. Si la instalación del paquete falla, confirma que Node.js y npm estén disponibles y que tu versión de Node.js sea como mínimo la 20.

Implementación del servidor

Crea src/index.ts con la siguiente implementación. La función createServer construye un servidor MCP nuevo y registra la herramienta calculate-shipping. El esquema de entrada acepta un peso de paquete positivo, una de las tres zonas de destino y uno de los dos niveles de servicio. El esquema de salida describe el resultado estructurado que los clientes pueden consumir sin analizar texto.

typescript
import { McpServer } from '@modelcontextprotocol/server';
import { serveStdio } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';
const inputSchema = z.object({
weightKg: z.number().positive().max(50),
zone: z.enum(['domestic', 'regional', 'international']),
service: z.enum(['standard', 'express'])
});
const outputSchema = z.object({
currency: z.literal('USD'),
baseCost: z.number(),
serviceSurcharge: z.number(),
totalCost: z.number(),
estimatedDays: z.number().int()
});
function createServer(): McpServer {
const server = new McpServer({
name: 'shipping-quote-server',
version: '1.0.0'
});
server.registerTool(
'calculate-shipping',
{
title: 'Calculate Shipping Quote',
description: 'Calculate a shipping price and delivery estimate for a package',
inputSchema,
outputSchema
},
async ({ weightKg, zone, service }) => {
const ratePerKg = {
domestic: 2.5,
regional: 5,
international: 9
}[zone];
const standardDays = {
domestic: 4,
regional: 7,
international: 14
}[zone];
const baseCost = Number((5 + weightKg * ratePerKg).toFixed(2));
const serviceSurcharge = service === 'express'
? Number((baseCost * 0.6).toFixed(2))
: 0;
const totalCost = Number((baseCost + serviceSurcharge).toFixed(2));
const estimatedDays = service === 'express'
? Math.max(1, Math.ceil(standardDays / 2))
: standardDays;
const result = {
currency: 'USD' as const,
baseCost,
serviceSurcharge,
totalCost,
estimatedDays
};
return {
content: [
{
type: 'text',
text: `Shipping costs USD ${totalCost.toFixed(2)} and takes approximately ${estimatedDays} days.`
}
],
structuredContent: result
};
}
);
return server;
}
void serveStdio(createServer);
console.error('Shipping MCP server is running on stdio');

La descripción de la herramienta debe indicar qué capacidad ofrece, porque un agente de IA utiliza el nombre, la descripción y el JSON Schema generado para decidir si debe llamarla. El esquema también constituye un límite ejecutable. Un peso negativo, una zona no compatible o un servicio desconocido se rechazan antes de que se ejecute el handler de cálculo.

Comprender el resultado

El handler devuelve dos representaciones del mismo resultado. El array content contiene un bloque de texto que un modelo o una interfaz de usuario puede leer directamente. El valor structuredContent contiene campos predecibles para las aplicaciones que necesitan mostrar un precio, comparar opciones, guardar el resultado o pasarlo a otra herramienta. Como se ha registrado un esquema de salida, el valor estructurado debe coincidir con dicho esquema.

Para una entrada que contenga un peso de 3 kilogramos, la zona regional y el servicio express, el coste base es de 20 dólares. El recargo por express es de 12 dólares, lo que produce un total de 32 dólares y un tiempo estimado de entrega de cuatro días. Un resultado correcto tiene esta estructura lógica.

json
{
"content": [
{
"type": "text",
"text": "Shipping costs USD 32.00 and takes approximately 4 days."
}
],
"structuredContent": {
"currency": "USD",
"baseCost": 20,
"serviceSurcharge": 12,
"totalCost": 32,
"estimatedDays": 4
}
}

La salida estructurada es preferible a incluir toda la información en una sola frase. Un cliente no debería necesitar expresiones regulares para recuperar el precio total o la estimación de entrega. El texto sigue siendo útil para el contexto del modelo, mientras que el objeto estructurado proporciona una interfaz de aplicación estable.

Ejecución y pruebas

Inicia el servidor directamente para verificar que el proceso se carga sin errores de sintaxis o de importación. Debe mostrar su mensaje de estado y continuar esperando, porque un servidor stdio necesita que un cliente MCP envíe solicitudes. Deténlo con el comando de interrupción del terminal después de confirmar que permanece activo.

bash
npm start

A continuación, inicia el MCP Inspector. El Inspector inicia tu servidor, realiza el handshake de conexión y proporciona una interfaz de navegador para listar y llamar a las herramientas. Conéctate al servidor, abre la vista Tools, selecciona calculate-shipping y envía valores válidos como 3, regional y express.

bash
npm run inspect

Verifica tres comportamientos en lugar de comprobar únicamente el caso correcto. Primero, confirma que la herramienta aparece con la descripción y los campos de entrada esperados. Segundo, llámala con datos válidos y compara el resultado estructurado con las reglas de cálculo. Tercero, prueba con un peso no válido, como menos 2, o con una zona no compatible. La solicitud no válida debe fallar durante la validación del esquema en lugar de llegar al handler.

secuencia de pruebas que muestra el descubrimiento de la herramienta, una llamada válida a calculate-shipping con weightKg 3, zone regional y service express, el resultado estructurado de 32 USD y una llamada con peso negativo detenida por la validación del esquema

Fallos comunes y próximos pasos

El error más destructivo en stdio consiste en escribir los registros de la aplicación en la salida estándar. La salida estándar está reservada para los mensajes del protocolo, por lo que una línea de registro normal puede corromper el flujo JSON-RPC y desconectar al cliente. Usa la salida de error estándar para los diagnósticos, tal como muestra el mensaje de estado. Esta restricción se aplica tanto a tu propio código como a las bibliotecas que puedan imprimir contenido inesperado.

Si Inspector no puede iniciar el proceso, ejecuta directamente el comando de inicio desde la raíz del proyecto. Los errores de importación suelen indicar nombres de paquetes incorrectos, una versión antigua de Node.js o una configuración de módulos ES ausente. Si la herramienta es visible, pero las llamadas fallan, revisa los nombres y tipos de los argumentos enviados. El esquema espera un peso numérico y valores exactos del enum; una cadena numérica no equivale automáticamente a un número.

Que un servidor se inicie correctamente no lo convierte automáticamente en un servidor MCP útil. Su herramienta necesita una descripción precisa, entradas restringidas, un comportamiento determinista y resultados fáciles de consumir tanto para los modelos como para las aplicaciones. Ahora tienes el flujo local completo: crear un servidor, registrar una herramienta estructurada, servirla mediante stdio y probarla con un cliente MCP real. El siguiente paso práctico es conectar el mismo comando de inicio a un host de IA o reemplazar el cálculo local por una base de datos o un servicio externo, manteniendo el contrato de la herramienta.

Práctica de la lección

Punto de control de la lección

Repasa lo aprendido y recibe comentarios sobre tu trabajo.

0/1
Respondidas · 0/1