Back

Model Context Protocol (MCP): La Guía Completa para Construir Agentes de IA que Realmente Funcionan

Si estás construyendo aplicaciones de IA en 2025, probablemente te has topado con el mismo muro que todos: tu LLM es brillante generando texto, pero cuando intentas conectarlo a bases de datos reales o APIs, terminas pegando todo con cinta adhesiva y rezando que los prompts funcionen.

Aquí entra Model Context Protocol (MCP)—un estándar abierto que está volviéndose tan fundamental para el desarrollo de IA como REST lo es para desarrollo web. Creado por Anthropic y ahora adoptado en toda la industria, MCP resuelve el mayor dolor de cabeza de ingeniería de IA: ¿cómo le das a tu agente acceso seguro y estructurado al mundo exterior?

En esta guía cubrimos qué es MCP, por qué importa, cómo funciona y lo más importante—cómo implementarlo.

El Problema que MCP Resuelve

Antes de entrar en MCP, veamos qué dolor resuelve.

El Infierno de las Integraciones

El desarrollo tradicional de aplicaciones de IA se ve así:

┌─────────────────────────────────────────────────────────────┐
│                    Tu Aplicación de IA                       │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐             │
│   │ OpenAI   │    │ Database │    │ Slack    │             │
│   │ API      │    │ Queries  │    │ API      │             │
│   └────┬─────┘    └────┬─────┘    └────┬─────┘             │
│        │               │               │                    │
│   Parser Custom   Parser Custom   Parser Custom             │
│        │               │               │                    │
│   Hack de Prompt  Hack de Prompt  Hack de Prompt           │
│        │               │               │                    │
│   Error Handler   Error Handler   Error Handler             │
│        │               │               │                    │
│        └───────────────┴───────────────┘                    │
│                        │                                     │
│              ┌─────────▼─────────┐                          │
│              │   Código Espagueti│                          │
│              │   de Orquestación │                          │
│              └───────────────────┘                          │
└─────────────────────────────────────────────────────────────┘

Lo que necesitas para cada integración:

  • Manejo de autenticación personalizado
  • Parsing de respuestas específico
  • Prompt engineering para explicar la herramienta al LLM
  • Manejo de errores diferente por integración
  • Mantenimiento manual de schemas

Si un agente típico necesita 10+ integraciones, tienes una pesadilla de mantenimiento.

La Limitación del Function Calling

El function calling de OpenAI ayuda, pero es específico del vendor. Tus definiciones cuidadosamente hechas para GPT-4 no funcionan con Claude, Gemini o el último modelo open source.

// Esto funciona para OpenAI... const tools = [{ type: "function", function: { name: "get_weather", description: "Obtener clima actual", parameters: { type: "object", properties: { location: { type: "string" } } } } }]; // Pero Claude tiene otro formato... // Y Gemini otro... // Y Llama otro...

Lo Que Realmente Necesitamos

La solución ideal sería:

  1. Universal: Funciona sin importar el proveedor de LLM
  2. Estandarizado: Un patrón de integración para todas las fuentes de datos
  3. Bidireccional: El AI puede consultar datos y recibir actualizaciones
  4. Seguro: Autenticación y permisos integrados
  5. Descubrible: El AI aprende qué herramientas hay en runtime

MCP proporciona exactamente esto.

¿Qué es Model Context Protocol?

MCP es un protocolo abierto que estandariza cómo las aplicaciones de IA se conectan a fuentes de datos y herramientas externas. Piénsalo como "USB para IA"—un conector universal para que cualquier modelo se conecte a cualquier dato.

Arquitectura

MCP usa arquitectura cliente-servidor:

┌────────────────────────────────────────────────────────────────┐
│                        Arquitectura MCP                         │
├────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────┐         ┌─────────────────────────────┐  │
│  │   MCP Client    │         │        MCP Servers          │  │
│  │                 │         │                             │  │
│  │  ┌───────────┐  │         │  ┌─────────┐ ┌─────────┐   │  │
│  │  │ AI Model  │  │ JSON-RPC│  │ GitHub  │ │ Slack   │   │  │
│  │  │(GPT/Claude│◄─┼─────────┼─►│ Server  │ │ Server  │   │  │
│  │  │ /Gemini)  │  │   over  │  └─────────┘ └─────────┘   │  │
│  │  └───────────┘  │  stdio/ │                             │  │
│  │                 │ SSE/WS  │  ┌─────────┐ ┌─────────┐   │  │
│  │  ┌───────────┐  │         │  │Database │ │ Custom  │   │  │
│  │  │Host App   │  │         │  │ Server  │ │ Server  │   │  │
│  │  │(Tu App)   │  │         │  └─────────┘ └─────────┘   │  │
│  │  └───────────┘  │         │                             │  │
│  └─────────────────┘         └─────────────────────────────┘  │
│                                                                 │
└────────────────────────────────────────────────────────────────┘

