MCP desde cero: cómo conectar agentes de IA con tus sistemas
Una guía práctica para entender qué es Model Context Protocol, cuándo conviene usarlo y cómo construir tu primer servidor MCP con TypeScript.
Los modelos de lenguaje saben analizar texto, razonar y generar respuestas, pero por sí solos no pueden consultar la base de datos de tu empresa, revisar un ticket o ejecutar una operación en tu sistema. Para lograrlo necesitan una forma segura y estructurada de comunicarse con el mundo exterior.
Ahí entra MCP, o Model Context Protocol.
MCP es un estándar abierto para conectar aplicaciones de inteligencia artificial con datos, herramientas y flujos de trabajo externos. Una analogía útil es pensar en MCP como un puerto USB-C para agentes de IA: en lugar de crear una integración diferente para cada asistente, expones una interfaz estándar que distintos clientes compatibles pueden entender.
En este artículo construiremos un pequeño servidor MCP para gestionar tickets de soporte. Al terminar podrás:
- explicar qué problema resuelve MCP;
- distinguir entre host, cliente y servidor;
- decidir cuándo usar MCP y cuándo no;
- entender herramientas, recursos y prompts;
- ejecutar un servidor local;
- probarlo con MCP Inspector;
- conectarlo a Codex;
- reconocer los cambios necesarios antes de llevarlo a producción.
El problema que MCP intenta resolver
Imagina que una empresa tiene:
- tickets en una plataforma interna;
- clientes en un CRM;
- facturas en QuickBooks;
- documentación en SharePoint;
- despliegues en Azure DevOps.
Quieres que un agente pueda responder solicitudes como:
Revisa el ticket 101, dime su prioridad y márcalo como resuelto si ya tiene solución.
Sin MCP tendrías que crear una integración personalizada entre cada agente y cada sistema. También tendrías que resolver por tu cuenta cómo describir las operaciones disponibles, validar sus argumentos, devolver resultados y manejar errores.
Con MCP construyes un servidor que publica capacidades con un contrato común. Un cliente compatible puede descubrirlas y utilizarlas sin conocer la implementación interna.
flowchart LR
U["Usuario"] --> H["Host de IA"]
H --> C["Cliente MCP"]
C --> S["Servidor MCP"]
S --> D["API, base de datos o archivos"]
MCP no sustituye la lógica de negocio ni la seguridad de tu aplicación. Es la capa estandarizada que permite que el agente encuentre y use las capacidades que tú decidas publicar.
Las piezas principales
Host
Es la aplicación donde interactúa el usuario con el modelo. Codex, ChatGPT, un IDE o una aplicación propia pueden actuar como host.
El host administra la conversación, presenta las solicitudes de autorización y decide qué servidores puede utilizar.
Cliente MCP
Es el componente del host que mantiene la conexión con un servidor MCP. Se encarga de negociar capacidades, descubrir herramientas y transportar solicitudes y respuestas.
Normalmente no tienes que construirlo si utilizas un host que ya soporta MCP.
Servidor MCP
Es el programa que expones. Puede envolver una API REST, una base de datos, archivos locales o cualquier servicio de negocio.
Un servidor publica principalmente tres tipos de capacidades:
| Capacidad | Qué representa | Ejemplo |
|---|---|---|
| Tool | Una función que el modelo puede solicitar ejecutar | Cambiar el estado de un ticket |
| Resource | Información que el cliente puede leer como contexto | Manual de soporte o ficha de un cliente |
| Prompt | Una plantilla reutilizable para guiar una tarea | Analizar un ticket siguiendo un proceso |
Una forma fácil de recordarlo es:
- Tools hacen cosas.
- Resources proporcionan información.
- Prompts enseñan cómo abordar una tarea.
¿Cómo ocurre una llamada?
Supón que el usuario escribe: «Busca el ticket 101».
- El cliente pregunta al servidor cuáles herramientas ofrece.
- El servidor describe
get_tickety el esquema de sus parámetros. - El modelo determina que esa herramienta puede resolver la solicitud.
- El host solicita aprobación cuando corresponda.
- El cliente envía una llamada con
{ "id": 101 }. - El servidor valida el argumento, ejecuta la lógica y devuelve el resultado.
- El modelo interpreta ese resultado y responde al usuario.
El modelo no entra directamente en la base de datos. Solo puede pedir la ejecución de las operaciones que el servidor haya publicado.
¿Para qué se usa MCP?
MCP resulta especialmente útil para:
- consultar información privada o actualizada que el modelo no conoce;
- permitir que un agente invoque APIs internas;
- crear o actualizar tickets, facturas, tareas o registros;
- consultar documentación empresarial;
- automatizar flujos que abarcan varios sistemas;
- reutilizar una integración en diferentes clientes compatibles;
- dar a un agente herramientas con nombres, descripciones y argumentos bien definidos.
Por ejemplo, podrías construir un servidor MCP de QuickBooks con herramientas como create_invoice, find_customer y list_companies. El agente sabría qué operación elegir por su descripción y podría validar los datos antes de invocarla.
¿Cuándo debería usarlo?
Usa MCP cuando se cumplan varias de estas condiciones:
- la funcionalidad será consumida por uno o más agentes o asistentes de IA;
- quieres que las capacidades puedan descubrirse dinámicamente;
- necesitas reutilizar la misma integración desde diferentes hosts;
- quieres separar el agente de la API o base de datos real;
- necesitas contratos claros y validación estructurada de entradas;
- vas a ofrecer un conjunto relacionado de herramientas, recursos o prompts.
¿Cuándo no hace falta?
MCP no es obligatorio para todo proyecto con IA. Probablemente no lo necesitas si:
- tu aplicación solo hace una llamada fija y sencilla a una API;
- ningún modelo necesita decidir qué herramienta utilizar;
- el consumidor no es un agente ni un cliente compatible con MCP;
- una función interna directa es suficiente;
- estás agregando MCP únicamente porque está de moda.
Una regla práctica: si la integración solo será llamada por tu backend de una forma totalmente determinista, una función o API normal suele ser más simple. Si quieres que diferentes agentes descubran y utilicen capacidades mediante un contrato común, MCP empieza a aportar valor.
MCP no es lo mismo que function calling, una API o RAG
| Tecnología | Propósito principal |
|---|---|
| API REST/GraphQL | Exponer operaciones de un sistema a otros programas |
| Function calling | Permitir que un modelo seleccione funciones definidas por una aplicación |
| RAG | Recuperar documentos relevantes para incorporarlos al contexto del modelo |
| MCP | Estandarizar cómo clientes de IA descubren y consumen herramientas, recursos y prompts |
Estas tecnologías pueden trabajar juntas. Un servidor MCP puede envolver una API REST; una tool puede ejecutar una búsqueda RAG; y el host puede convertir las tools descubiertas en funciones disponibles para el modelo.
Ejemplo práctico: MCP para tickets de soporte
Construiremos un servidor local y sin dependencias externas. Los datos vivirán en memoria para concentrarnos en MCP.
El servidor publicará:
get_ticket: consulta un ticket;update_ticket_status: actualiza su estado;support://playbook: ofrece una guía como recurso;analyze_ticket: genera un prompt para analizar un ticket.
Requisitos
- Node.js 20 o posterior para el servidor;
- Node.js 22.19 o posterior si deseas usar la versión actual de MCP Inspector;
- npm;
- Codex opcionalmente, para probarlo desde un agente real.
1. Crear el proyecto
mkdir mcp-ticket-server
cd mcp-ticket-server
npm init -y
npm install @modelcontextprotocol/server zod
npm install -D typescript @types/node
mkdir src
La familia actual del SDK oficial separa los paquetes de servidor y cliente. En este ejemplo solo instalamos el servidor.
2. Configurar package.json
Reemplaza el contenido por:
{
"name": "mcp-ticket-server",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "tsc",
"start": "node build/index.js",
"inspect": "npm run build && npx @modelcontextprotocol/inspector node build/index.js"
},
"dependencies": {
"@modelcontextprotocol/server": "latest",
"zod": "latest"
},
"devDependencies": {
"@types/node": "latest",
"typescript": "latest"
}
}
Para un tutorial, latest evita copiar una versión que pronto quede vieja. En un proyecto real debes conservar el package-lock.json y usar versiones fijadas y revisadas.
3. Crear tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"types": ["node"],
"rootDir": "./src",
"outDir": "./build",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*.ts"]
}
4. Crear src/index.ts
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import * as z from "zod/v4";
type TicketStatus = "open" | "in_progress" | "resolved";
interface Ticket {
id: number;
title: string;
priority: "low" | "medium" | "high";
status: TicketStatus;
}
const tickets = new Map<number, Ticket>([
[
101,
{
id: 101,
title: "El usuario no puede iniciar sesión",
priority: "high",
status: "open",
},
],
[
102,
{
id: 102,
title: "El reporte mensual tarda demasiado",
priority: "medium",
status: "in_progress",
},
],
]);
const server = new McpServer({
name: "ticket-support",
version: "1.0.0",
});
server.registerTool(
"get_ticket",
{
title: "Consultar ticket",
description: "Obtiene un ticket de soporte por su identificador numérico.",
inputSchema: z.object({
id: z.number().int().positive().describe("Identificador del ticket"),
}),
},
async ({ id }) => {
const ticket = tickets.get(id);
if (!ticket) {
return {
isError: true,
content: [{ type: "text", text: `No existe el ticket ${id}.` }],
};
}
return {
content: [{ type: "text", text: JSON.stringify(ticket, null, 2) }],
structuredContent: { ...ticket },
};
},
);
server.registerTool(
"update_ticket_status",
{
title: "Actualizar estado del ticket",
description:
"Cambia el estado de un ticket. Es una operación de escritura y debe confirmarse con el usuario.",
inputSchema: z.object({
id: z.number().int().positive(),
status: z.enum(["open", "in_progress", "resolved"]),
}),
},
async ({ id, status }) => {
const ticket = tickets.get(id);
if (!ticket) {
return {
isError: true,
content: [{ type: "text", text: `No existe el ticket ${id}.` }],
};
}
const previousStatus = ticket.status;
ticket.status = status;
return {
content: [
{
type: "text",
text: `Ticket ${id}: ${previousStatus} → ${status}`,
},
],
structuredContent: { ...ticket },
};
},
);
server.registerResource(
"support-playbook",
"support://playbook",
{
title: "Guía de soporte",
description: "Reglas para analizar y cerrar tickets.",
mimeType: "text/markdown",
},
async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "text/markdown",
text: [
"# Guía de soporte",
"",
"1. Confirma el síntoma y el impacto.",
"2. Revisa evidencia antes de cambiar el estado.",
"3. No marques un ticket como resuelto sin una solución verificable.",
"4. Resume la causa, la solución y la validación realizada.",
].join("\n"),
},
],
}),
);
server.registerPrompt(
"analyze_ticket",
{
title: "Analizar ticket",
description: "Crea una instrucción estructurada para analizar un ticket.",
argsSchema: z.object({
ticketId: z.string().describe("Identificador del ticket"),
}),
},
({ ticketId }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: [
`Analiza el ticket ${ticketId}.`,
"Consulta primero sus datos y utiliza la guía de soporte.",
"Explica impacto, posible causa y próximo paso.",
"No cambies su estado sin mi confirmación.",
].join(" "),
},
},
],
}),
);
async function main(): Promise<void> {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP de tickets ejecutándose mediante stdio");
}
main().catch((error: unknown) => {
console.error("Error fatal:", error);
process.exit(1);
});
¿Qué hace este código?
McpServer mantiene el catálogo de capacidades. Cada registro contiene una descripción que ayuda al modelo a decidir cuándo utilizarlo y un esquema Zod que valida la entrada.
StdioServerTransport comunica al cliente y al servidor mediante entrada y salida estándar. Es una buena opción para integraciones locales porque el host inicia el proceso por ti.
Hay un detalle importante: en un servidor stdio, stdout está reservado para los mensajes del protocolo. Por eso los logs usan console.error, que escribe en stderr. Un console.log podría corromper la comunicación.
Los tickets se guardan en memoria. Al reiniciar el servidor, cualquier cambio desaparece. Eso es intencional para esta prueba; luego podrías sustituir el Map por PostgreSQL, SQL Server o una API.
5. Compilar
npm run build
Deberías obtener el archivo build/index.js.
6. Probar todo con MCP Inspector
Ejecuta:
npm run inspect
El Inspector mostrará una URL local con un token temporal. Ábrela en el navegador y prueba lo siguiente:
-
El nodo podria estar Desconectado (Disconnected) conectalo.
-
En ya conectado ve a Tools, ejecuta
get_ticketcon:{ "id": 101 } -
Ejecuta
update_ticket_statuscon:{ "id": 101, "status": "resolved" } -
Vuelve a consultar el ticket y verifica el cambio.
-
En Resources, abre
support://playbookó Guía de soporte. -
En Prompts, selecciona
analyze_tickety usa101como argumento.
También puedes verificar las herramientas desde la terminal:
npx @modelcontextprotocol/inspector --cli \
node build/index.js \
--method tools/list
Si el servidor inicia, pero el Inspector no muestra nada, revisa primero que hayas compilado y que no exista ningún console.log escribiendo en stdout.
Conectarlo a Codex
Desde el directorio del proyecto, obtén su ruta absoluta:
pwd
Luego registra el servidor, sustituyendo la ruta del ejemplo:
codex mcp add ticket-support -- \
node /ruta/absoluta/mcp-ticket-server/build/index.js
Comprueba la configuración:
codex mcp list
En Codex puedes usar /mcp para ver los servidores activos. Después prueba solicitudes naturales:
Consulta el ticket 101 y resume su prioridad y estado.
Utiliza la guía de soporte para analizar el ticket 101.
Cambia el ticket 101 a resuelto.
La tercera solicitud modifica datos. Un buen host debe hacer visible la invocación y permitir que el usuario la rechace. Aun así, la autorización real también debe existir en el servidor; nunca dependas únicamente de que el modelo «se comporte bien».
Configuración manual opcional
Codex permite declarar servidores locales en ~/.codex/config.toml o, para un proyecto confiable, en .codex/config.toml:
[mcp_servers.ticket-support]
command = "node"
args = ["/ruta/absoluta/mcp-ticket-server/build/index.js"]
Los servidores locales utilizan command y args. Los servidores remotos utilizan una url y normalmente un mecanismo de autenticación.
De demostración a producción
El ejemplo enseña el protocolo, pero un servidor real necesita más controles.
Autenticación y autorización
No basta con saber quién se conectó. Cada operación debe verificar qué puede hacer esa identidad. Un usuario que puede consultar tickets no necesariamente debe poder cerrarlos.
Principio de mínimo privilegio
Expón solo las capacidades necesarias. No publiques una herramienta genérica como execute_sql si puedes ofrecer operaciones limitadas como get_ticket o list_open_tickets.
Confirmación para acciones sensibles
Crear facturas, borrar datos, enviar correos o desplegar a producción requiere controles explícitos. Diseña las herramientas para que las acciones de lectura y escritura sean claramente distinguibles.
Validación en el servidor
El esquema valida la forma de los argumentos, pero la lógica de negocio debe validar permisos, transiciones de estado, límites, duplicados e invariantes.
Idempotencia
Una llamada puede repetirse por un reintento. Para operaciones como crear facturas o realizar pagos, utiliza una clave de idempotencia y evita resultados duplicados.
Auditoría
Registra quién solicitó la acción, qué herramienta se ejecutó, sobre qué entidad, cuándo ocurrió y cuál fue el resultado. No escribas secretos ni datos personales innecesarios en los logs.
Secretos
Usa variables de entorno o un gestor de secretos. Nunca incluyas tokens y contraseñas en el código, en la descripción de una tool ni en su respuesta.
Errores útiles
Devuelve mensajes que el agente pueda interpretar y que no filtren información sensible. Para errores esperados de una tool, devuelve isError: true junto con una explicación segura.
Transporte
- stdio: adecuado para un servidor local iniciado por el host;
- Streamable HTTP: adecuado para un servicio remoto compartido por varios usuarios o equipos.
Pasar de stdio a HTTP no consiste únicamente en abrir un puerto. Debes añadir autenticación, autorización, TLS, protección contra abuso, límites, observabilidad y aislamiento entre usuarios.
Cómo diseñar buenas tools
Una tool bien diseñada debería ser:
- específica:
close_ticketcomunica mejor la intención queexecute_action; - pequeña: una responsabilidad principal por operación;
- descriptiva: el modelo depende de la descripción para elegirla;
- validada: argumentos limitados por tipos y reglas claras;
- predecible: resultados con una estructura estable;
- segura: permisos y confirmaciones acordes con el impacto;
- observable: errores y actividad rastreables.
Evita descripciones vagas como «maneja tickets». Es preferible escribir «Obtiene un ticket por su identificador numérico, sin modificarlo».
Errores comunes al comenzar
Creer que MCP es el modelo
MCP no razona ni genera texto. El modelo está en el host; MCP define cómo se comunica con capacidades externas.
Dar acceso directo a toda la base de datos
El agente debería recibir operaciones enfocadas y con permisos reducidos. El acceso amplio aumenta el riesgo de fugas y cambios accidentales.
Confundir resource con tool
Si el consumidor solo necesita leer información identificable, considera un resource. Si necesita ejecutar una operación o una consulta con parámetros, normalmente corresponde una tool.
Confiar la seguridad al prompt
«No borres datos sin permiso» es una instrucción útil, no un control de seguridad. La autorización debe aplicarse en código.
Usar stdout para logs en stdio
Los mensajes de diagnóstico deben ir a stderr. stdout transporta los mensajes MCP.
Hacer tools gigantes
Una herramienta que recibe una instrucción libre y puede ejecutar cualquier acción es difícil de controlar, probar y auditar. Prefiere capacidades concretas.
Lista de verificación
Antes de publicar un servidor MCP, confirma:
- [ ] Las tools tienen nombres y descripciones inequívocos.
- [ ] Todas las entradas se validan.
- [ ] Lecturas y escrituras están claramente separadas.
- [ ] Las operaciones sensibles requieren autorización adecuada.
- [ ] El servidor aplica mínimo privilegio.
- [ ] Los secretos no aparecen en código ni respuestas.
- [ ] Los errores esperados se devuelven de forma estructurada.
- [ ] Las operaciones críticas son idempotentes cuando corresponde.
- [ ] Existe auditoría sin datos sensibles innecesarios.
- [ ] El servidor fue probado con MCP Inspector.
- [ ] No se escriben logs en
stdoutcuando se usa stdio. - [ ] Las dependencias están fijadas y revisadas para producción.
Ideas para extender el ejemplo
Cuando tengas el servidor básico funcionando, intenta:
- sustituir el
Mappor PostgreSQL; - agregar
list_ticketscon filtros por prioridad y estado; - exigir una razón al resolver un ticket;
- impedir la transición directa de
openaresolved; - guardar un historial de cambios;
- añadir pruebas automatizadas;
- crear una versión remota con Streamable HTTP y autenticación;
- conectar el mismo servidor desde otro cliente MCP y comprobar su portabilidad.
Conclusión
MCP convierte una integración específica en una capacidad que los agentes pueden descubrir y utilizar mediante un estándar común. Su valor no está en reemplazar tus APIs, bases de datos o reglas de negocio, sino en ofrecer una frontera consistente y controlable entre esas piezas y las aplicaciones de IA.
Para comenzar, elige un caso pequeño y de bajo riesgo: consultar tickets, leer documentación o ejecutar un cálculo. Define una tool concreta, pruébala con Inspector y conéctala a un host. Cuando el flujo sea claro, añade persistencia, autenticación y controles de producción.
El ejemplo de este artículo ya contiene las tres piezas fundamentales —tools, resources y prompts— y puede servirte como base para construir un MCP de QuickBooks, Monday, Azure DevOps, Keycloak o cualquier sistema que quieras poner a disposición de tus agentes.
Fuentes oficiales
- Introducción a Model Context Protocol
- Guía oficial para construir un servidor MCP
- MCP Inspector
- Especificación de tools en MCP
- Configurar servidores MCP en Codex
- SDK oficial de MCP para TypeScript
Artículo actualizado en agosto de 2026. MCP y sus SDK evolucionan rápidamente; verifica las versiones y la documentación oficial antes de iniciar una implementación de producción.
Comments ()