Volver al Diario
7 min de lectura

Estandarización de integraciones de IA con Model Context Protocol (MCP)

Una guía práctica para compilar y proteger servidores MCP personalizados para conectar de forma segura datos empresariales con agentes de desarrollo de IA.

AI AgentsMCPNode.jsEnterprise ArchitectureAPI Security
Estandarización de integraciones de IA con Model Context Protocol (MCP)

Conectar modelos de lenguaje grandes a fuentes de datos y sistemas internos ha sido históricamente una tarea fragmentada y ad-hoc. Cada proyecto de ingeniería de IA suele comenzar con la escritura de wrappers de API personalizados, el mapeo de esquemas JSON o la escritura de código de acoplamiento propietario dentro de los entornos de ejecución del orquestador para gestionar la llamada a herramientas.

Si cambias de OpenAI a Anthropic, o si tus desarrolladores pasan de un IDE asistido por IA a otro, a menudo tienes que reescribir la capa de integración.

Esta fricción es la razón por la que el Model Context Protocol (MCP), un estándar abierto liderado por Anthropic, ha ganado terreno rápidamente. Al igual que el Language Server Protocol (LSP) estandarizó cómo los compiladores se comunican con los editores de código, MCP estandariza cómo los modelos de IA se comunican con las fuentes de datos y las herramientas de desarrollo.

Aquí tienes una guía práctica para compilar, estructurar y proteger un servidor MCP personalizado para exponer de forma segura los datos de la empresa a tus agentes de desarrollo.


La arquitectura: desacoplamiento de clientes y servidores

El principio de diseño central de MCP es el desacoplamiento. En lugar de codificar las integraciones de API directamente en el entorno de ejecución de un agente, MCP introduce una arquitectura cliente-servidor:

[ AI Agent / IDE ] (MCP Client)
       │
       │ (JSON-RPC 2.0 over Stdio or SSE)
       ▼
[ Custom MCP Server ]
       │
       ├─► Secure Database (PostgreSQL / BigQuery)
       ├─► Internal Microservices
       └─► Local Development Tools
  • Cliente MCP: El coordinador (por ejemplo, Cursor, Windsurf o un orquestador LangChain personalizado). Gestiona el razonamiento del LLM, mantiene el contexto del usuario e inicia las solicitudes de herramientas.
  • Servidor MCP: Un proceso o servicio ligero que expone tres primitivas principales:
    1. Prompts: Plantillas reutilizables para guiar las interacciones del modelo.
    2. Resources: Fuentes de datos de solo lectura (contenido de archivos, filas de bases de datos, respuestas de API).
    3. Tools: Funciones ejecutables que pueden realizar operaciones o llamar a APIs externas.

Compilación de un servidor MCP personalizado en TypeScript

Vamos a compilar un servidor MCP listo para producción utilizando Node.js y el SDK oficial de TypeScript. Este servidor expondrá una herramienta segura para buscar registros en bases de datos internas (por ejemplo, candidatos o casos de estudio) con validación de argumentos en tiempo de ejecución.

1. Inicialización del proyecto

Primero, configura un proyecto TypeScript e instala las dependencias necesarias:

npm init -y
npm install @modelcontextprotocol/sdk zod
npm install --save-dev typescript @types/node
npx tsc --init

2. Implementación del servidor

A continuación se muestra el código para un servidor MCP seguro basado en stdio. Registramos una herramienta de búsqueda y usamos zod para aplicar una validación de esquema estricta en los payloads entrantes:

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";

// 1. Initialize the MCP Server
const server = new Server(
  {
    name: "enterprise-search-service",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {}, // Advertise tool capabilities to the client
    },
  }
);

// 2. Define the Search Schema using Zod
const CandidateSearchSchema = z.object({
  query: z.string().min(2),
  limit: z.number().optional().default(5),
  department: z.enum(["Engineering", "Design", "Product", "Sales"]).optional(),
});

// 3. Register Available Tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "search_candidates",
        description: "Search internal database for candidates matching specific skills and departments.",
        inputSchema: {
          type: "object",
          properties: {
            query: { type: "string", description: "Search term or required skill" },
            limit: { type: "number", description: "Maximum records to return" },
            department: { type: "string", enum: ["Engineering", "Design", "Product", "Sales"] },
          },
          required: ["query"],
        },
      },
    ],
  };
});

