MCP: Model Context Protocol
MCP (Model Context Protocol) es un protocolo abierto creado por Anthropic que estandariza cómo las aplicaciones AI se conectan con fuentes de datos y herramientas externas. Piensa en MCP como el "USB-C de la IA": una interfaz universal para conectar LLMs con el mundo.
¿Por qué MCP?
Antes de MCP, cada integración era custom:
Sin MCP: Con MCP:
App1 ──custom──▶ GitHub App1 ──MCP──▶ MCP Server GitHub
App1 ──custom──▶ Slack App2 ──MCP──▶ MCP Server GitHub
App2 ──custom──▶ GitHub App1 ──MCP──▶ MCP Server Slack
App2 ──custom──▶ Slack App2 ──MCP──▶ MCP Server Slack
N apps × M tools = N×M N apps × 1 protocolo = N+M
integraciones integraciones
Arquitectura MCP
┌─────────────────┐
│ MCP Client │ (Claude Desktop, Cursor, tu app)
│ (Host App) │
└────────┬────────┘
│ MCP Protocol (JSON-RPC sobre stdio/SSE/HTTP)
│
┌────────┴────────┐
│ MCP Server │ (proceso separado)
│ │
│ ┌─ Tools ──────┐│ Funciones que el LLM puede invocar
│ ├─ Resources ──┐│ Datos que el LLM puede leer
│ └─ Prompts ────┘│ Templates de prompts reutilizables
└─────────────────┘
Conceptos clave
| Concepto | Descripción | Ejemplo |
|---|---|---|
| Tool | Función ejecutable | create_issue, send_email |
| Resource | Datos de solo lectura | file://docs/api.md, db://users |
| Prompt | Template de prompt reutilizable | code-review, summarize |
| Transport | Cómo se comunican client/server | stdio, SSE, HTTP |
Crear un MCP Server con TypeScript
Setup
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
Server básico
// src/server.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const server = new McpServer({
name: 'my-app-server',
version: '1.0.0',
});
// ── Tool: buscar en base de datos ──────────────────────
server.tool(
'search_docs',
'Busca documentos en la base de conocimiento',
{
query: z.string().describe('Término de búsqueda'),
limit: z.number().default(5).describe('Máximo de resultados'),
},
async ({ query, limit }) => {
const results = await searchDatabase(query, limit);
return {
content: [
{
type: 'text',
text: JSON.stringify(results, null, 2),
},
],
};
}
);
// ── Tool: crear ticket de soporte ──────────────────────
server.tool(
'create_ticket',
'Crea un ticket de soporte técnico',
{
title: z.string().describe('Título del ticket'),
description: z.string().describe('Descripción detallada'),
priority: z.enum(['low', 'medium', 'high', 'critical']),
},
async ({ title, description, priority }) => {
const ticket = await createSupportTicket({ title, description, priority });
return {
content: [
{
type: 'text',
text: `Ticket creado: #${ticket.id} - ${ticket.title}`,
},
],
};
}
);
// ── Resource: documentación de la API ──────────────────
server.resource(
'api-docs',
'docs://api/reference',
async (uri) => {
const docs = await loadApiDocs();
return {
contents: [
{
uri: uri.href,
mimeType: 'text/markdown',
text: docs,
},
],
};
}
);
// ── Resource dinámico: usuario por ID ──────────────────
server.resource(
'user-profile',
'users://{userId}/profile',
async (uri) => {
const userId = uri.pathname.split('/')[1];
const user = await getUser(userId);
return {
contents: [
{
uri: uri.href,
mimeType: 'application/json',
text: JSON.stringify(user),
},
],
};
}
);
// ── Prompt: revisión de código ─────────────────────────
server.prompt(
'code-review',
'Genera un prompt para revisión de código',
{
language: z.string().describe('Lenguaje de programación'),
code: z.string().describe('Código a revisar'),
},
async ({ language, code }) => ({
messages: [
{
role: 'user',
content: {
type: 'text',
text: `Revisa este código ${language} como un senior developer:
\`\`\`${language}
${code}
\`\`\`
Analiza: bugs, seguridad, performance, legibilidad y mejoras.`,
},
},
],
})
);
// Iniciar server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('MCP Server running on stdio');
}
main();
Configurar MCP Client
En Claude Desktop
// ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
// %APPDATA%\Claude\claude_desktop_config.json (Windows)
{
"mcpServers": {
"my-app": {
"command": "node",
"args": ["/path/to/my-server/dist/server.js"],
"env": {
"DATABASE_URL": "postgresql://...",
"API_KEY": "..."
}
}
}
}
En tu propia aplicación
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
const transport = new StdioClientTransport({
command: 'node',
args: ['./mcp-server/dist/server.js'],
});
const client = new Client({ name: 'my-app', version: '1.0.0' }, {});
await client.connect(transport);
// Listar tools disponibles
const tools = await client.listTools();
console.log('Tools:', tools.tools.map(t => t.name));
// Ejecutar una tool
const result = await client.callTool({
name: 'search_docs',
arguments: { query: 'authentication', limit: 3 },
});
console.log('Result:', result);
// Leer un resource
const resource = await client.readResource({
uri: 'docs://api/reference',
});
console.log('Resource:', resource);
MCP con SSE Transport (HTTP)
Para servidores remotos, usa SSE en lugar de stdio:
// Server con SSE
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import express from 'express';
const app = express();
app.get('/sse', async (req, res) => {
const transport = new SSEServerTransport('/messages', res);
await server.connect(transport);
});
app.post('/messages', async (req, res) => {
// Manejar mensajes del cliente
await transport.handlePostMessage(req, res);
});
app.listen(3002, () => console.log('MCP SSE server on port 3002'));
// Client con SSE
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
const transport = new SSEClientTransport(
new URL('http://localhost:3002/sse')
);
const client = new Client({ name: 'my-app', version: '1.0.0' }, {});
await client.connect(transport);
Integrar MCP Tools con un LLM
class MCPAgent {
private mcpClient: Client;
async chat(userMessage: string): Promise<string> {
// 1. Obtener tools del MCP server
const { tools } = await this.mcpClient.listTools();
// 2. Convertir a formato OpenAI
const openAITools = tools.map(tool => ({
type: 'function' as const,
function: {
name: tool.name,
description: tool.description || '',
parameters: tool.inputSchema,
},
}));
// 3. Chat con function calling
const messages = [
{ role: 'user' as const, content: userMessage },
];
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages,
tools: openAITools,
});
// 4. Ejecutar tools via MCP
if (response.choices[0].message.tool_calls) {
for (const call of response.choices[0].message.tool_calls) {
const result = await this.mcpClient.callTool({
name: call.function.name,
arguments: JSON.parse(call.function.arguments),
});
// ... continuar el loop
}
}
return response.choices[0].message.content!;
}
}
MCP Servers populares
| Server | Descripción |
|---|---|
@modelcontextprotocol/server-github |
Issues, PRs, repos de GitHub |
@modelcontextprotocol/server-slack |
Canales, mensajes de Slack |
@modelcontextprotocol/server-postgres |
Consultas SQL a PostgreSQL |
@modelcontextprotocol/server-filesystem |
Leer/escribir archivos locales |
@modelcontextprotocol/server-brave-search |
Búsqueda web |
@modelcontextprotocol/server-puppeteer |
Navegación web automatizada |