Componentes Core:

  1. MCP Client: Vive en tu app de IA. Descubre y conecta a servidores MCP.
  2. MCP Server: Expone datos y herramientas en formato estándar.
  3. Transport Layer: JSON-RPC 2.0 sobre stdio, SSE o WebSockets.

Los Tres Primitivos

MCP define tres elementos core que cubren casi toda interacción AI-mundo exterior:

1. Resources

Datos estáticos o dinámicos que el AI puede leer. Piénsalos como "archivos" accesibles.

{ "uri": "file:///project/README.md", "name": "Project README", "mimeType": "text/markdown" }

2. Tools

Funciones que el AI puede invocar para realizar acciones.

{ "name": "create_github_issue", "description": "Crear nuevo issue en repo de GitHub", "inputSchema": { "type": "object", "properties": { "repo": { "type": "string" }, "title": { "type": "string" }, "body": { "type": "string" } }, "required": ["repo", "title"] } }

3. Prompts

Templates de prompts reutilizables con parámetros.

{ "name": "code_review", "description": "Review de código para mejores prácticas", "arguments": [ { "name": "code", "description": "El código a revisar", "required": true } ] }

Por Qué MCP Importa Ahora

La Explosión de Agentes de IA

2025 es el año de los agentes de IA. Desde Operator de OpenAI hasta las capacidades de computadora de Claude, el AI se mueve de chat a acción autónoma. Pero hay un secreto sucio: el AI autónomo solo es tan bueno como su acceso al mundo real.

Un agente de IA que no puede confiablemente:

  • Leer tu codebase
  • Consultar tu base de datos
  • Revisar tu calendario
  • Mandar mensajes al equipo

...es solo un chatbot muy caro.

MCP hace estas integraciones confiables, consistentes y mantenibles.

El Momento de Estandarización

Estamos en un punto de inflexión similar a los servicios web de los 2000. Entonces competían CORBA, DCOM y protocolos propietarios. REST ganó y de repente todos podían hacer servicios interoperables.

MCP busca ser el REST de integraciones de IA. Los players principales ya están:

  • Anthropic: Creó y mantiene el protocolo
  • Microsoft: Integrando en Copilot
  • Cursor: Soporte nativo MCP en el IDE
  • Sourcegraph: Servidores MCP para inteligencia de código

Correr un Servidor MCP es el Nuevo Servidor Web

Predicción audaz: para 2026, "¿Puedes correr un servidor MCP?" será tan común en entrevistas como "¿Puedes hacer una API REST?".

¿Por qué? Toda empresa con datos valiosos querrá exponerlos a agentes de IA de forma controlada. Eso significa servidores MCP para:

  • Documentación interna
  • Datos de clientes (con autorización)
  • Procesos de negocio
  • Herramientas de dominio

Construyendo Tu Primer Servidor MCP

Manos a la obra. Construiremos un servidor MCP que expone una API de lista de tareas.

Setup del Proyecto

# Nuevo proyecto mkdir mcp-todo-server cd mcp-todo-server npm init -y # Dependencias npm install @modelcontextprotocol/sdk zod npm install -D typescript @types/node tsx

Estructura Básica del Servidor

Crea src/index.ts:

import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; // Storage en memoria interface Todo { id: string; title: string; completed: boolean; createdAt: Date; } const todos: Map<string, Todo> = new Map(); // Crear servidor MCP const server = new Server( { name: "todo-server", version: "1.0.0", }, { capabilities: { tools: {}, resources: {}, }, } ); // Lista de herramientas disponibles server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "create_todo", description: "Crear nuevo item de tarea", inputSchema: { type: "object", properties: { title: { type: "string", description: "Título del item", }, }, required: ["title"], }, }, { name: "complete_todo", description: "Marcar tarea como completada", inputSchema: { type: "object", properties: { id: { type: "string", description: "ID de la tarea a completar", }, }, required: ["id"], }, }, { name: "delete_todo", description: "Eliminar tarea", inputSchema: { type: "object", properties: { id: { type: "string", description: "ID de la tarea a eliminar", }, }, required: ["id"], }, }, ], }; }); // Manejar llamadas a herramientas server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; switch (name) { case "create_todo": { const id = crypto.randomUUID(); const todo: Todo = { id, title: args.title as string, completed: false, createdAt: new Date(), }; todos.set(id, todo); return { content: [ { type: "text", text: JSON.stringify({ success: true, todo }, null, 2), }, ], }; } case "complete_todo": { const todo = todos.get(args.id as string); if (!todo) { return { content: [ { type: "text", text: JSON.stringify({ error: "Tarea no encontrada" }) }, ], isError: true, }; } todo.completed = true; return { content: [ { type: "text", text: JSON.stringify({ success: true, todo }, null, 2), }, ], }; } case "delete_todo": { const deleted = todos.delete(args.id as string); return { content: [ { type: "text", text: JSON.stringify({ success: deleted }, null, 2), }, ], }; } default: return { content: [ { type: "text", text: JSON.stringify({ error: "Herramienta desconocida" }) }, ], isError: true, }; } }); // Lista de recursos disponibles server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [ { uri: "todo://list", name: "Lista de Tareas", description: "Lista actual de todos los items", mimeType: "application/json", }, ], }; }); // Leer recursos server.setRequestHandler(ReadResourceRequestSchema, async (request) => { if (request.params.uri === "todo://list") { const todoList = Array.from(todos.values()); return { contents: [ { uri: "todo://list", mimeType: "application/json", text: JSON.stringify(todoList, null, 2), }, ], }; } throw new Error(`Recurso desconocido: ${request.params.uri}`); }); // Iniciar servidor async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Servidor Todo MCP corriendo en stdio"); } main().catch(console.error);