// Mock database resolver
async function queryDatabase(query: string, limit: number, dept?: string) {
  // Real implementation would connect to Cloud SQL / BigQuery
  return [
    { id: 101, name: "Sarah Connor", role: "Staff Platform Engineer", tags: ["Kubernetes", "Go", "MACH"] },
    { id: 102, name: "Marcus Wright", role: "Solutions Architect", tags: ["Next.js", "React", "AI Agents"] }
  ];
}

// 4. Handle Tool Executions
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name !== "search_candidates") {
    throw new Error(`Tool ${request.params.name} not found`);
  }

  try {
    // Validate the arguments at runtime
    const args = CandidateSearchSchema.parse(request.params.arguments);

    // Fetch secure records
    const results = await queryDatabase(args.query, args.limit, args.department);

    return {
      content: [
        {
          type: "text",
          text: JSON.stringify(results, null, 2),
        },
      ],
    };
  } catch (error: any) {
    return {
      content: [
        {
          type: "text",
          text: `Error validating arguments: ${error.message}`,
        },
      ],
      isError: true,
    };
  }
});

// 5. Start Server Transport (stdio is standard for local IDE integrations)
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Enterprise Search MCP Server running on stdio");

Seguridad reforzada para el uso empresarial

Aunque la lógica de TypeScript es sencilla, exponer datos de producción internos a los LLM requiere estrictas medidas de seguridad operativas. Los modelos de IA son propensos a alucinaciones, inyecciones de prompts y bucles de ejecución de herramientas no deseados.

Aquí tienes los cuatro pilares de la seguridad empresarial en MCP:

1. Validación de esquemas y sanitización de entradas

Nunca confíes en los argumentos generados por un LLM. Valida siempre los parámetros utilizando librerías como Zod, y sanitiza las cadenas para evitar riesgos en las bases de datos:

  • Usa consultas parametrizadas u ORMs para evitar la inyección de SQL.
  • Valida las rutas de directorios para evitar exploits de recorrido de directorios si tu herramienta lee archivos locales.

2. Autenticación y transferencia de contexto OAuth

Al ejecutar en una red empresarial, debes verificar el nivel de autorización del usuario que está detrás del agente.

  • Expón MCP a través de Server-Sent Events (SSE) en lugar de stdio. Esto permite ejecutar el servidor MCP como un microservicio independiente detrás de una pasarela de API.
  • Reenvía el token de identidad del usuario (JWT) en las cabeceras de la solicitud, permitiendo que tu servidor MCP aplique Seguridad a Nivel de Fila (RLS) en la base de datos de destino.

3. Supervisión humana en el bucle (HITL)

Exponer herramientas de lectura (como search_candidates) es de bajo riesgo. Sin embargo, exponer herramientas de escritura (como update_records o deploy_service) es peligroso.

  • Implementa aprobaciones explícitas para las acciones de escritura. El cliente MCP debe interceptar las solicitudes de escritura y mostrar al desarrollador un diff con los cambios que el agente desea confirmar.
[ Agent Tool Request ] ──► [ Security Middleware ] ──► (Diff Check / UI Popup)
                                                             │
                  ┌──────────────────────────────────────────┘
                  ▼
         [ User Approves? ] ──(Yes)──► [ Execute Write on Server ]

4. Límite de tasa y restricciones

Los agentes pueden entrar en bucles de razonamiento recursivo en los que llaman a una herramienta docenas de veces en pocos segundos, lo que genera grandes cargas en las bases de datos o costes de tokens elevados.

  • Aplica un límite de tasa de solicitudes por sesión.
  • Limita el tamaño máximo de los payloads de resultados para evitar el desbordamiento de tokens.

Perspectivas futuras: Exponer sistemas heredados como primitivas de IA

El poder de Model Context Protocol radica en que convierte las interfaces de software heredadas en capacidades limpias y detectables.

Al empaquetar bases de datos internas, scripts de despliegue y APIs en una capa MCP estandarizada, te aseguras de que cualquier herramienta de IA (ya sea un editor como Cursor, un asistente de terminal o un agente de despliegue autónomo) pueda interactuar con tu lógica de negocio de forma segura, predecible y sin costes de traducción adicionales.

Share this article