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:
- Universal: Funciona sin importar el proveedor de LLM
- Estandarizado: Un patrón de integración para todas las fuentes de datos
- Bidireccional: El AI puede consultar datos y recibir actualizaciones
- Seguro: Autenticación y permisos integrados
- 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:
- MCP Client: Vive en tu app de IA. Descubre y conecta a servidores MCP.
- MCP Server: Expone datos y herramientas en formato estándar.
- 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
- Respuestas Streaming: Soporte first-class para outputs de herramientas en streaming
- Herramientas Multi-Modales: Herramientas que retornan imágenes, audio o video
- Composición de Herramientas: Combinar múltiples herramientas en workflows
- Seguridad Mejorada: Flujos OAuth integrados y scopes de permisos
MCP vs. Alternativas
| Feature | MCP | OpenAI Functions | LangChain Tools |
|---|---|---|---|
| Vendor-agnostic | ✅ | ❌ | ✅ |
| Protocolo estándar | ✅ | ❌ | ❌ |
| Recursos integrados | ✅ | ❌ | ❌ |
| Notificaciones de progreso | ✅ | ❌ | Parcial |
| Servidores comunitarios | Creciendo | N/A | Limitado |
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:
- MCP = el estándar para conectar AI con el mundo
- Empieza con servidores simples — el SDK lo hace fácil
- Seguridad primero — los agentes AI son impredecibles
- Descripciones ricas importan — es documentación de API para AI
- 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
| Concepto | Descripción | Ejemplo |
|---|---|---|
| Server | Expone herramientas y recursos | Servidor de acceso a DB |
| Client | Conecta a servidores, usado por AI | Claude Desktop |
| Tool | Función invocable | create_github_issue |
| Resource | Datos legibles | file:///project/README.md |
| Prompt | Template reutilizable | Template de code review |
| Transport | Capa de comunicación | stdio, SSE, WebSocket |
Recursos:
Explora herramientas relacionadas
Prueba estas herramientas gratuitas de Pockit