UNPKG

taiga-mcp

Version:

MCP server for interacting with Taiga using natural language

569 lines (459 loc) 16.4 kB
# Guía Completa para Implementar Model Context Protocol (MCP) ## Introducción al Model Context Protocol (MCP) El Model Context Protocol (MCP) es un protocolo abierto que estandariza cómo las aplicaciones proporcionan contexto a los modelos de lenguaje (LLMs). Podemos pensar en MCP como un "puerto USB-C para aplicaciones de IA" - proporciona una forma estandarizada de conectar modelos de IA con diferentes fuentes de datos y herramientas. ### ¿Por qué usar MCP? MCP facilita la construcción de agentes y flujos de trabajo complejos basados en LLMs, ofreciendo: - Una lista creciente de integraciones predefinidas que tu LLM puede usar directamente - Flexibilidad para cambiar entre diferentes proveedores y vendedores de LLMs - Buenas prácticas para asegurar tus datos dentro de tu infraestructura ### Arquitectura general MCP sigue una arquitectura cliente-servidor donde una aplicación host puede conectarse a múltiples servidores: - **Hosts MCP**: Programas como Claude Desktop, IDEs o herramientas de IA que quieren acceder a datos a través de MCP - **Clientes MCP**: Clientes del protocolo que mantienen conexiones 1:1 con servidores - **Servidores MCP**: Programas ligeros que exponen capacidades específicas a través del protocolo estandarizado - **Fuentes de datos locales**: Archivos de tu computadora, bases de datos y servicios a los que los servidores MCP pueden acceder de forma segura - **Servicios remotos**: Sistemas externos disponibles a través de internet (por ejemplo, a través de APIs) a los que los servidores MCP pueden conectarse ## Instalación y configuración ### Instalación del SDK de TypeScript Para implementar MCP en TypeScript, necesitas instalar el SDK: ```bash npm install @modelcontextprotocol/sdk ``` ### Estructura básica de un servidor MCP A continuación se muestra la estructura básica de un servidor MCP usando el SDK de TypeScript: ```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; // Crear un servidor MCP const server = new McpServer({ name: "Mi Servidor MCP", version: "1.0.0", }); // Configurar recursos, herramientas y prompts // ... // Iniciar la comunicación a través de entrada/salida estándar const transport = new StdioServerTransport(); await server.connect(transport); ``` ## Conceptos fundamentales de MCP ### 1. Recursos (Resources) Los recursos son la forma de exponer datos a los LLMs, similares a endpoints GET en una API REST. Proporcionan datos pero no deberían realizar cálculos significativos o tener efectos secundarios. #### Recurso estático ```typescript server.resource( "config", // Nombre del recurso "config://app", // URI estática async (uri) => ({ contents: [ { uri: uri.href, text: "Configuración de la aplicación aquí", }, ], }) ); ``` #### Recurso dinámico con parámetros ```typescript import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; server.resource( "perfil-usuario", new ResourceTemplate("usuarios://{userId}/perfil", { list: undefined }), async (uri, { userId }) => ({ contents: [ { uri: uri.href, text: `Datos del perfil para el usuario ${userId}`, }, ], }) ); ``` ### 2. Herramientas (Tools) Las herramientas permiten a los LLMs realizar acciones a través de tu servidor. A diferencia de los recursos, las herramientas están diseñadas para realizar cálculos y tener efectos secundarios. #### Herramienta simple con parámetros ```typescript import { z } from "zod"; server.tool( "calcular-imc", // Nombre de la herramienta { pesoKg: z.number(), // Esquema de validación para parámetros alturaM: z.number(), }, async ({ pesoKg, alturaM }) => ({ content: [ { type: "text", text: String(pesoKg / (alturaM * alturaM)), }, ], }) ); ``` #### Herramienta asíncrona con llamada a API externa ```typescript server.tool("obtener-clima", { ciudad: z.string() }, async ({ ciudad }) => { const response = await fetch(`https://api.clima.com/${ciudad}`); const data = await response.text(); return { content: [{ type: "text", text: data }], }; }); ``` ### 3. Prompts Los prompts son plantillas reutilizables que ayudan a los LLMs a interactuar eficazmente con tu servidor: ```typescript server.prompt("revisar-codigo", { codigo: z.string() }, ({ codigo }) => ({ messages: [ { role: "user", content: { type: "text", text: `Por favor revisa este código:\n\n${codigo}`, }, }, ], })); ``` ## Implementando un servidor MCP ### Ejemplo completo: Servidor de calculadora Vamos a crear un servidor MCP que expone una calculadora como herramienta y algunos datos como recursos: ```typescript import { McpServer, ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; // Crear un servidor MCP const server = new McpServer({ name: "Calculadora MCP", version: "1.0.0", }); // Añadir recursos server.resource( "instrucciones", "docs://calculadora/instrucciones", async (uri) => ({ contents: [ { uri: uri.href, text: "Esta calculadora puede realizar operaciones básicas: suma, resta, multiplicación y división.", }, ], }) ); // Añadir recurso dinámico para historial de cálculos const historialCalculos = []; server.resource("historial", "historial://calculadora", async (uri) => ({ contents: [ { uri: uri.href, text: `Historial de cálculos recientes:\n${historialCalculos.join("\n")}`, }, ], })); // Añadir herramientas para operaciones matemáticas server.tool("sumar", { a: z.number(), b: z.number() }, async ({ a, b }) => { const resultado = a + b; historialCalculos.push(`${a} + ${b} = ${resultado}`); return { content: [{ type: "text", text: String(resultado) }] }; }); server.tool("restar", { a: z.number(), b: z.number() }, async ({ a, b }) => { const resultado = a - b; historialCalculos.push(`${a} - ${b} = ${resultado}`); return { content: [{ type: "text", text: String(resultado) }] }; }); server.tool( "multiplicar", { a: z.number(), b: z.number() }, async ({ a, b }) => { const resultado = a * b; historialCalculos.push(`${a} * ${b} = ${resultado}`); return { content: [{ type: "text", text: String(resultado) }] }; } ); server.tool("dividir", { a: z.number(), b: z.number() }, async ({ a, b }) => { if (b === 0) { return { content: [{ type: "text", text: "Error: División por cero" }], isError: true, }; } const resultado = a / b; historialCalculos.push(`${a} / ${b} = ${resultado}`); return { content: [{ type: "text", text: String(resultado) }] }; }); // Añadir un prompt de ejemplo server.prompt("calcular", { operacion: z.string() }, ({ operacion }) => ({ messages: [ { role: "user", content: { type: "text", text: `Por favor calcula lo siguiente: ${operacion}`, }, }, ], })); // Iniciar la comunicación const transport = new StdioServerTransport(); await server.connect(transport); ``` ## Métodos de transporte para servidores MCP Los servidores MCP en TypeScript necesitan conectarse a un transporte para comunicarse con los clientes. Hay varias opciones: ### 1. Entrada/Salida Estándar (stdio) Ideal para herramientas de línea de comandos e integraciones directas: ```typescript import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const transport = new StdioServerTransport(); await server.connect(transport); ``` ### 2. HTTP con Server-Sent Events (SSE) Para servidores remotos, se puede iniciar un servidor web con un endpoint SSE y otro endpoint para que el cliente envíe sus mensajes: ```typescript import express, { Request, Response } from "express"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; const server = new McpServer({ name: "servidor-remoto", version: "1.0.0", }); // Configurar recursos, herramientas y prompts... const app = express(); // Para soportar múltiples conexiones simultáneas tenemos un objeto // de búsqueda desde sessionId hasta transporte const transports: { [sessionId: string]: SSEServerTransport } = {}; app.get("/sse", async (_: Request, res: Response) => { const transport = new SSEServerTransport("/messages", res); transports[transport.sessionId] = transport; res.on("close", () => { delete transports[transport.sessionId]; }); await server.connect(transport); }); app.post("/messages", async (req: Request, res: Response) => { const sessionId = req.query.sessionId as string; const transport = transports[sessionId]; if (transport) { await transport.handlePostMessage(req, res); } else { res.status(400).send("No se encontró transporte para sessionId"); } }); app.listen(3001, () => { console.log("Servidor MCP escuchando en puerto 3001"); }); ``` ## Implementando clientes MCP El SDK también proporciona una interfaz de cliente de alto nivel para conectarse a servidores MCP: ```typescript import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; const transport = new StdioClientTransport({ command: "node", args: ["servidor.js"], }); const client = new Client( { name: "cliente-ejemplo", version: "1.0.0", }, { capabilities: { prompts: {}, resources: {}, tools: {}, }, } ); await client.connect(transport); // Listar prompts const prompts = await client.listPrompts(); console.log("Prompts disponibles:", prompts); // Obtener un prompt const prompt = await client.getPrompt("calcular", { operacion: "125 * 37", }); console.log("Prompt generado:", prompt); // Listar recursos const resources = await client.listResources(); console.log("Recursos disponibles:", resources); // Leer un recurso const resource = await client.readResource("docs://calculadora/instrucciones"); console.log("Contenido del recurso:", resource); // Llamar a una herramienta const result = await client.callTool({ name: "sumar", arguments: { a: 42, b: 58, }, }); console.log("Resultado de la herramienta:", result); ``` ## Casos de uso avanzados ### Integrando con bases de datos: Explorador SQLite Este ejemplo muestra cómo integrar un servidor MCP con una base de datos SQLite: ```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import sqlite3 from "sqlite3"; import { promisify } from "util"; import { z } from "zod"; const server = new McpServer({ name: "Explorador SQLite", version: "1.0.0", }); // Helper para crear conexión a la DB const getDb = () => { const db = new sqlite3.Database("database.db"); return { all: promisify<string, any[]>(db.all.bind(db)), close: promisify(db.close.bind(db)), }; }; // Recurso para exponer el esquema de la base de datos server.resource("schema", "schema://main", async (uri) => { const db = getDb(); try { const tables = await db.all( "SELECT sql FROM sqlite_master WHERE type='table'" ); return { contents: [ { uri: uri.href, text: tables.map((t: { sql: string }) => t.sql).join("\n"), }, ], }; } finally { await db.close(); } }); // Herramienta para ejecutar consultas SQL server.tool("consulta", { sql: z.string() }, async ({ sql }) => { const db = getDb(); try { const results = await db.all(sql); return { content: [ { type: "text", text: JSON.stringify(results, null, 2), }, ], }; } catch (err: unknown) { const error = err as Error; return { content: [ { type: "text", text: `Error: ${error.message}`, }, ], isError: true, }; } finally { await db.close(); } }); // Iniciar la comunicación const transport = new StdioServerTransport(); await server.connect(transport); ``` ### Servidor de bajo nivel Para mayor control, puedes usar la clase Server de bajo nivel directamente: ```typescript import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; const server = new Server( { name: "servidor-ejemplo", version: "1.0.0", }, { capabilities: { prompts: {}, }, } ); server.setRequestHandler(ListPromptsRequestSchema, async () => { return { prompts: [ { name: "prompt-ejemplo", description: "Una plantilla de prompt de ejemplo", arguments: [ { name: "arg1", description: "Argumento de ejemplo", required: true, }, ], }, ], }; }); server.setRequestHandler(GetPromptRequestSchema, async (request) => { if (request.params.name !== "prompt-ejemplo") { throw new Error("Prompt desconocido"); } return { description: "Prompt de ejemplo", messages: [ { role: "user", content: { type: "text", text: "Texto del prompt de ejemplo", }, }, ], }; }); const transport = new StdioServerTransport(); await server.connect(transport); ``` ## Pruebas y depuración Para probar tu servidor MCP, puedes usar el [MCP Inspector](https://github.com/modelcontextprotocol/inspector), una herramienta interactiva de depuración. ### Pasos para depurar: 1. Inicia tu servidor MCP 2. Utiliza MCP Inspector para conectarte a tu servidor 3. Prueba los recursos, herramientas y prompts disponibles 4. Verifica el correcto funcionamiento y manejo de errores ## Mejores prácticas Al construir servidores MCP, considera las siguientes prácticas recomendadas: 1. **Seguridad**: Valida todas las entradas y limita el acceso apropiadamente 2. **Modularidad**: Divide servidores complejos en piezas más pequeñas y manejables 3. **Pruebas**: Prueba cada componente a fondo antes de seguir adelante 4. **Documentación**: Documenta bien tu código para facilitar el mantenimiento futuro 5. **Cumplimiento**: Sigue cuidadosamente las especificaciones del protocolo MCP 6. **Manejo de errores**: Implementa un manejo robusto de errores y excepciones 7. **Desempeño**: Optimiza para operaciones asíncronas cuando sea posible ## Uso de LLMs para ayudar a construir servidores MCP Puedes utilizar modelos como Claude para ayudarte a construir servidores MCP: 1. Proporciona al modelo la documentación relevante de MCP 2. Describe claramente qué tipo de servidor quieres construir, especificando: - Qué recursos expondrá tu servidor - Qué herramientas proporcionará - Cualquier prompt que deba ofrecer - Con qué sistemas externos necesita interactuar 3. Comienza con la funcionalidad principal y luego itera para añadir más características 4. Pide al modelo que explique cualquier parte del código que no entiendas 5. Solicita modificaciones o mejoras según sea necesario ## Recursos adicionales - [Documentación oficial de MCP](https://modelcontextprotocol.io/) - [Especificación MCP](https://spec.modelcontextprotocol.io/) - [Repositorio de TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) - [Ejemplos de servidores MCP](https://github.com/modelcontextprotocol/servers) - [MCP Inspector](https://github.com/modelcontextprotocol/inspector) ## Conclusión El Model Context Protocol (MCP) proporciona un enfoque estandarizado para conectar modelos de lenguaje con datos y funcionalidades. Siguiendo esta guía, puedes crear servidores MCP personalizados que expongan recursos, herramientas y prompts, permitiendo interacciones más ricas y contextuales con LLMs como Claude. Ya sea que estés construyendo integraciones simples o complejas arquitecturas de agentes, MCP ofrece un marco flexible y seguro para tus necesidades de IA.