Configuración para Claude Desktop

Para usar con Claude Desktop, agrega a claude_desktop_config.json:

{ "mcpServers": { "todo": { "command": "npx", "args": ["tsx", "/path/to/mcp-todo-server/src/index.ts"] } } }

Ahora Claude puede:

  • Crear tareas: "Agrega una tarea para comprar víveres"
  • Completar tareas: "Marca la tarea de víveres como hecha"
  • Ver tareas: "¿Qué hay en mi lista de tareas?"

Patrones Avanzados de MCP

Patrón 1: Integración con Base de Datos

Una de las aplicaciones más poderosas es dar al AI acceso de lectura (a veces escritura) a tu DB:

import { Pool } from 'pg'; const pool = new Pool({ connectionString: process.env.DATABASE_URL, }); server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === "query_database") { const { query } = request.params.arguments as { query: string }; // IMPORTANTE: Validar y sanitizar query if (!isReadOnlyQuery(query)) { return { content: [{ type: "text", text: "Solo queries SELECT permitidos" }], isError: true, }; } try { const result = await pool.query(query); return { content: [{ type: "text", text: JSON.stringify(result.rows, null, 2), }], }; } catch (error) { return { content: [{ type: "text", text: `Error de query: ${error.message}` }], isError: true, }; } } }); function isReadOnlyQuery(query: string): boolean { const normalized = query.trim().toLowerCase(); return normalized.startsWith('select') && !normalized.includes('into') && !normalized.includes('update') && !normalized.includes('delete') && !normalized.includes('insert') && !normalized.includes('drop') && !normalized.includes('alter'); }

Patrón 2: Integración OAuth

Para APIs que requieren autenticación de usuario:

import { OAuth2Client } from 'google-auth-library'; const oauth2Client = new OAuth2Client( process.env.GOOGLE_CLIENT_ID, process.env.GOOGLE_CLIENT_SECRET, 'http://localhost:3000/callback' ); server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "list_calendar_events", description: "Listar próximos eventos de calendario", inputSchema: { type: "object", properties: { maxResults: { type: "number", description: "Máximo de eventos a devolver", default: 10, }, }, }, }, ], }; });

Patrón 3: Operaciones de Larga Duración con Progreso

Para operaciones que toman tiempo, MCP soporta notificaciones de progreso:

server.setRequestHandler(CallToolRequestSchema, async (request, extra) => { if (request.params.name === "analyze_codebase") { const files = await getAllFiles(request.params.arguments.path); const total = files.length; for (let i = 0; i < files.length; i++) { // Enviar actualización de progreso await extra.sendNotification({ method: "notifications/progress", params: { progressToken: request.params._meta?.progressToken, progress: i, total, }, }); await analyzeFile(files[i]); } return { content: [{ type: "text", text: `${total} archivos analizados`, }], }; } });

Mejores Prácticas de Seguridad MCP

1. Validar Todas las Entradas

Nunca confíes en datos del AI. Siempre valida:

import { z } from 'zod'; const CreateTodoSchema = z.object({ title: z.string().min(1).max(200), priority: z.enum(['low', 'medium', 'high']).optional(), }); server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === "create_todo") { const parsed = CreateTodoSchema.safeParse(request.params.arguments); if (!parsed.success) { return { content: [{ type: "text", text: `Error de validación: ${parsed.error.message}`, }], isError: true, }; } // parsed.data ahora es type-safe y validado } });

2. Implementar Rate Limiting

Protégete de agentes AI descontrolados:

import { RateLimiter } from 'limiter'; const limiter = new RateLimiter({ tokensPerInterval: 100, interval: 'minute', }); server.setRequestHandler(CallToolRequestSchema, async (request) => { if (!await limiter.tryRemoveTokens(1)) { return { content: [{ type: "text", text: "Límite de rate excedido. Intenta luego.", }], isError: true, }; } // Procesar request... });

3. Logging de Auditoría

Loguea todas las invocaciones para seguridad y debugging:

function logToolInvocation(name: string, args: unknown, result: unknown) { console.log(JSON.stringify({ timestamp: new Date().toISOString(), tool: name, arguments: args, result: result, })); }

4. Principio de Mínimo Privilegio

Solo expone lo necesario:

// MAL: Exponer acceso raw a DB tools: [{ name: "execute_sql", description: "Ejecutar cualquier query SQL", // ¡Pesadilla de seguridad! }] // BIEN: Exponer operaciones específicas tools: [ { name: "get_user_orders", description: "Obtener pedidos de usuario específico", inputSchema: { type: "object", properties: { userId: { type: "string" }, limit: { type: "number", maximum: 100 }, }, required: ["userId"], }, }, ]

MCP en Producción: Lecciones Aprendidas

Lección 1: Diseña para el Fallo

Los agentes AI llaman herramientas de formas inesperadas. Construye defensivamente:

server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const result = await handleTool(request); return result; } catch (error) { // Retorna error estructurado para que el AI recupere return { content: [{ type: "text", text: JSON.stringify({ error: error.message, suggestion: "Intenta con otros parámetros", validExamples: [ { title: "Comprar víveres" }, { title: "Llamar a mamá" }, ], }), }], isError: true, }; } });

Lección 2: Proporciona Descripciones Ricas

La calidad de tus descripciones impacta directamente qué tan bien el AI usa las herramientas:

// MAL { name: "search", description: "Buscar items", } // BIEN { name: "search_products", description: `Buscar catálogo de productos. Retorna hasta 20 productos que coinciden. Soporta filtros de categoría, rango de precio y disponibilidad. Resultados incluyen nombre, precio, estado de stock y URL de thumbnail. Para mejores resultados usa términos específicos.`, inputSchema: { type: "object", properties: { query: { type: "string", description: "Query de búsqueda. Ej: 'audífonos inalámbricos'", }, }, }, }

Lección 3: Versiona Tus Servidores

Mantén compatibilidad backward cuando evoluciones:

const server = new Server( { name: "my-server", version: "2.1.0", // Semantic versioning }, { capabilities: { tools: {}, resources: {}, }, } ); // Soporta nombres viejos y nuevos durante migración server.setRequestHandler(CallToolRequestSchema, async (request) => { const name = request.params.name; // Manejar nombre legacy if (name === "old_tool_name") { console.warn("Deprecated: usa 'new_tool_name'"); return handleNewTool(request); } if (name === "new_tool_name") { return handleNewTool(request); } });

El Futuro de MCP

Lo Que Viene

  1. Respuestas Streaming: Soporte first-class para outputs de herramientas en streaming
  2. Herramientas Multi-Modales: Herramientas que retornan imágenes, audio o video
  3. Composición de Herramientas: Combinar múltiples herramientas en workflows
  4. Seguridad Mejorada: Flujos OAuth integrados y scopes de permisos

MCP vs. Alternativas

FeatureMCPOpenAI FunctionsLangChain Tools
Vendor-agnostic
Protocolo estándar
Recursos integrados
Notificaciones de progresoParcial
Servidores comunitariosCreciendoN/ALimitado

Conclusión: El Momento de Aprender MCP es Ahora

MCP todavía está temprano, pero la trayectoria es clara. Así como saber REST era obligatorio para cualquier dev web en los 2010s, saber MCP se está volviendo obligatorio para devs de AI en los 2020s.

Puntos clave:

  1. MCP = el estándar para conectar AI con el mundo
  2. Empieza con servidores simples — el SDK lo hace fácil
  3. Seguridad primero — los agentes AI son impredecibles
  4. Descripciones ricas importan — es documentación de API para AI
  5. Diseña para fallos — ayuda al AI a recuperarse

Los equipos que dominen MCP temprano tendrán ventaja significativa. Sus agentes serán más capaces, confiables y mantenibles que los que siguen pegando APIs con cinta.

La pregunta no es si deberías aprender MCP—es si estarás adelante o corriendo detrás.


Referencia Rápida: Conceptos MCP

ConceptoDescripciónEjemplo
ServerExpone herramientas y recursosServidor de acceso a DB
ClientConecta a servidores, usado por AIClaude Desktop
ToolFunción invocablecreate_github_issue
ResourceDatos legiblesfile:///project/README.md
PromptTemplate reutilizableTemplate de code review
TransportCapa de comunicaciónstdio, SSE, WebSocket

Recursos:

mcpai-agentsllmanthropicopenaiai-engineeringmodel-context-protocol

Explora herramientas relacionadas

Prueba estas herramientas gratuitas de Pockit