Cómo Construir un Servidor MCP para Tu Producto SaaS en 2025
Si has estado atento al espacio de herramientas de IA en 2025, probablemente has notado que el Protocolo de Contexto de Modelos (MCP) ha pasado de ser "especificación interesante de Anthropic" a "algo que todo producto SaaS serio necesita soportar". Y con razón. MCP proporciona a los agentes de IA -- Claude, asistentes basados en GPT, agentes personalizados -- una forma estandarizada de descubrir e invocar tu API. Piénsalo como OpenAPI para la era de los agentes, pero con comunicación bidireccional en tiempo real integrada.
He pasado los últimos meses construyendo servidores MCP para varios productos SaaS, y quiero compartir qué realmente funciona, qué la documentación no te dice, y las decisiones arquitectónicas que importan. Esto no es un resumen de la especificación. Esta es la guía que hubiera querido tener cuando comencé.
Tabla de Contenidos
- ¿Qué es el Protocolo de Contexto de Modelos (MCP)?
- Por Qué Tu Producto SaaS Necesita un Servidor MCP
- Arquitectura MCP: Cómo Funciona Realmente
- Configuración de Tu Proyecto de Servidor MCP
- Definición de Herramientas, Recursos y Mensajes
- Conexión a Tu API de SaaS
- Autenticación y Multi-Tenencia
- Manejo de Errores y Validación
- Pruebas de Tu Servidor MCP
- Implementación y Consideraciones de Producción
- Ejemplo del Mundo Real: Construyendo un Servidor MCP para un SaaS de Gestión de Proyectos
- Preguntas Frecuentes
¿Qué es el Protocolo de Contexto de Modelos (MCP)?
MCP es un protocolo abierto creado originalmente por Anthropic que define cómo los modelos de IA se comunican con herramientas externas y fuentes de datos. Lanzado a finales de 2024 y alcanzando estabilidad v1.0 a principios de 2025, ahora es soportado por Claude Desktop, Cursor, Windsurf, el SDK de Agentes de OpenAI, y docenas de otros clientes de IA.
La idea central: en lugar de que cada integración de IA sea un plugin personalizado con un formato propietario, MCP proporciona un único protocolo que cualquier cliente compatible puede usar para descubrir qué ofrece tu servicio e interactuar con él.
Aquí está lo que MCP define:
- Herramientas -- Funciones que la IA puede invocar (como
create_ticket,search_users,generate_report) - Recursos -- Datos que la IA puede leer (como documentación, registros de base de datos, archivos de configuración)
- Mensajes -- Plantillas de mensajes reutilizables que tu servidor puede exponer
- Muestreo -- La capacidad de tu servidor para solicitar completaciones de LLM del cliente
La capa de transporte usa JSON-RPC 2.0 sobre stdio (para servidores locales) o HTTP con Server-Sent Events (SSE) para servidores remotos. El transporte Streamable HTTP más nuevo, introducido en la revisión de especificación 2025-03, es lo que querrás para cualquier implementación SaaS de producción.
Por Qué Tu Producto SaaS Necesita un Servidor MCP
Déjame ser directo: si tu producto SaaS tiene una API, deberías estar construyendo un servidor MCP ahora mismo. Aquí está el porqué.
Los agentes de IA se están convirtiendo en los consumidores de API principales. En Q1 2025, Anthropic informó que los usuarios de Claude Desktop invocan herramientas MCP más de 2 millones de veces por día. Ese número está creciendo rápidamente. Tus clientes ya están intentando usar agentes de IA para interactuar con tu producto -- la pregunta es si lo haces fácil o frustrante.
Es un canal de distribución. Cuando alguien instala Claude Desktop y escribe "ayúdame a gestionar mis proyectos", la IA puede descubrir y usar servidores MCP que el usuario ha configurado. Tu producto se vuelve accesible a través del lenguaje natural. Esto no es un truco -- es una nueva superficie genuina para tu producto.
Tus competidores lo están haciendo. Stripe, Linear, Notion, GitHub, Sentry, y Supabase todos lanzaron servidores MCP en la primera mitad de 2025. Si estás en SaaS B2B y no tienes uno, te estás quedando atrás.
| Factor | Solo REST API | REST API + Servidor MCP |
|---|---|---|
| Accesibilidad de agentes de IA | Requiere integración personalizada por agente | Cualquier cliente MCP funciona automáticamente |
| Descubrimiento | Los desarrolladores leen documentos | La IA descubre capacidades en tiempo de ejecución |
| Tiempo para primera integración | Horas a días | Minutos |
| Interacción en lenguaje natural | No es posible sin envoltorio | Integrado |
| Carga de mantenimiento | Un código base | Dos códigos base (pero MCP envuelve REST) |
Arquitectura MCP: Cómo Funciona Realmente
Antes de escribir código, aclaremos la arquitectura. Una implementación MCP tiene tres partes:
- Cliente MCP -- La aplicación de IA (Claude Desktop, Cursor, tu agente personalizado). Descubre las capacidades de tu servidor y las invoca.
- Servidor MCP -- Tu código. Expone herramientas, recursos y mensajes a través del protocolo MCP. Esto es lo que estamos construyendo.
- Tu API de SaaS -- El backend real que tu servidor MCP llama para hacer las cosas.
El flujo se ve así:
Usuario → Cliente de IA → Cliente MCP → Servidor MCP → Tu API de SaaS
↓
Las respuestas vuelven
Tu servidor MCP es esencialmente un adaptador de protocolo. Traduce entre el protocolo MCP (que hablan los clientes de IA) y tu API REST/GraphQL existente. Esto significa que no necesitas modificar tu API existente en absoluto. El servidor MCP se sienta junto a ella.
Opciones de Transporte
Para productos SaaS, tienes dos opciones de transporte realistas:
- Streamable HTTP (recomendado): Usa solicitudes HTTP estándar con SSE opcional para streaming. Funciona detrás de balanceadores de carga, CDNs, e infraestructura estándar. Esto es lo que quieres para servidores MCP remotos/alojados.
- SSE (legado): El transporte remoto original. Todavía funciona pero la especificación recomienda Streamable HTTP para nuevas implementaciones.
El transporte Stdio es excelente para herramientas locales pero no es aplicable para el servidor MCP de un producto SaaS.
Configuración de Tu Proyecto de Servidor MCP
Construyamos esto con TypeScript. El paquete oficial @modelcontextprotocol/sdk está bien mantenido y es la opción correcta para uso en producción. Python tiene mcp (el SDK oficial de Python) si ese es tu stack, pero me enfocaré en TypeScript ya que la mayoría de backends SaaS con los que trabajo usan Node.js.
mkdir my-saas-mcp-server
cd my-saas-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod express
npm install -D typescript @types/node @types/express tsx
Configura tu tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
Ahora creemos la estructura básica del servidor:
// src/server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express from "express";
const app = express();
app.use(express.json());
const server = new McpServer({
name: "my-saas-mcp",
version: "1.0.0",
description: "MCP server for My SaaS Product",
});
// Agregaremos herramientas, recursos y mensajes aquí
app.post("/mcp", async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => crypto.randomUUID(),
});
await server.connect(transport);
await transport.handleRequest(req, res);
});
app.get("/mcp", async (req, res) => {
// Punto final SSE para respuestas de streaming
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => crypto.randomUUID(),
});
await server.connect(transport);
await transport.handleRequest(req, res);
});
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`MCP server running on port ${PORT}`);
});
Ese es tu esqueleto. Rellenémoslo.
Definición de Herramientas, Recursos y Mensajes
Herramientas
Las herramientas son la primitiva más importante. Cada herramienta es una función que la IA puede invocar con parámetros estructurados. Aquí está cómo definir una:
import { z } from "zod";
server.tool(
"create_project",
"Create a new project in the workspace",
{
name: z.string().describe("The project name"),
description: z.string().optional().describe("Project description"),
team_id: z.string().describe("ID of the team that owns this project"),
},
async ({ name, description, team_id }) => {
// Llama a tu API de SaaS aquí
const project = await apiClient.createProject({ name, description, team_id });
return {
content: [
{
type: "text",
text: JSON.stringify(project, null, 2),
},
],
};
}
);
Algunas cosas que he aprendido sobre el diseño de herramientas:
Sé específico con descripciones. La IA usa tu descripción de herramienta y descripciones de parámetros para decidir cuándo y cómo invocarla. Las descripciones vagas conducen a selección incorrecta de herramientas. "Create a new project" está bien. "Create a new project in the user's workspace. Requires a team_id which can be obtained from the list_teams tool" es mucho mejor.
Mantén el conteo de herramientas manejable. He visto a personas exponer 50+ herramientas y preguntarse por qué la IA se confunde. Comienza con 10-15 operaciones principales. Siempre puedes añadir más.
Devuelve datos estructurados. Siempre devuelve JSON en tu contenido de texto. La IA lo analiza mejor que la prosa.
Recursos
Los recursos exponen datos legibles. Piénsalos como puntos finales GET para consumo de IA:
server.resource(
"project-list",
"projects://list",
"List of all projects in the workspace",
async () => {
const projects = await apiClient.listProjects();
return {
contents: [
{
uri: "projects://list",
mimeType: "application/json",
text: JSON.stringify(projects, null, 2),
},
],
};
}
);
Mensajes
Los mensajes son plantillas reutilizables. Están infrautilizados pero son poderosos:
server.prompt(
"weekly-report",
"Generate a weekly status report for a project",
{
project_id: z.string().describe("The project ID to report on"),
},
async ({ project_id }) => {
const stats = await apiClient.getProjectStats(project_id);
return {
messages: [
{
role: "user",
content: {
type: "text",
text: `Generate a weekly status report based on this data: ${JSON.stringify(stats)}. Include completed tasks, blockers, and upcoming deadlines.`,
},
},
],
};
}
);
Conexión a Tu API de SaaS
Tu servidor MCP necesita hablar con tu API existente. Recomiendo crear una clase de cliente de API tipada:
// src/api-client.ts
class SaaSApiClient {
private baseUrl: string;
private apiKey: string;
constructor(baseUrl: string, apiKey: string) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
private async request<T>(path: string, options?: RequestInit): Promise<T> {
const response = await fetch(`${this.baseUrl}${path}`, {
...options,
headers: {
"Authorization": `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
...options?.headers,
},
});
if (!response.ok) {
const error = await response.text();
throw new Error(`API error ${response.status}: ${error}`);
}
return response.json() as T;
}
async createProject(data: CreateProjectInput): Promise<Project> {
return this.request<Project>("/api/v1/projects", {
method: "POST",
body: JSON.stringify(data),
});
}
async listProjects(): Promise<Project[]> {
return this.request<Project[]>("/api/v1/projects");
}
// ... más métodos
}
Mantén este cliente delgado. Es un pass-through, no una capa de lógica de negocio.
Autenticación y Multi-Tenencia
Aquí es donde las cosas se ponen interesantes -- y donde la mayoría de tutoriales eluden las partes difíciles.
Tu servidor MCP necesita autenticar solicitudes de dos formas:
- El cliente MCP autenticándose en tu servidor MCP ("¿es este un usuario válido?")
- Tu servidor MCP autenticándose en tu API de SaaS ("actúa en nombre de este usuario")
OAuth 2.0 (Recomendado para Producción)
La especificación MCP incluye un marco de autorización basado en OAuth 2.1. Para un producto SaaS, este es el enfoque correcto:
// Middleware para extraer y validar el token OAuth
app.use("/mcp", async (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith("Bearer ")) {
return res.status(401).json({ error: "Missing authorization" });
}
const token = authHeader.slice(7);
try {
const user = await validateOAuthToken(token);
req.user = user;
next();
} catch {
return res.status(401).json({ error: "Invalid token" });
}
});
Luego pasa el contexto del usuario en tus manejadores de herramientas. Esto es crítico para multi-tenencia -- cada llamada a API debe estar delimitada al workspace del usuario autenticado.
Enfoque de Clave de API (Más Simple, para Interno/Etapa Temprana)
Si estás en etapa temprana o es solo interno, las claves de API funcionan bien:
const apiKey = req.headers["x-api-key"] as string;
const client = new SaaSApiClient(process.env.API_BASE_URL!, apiKey);
El usuario proporciona su clave de API cuando configura el servidor MCP en su cliente, y se pasa a través de tu API.
Manejo de Errores y Validación
Los agentes de IA necesitan mensajes de error claros. Cuando una herramienta falla, la IA necesita entender por qué para que pueda arreglarlo o explicar el problema al usuario.
server.tool(
"get_project",
"Get project details by ID",
{ project_id: z.string().uuid().describe("The project's UUID") },
async ({ project_id }) => {
try {
const project = await apiClient.getProject(project_id);
return {
content: [{ type: "text", text: JSON.stringify(project, null, 2) }],
};
} catch (error) {
const message = error instanceof Error ? error.message : "Unknown error";
return {
isError: true,
content: [
{
type: "text",
text: `Failed to get project: ${message}. Make sure the project_id is a valid UUID and the project exists in your workspace.`,
},
],
};
}
}
);
Notifica la bandera isError: true. Esto le dice al cliente MCP que la invocación de herramienta falló, para que pueda manejarlo apropiadamente. Siempre incluye orientación accionable en los mensajes de error.
Pruebas de Tu Servidor MCP
Las pruebas de servidores MCP se han hecho mucho más fáciles en 2025. Aquí están tus opciones:
Inspector MCP
El Inspector MCP oficial es tu mejor amigo durante el desarrollo:
npx @modelcontextprotocol/inspector
Esto te proporciona una interfaz web donde puedes conectarte a tu servidor, explorar herramientas/recursos, e invocarlas de forma interactiva. Úsalo constantemente.
Pruebas Automatizadas
Para CI/CD, prueba tus herramientas como funciones asincrónicas regulares:
import { describe, it, expect } from "vitest";
describe("create_project tool", () => {
it("should create a project with valid input", async () => {
const result = await createProjectHandler({
name: "Test Project",
team_id: "team-123",
});
expect(result.isError).toBeUndefined();
const data = JSON.parse(result.content[0].text);
expect(data.name).toBe("Test Project");
});
it("should return error for missing team_id", async () => {
// La validación de Zod debe detectar esto antes de que el manejador se ejecute
// Prueba la capa de validación
});
});
Pruebas de Integración con Claude Desktop
Una vez que tu servidor se está ejecutando, agrégalo a la configuración de Claude Desktop:
{
"mcpServers": {
"my-saas": {
"url": "http://localhost:3001/mcp",
"headers": {
"Authorization": "Bearer your-test-token"
}
}
}
}
Luego simplemente habla con Claude e intenta usar tus herramientas naturalmente. Rápidamente encontrarás casos extremos que las pruebas automatizadas pierden.
Implementación y Consideraciones de Producción
Dónde Desplegar
Tu servidor MCP es solo una aplicación Express. Despliégala donde despliegas servicios Node.js. Algunas buenas opciones:
| Plataforma | Arranque en Frío | Costo (est.) | Mejor Para |
|---|---|---|---|
| Railway | Ninguno | ~$5-20/mes | SaaS pequeño-mediano |
| Fly.io | <500ms | ~$5-15/mes | Distribución global |
| AWS ECS/Fargate | Ninguno | ~$15-50/mes | Empresa, AWS existente |
| Vercel (Edge) | <100ms | $0-20/mes | Si ya estás en Vercel |
| Cloudflare Workers | <5ms | $0-5/mes | Crítico en rendimiento |
Limitación de Velocidad
Los agentes de IA pueden ser charlatanes. Una sola conversación de usuario podría disparar 20-30 invocaciones de herramientas. Implementa limitación de velocidad que sea generosa para el uso normal de IA pero previene abuso:
import rateLimit from "express-rate-limit";
const limiter = rateLimit({
windowMs: 60 * 1000, // 1 minuto
max: 60, // 60 solicitudes por minuto por usuario
keyGenerator: (req) => req.user?.id || req.ip,
});
app.use("/mcp", limiter);
Monitoreo
Registra cada invocación de herramienta con el ID de usuario, nombre de herramienta, y latencia. Quieres visibilidad en qué herramientas se usan más, cuáles fallan más, y dónde están los cuellos de botella de latencia. Datadog, Axiom, o incluso registros JSON estructurados a CloudWatch funcionan bien.
Versionamiento
Tu servidor MCP evolucionará. Usa el campo version en los metadatos de tu servidor y considera ejecutar múltiples versiones detrás de un prefijo de ruta (/mcp/v1, /mcp/v2) durante transiciones.
Ejemplo del Mundo Real: Construyendo un Servidor MCP para un SaaS de Gestión de Proyectos
Déjame caminar a través de un ejemplo real. Digamos que estás construyendo un servidor MCP para una herramienta de gestión de proyectos (piensa en una Linear o Asana simplificada).
Aquí está el conjunto de herramientas que expondría:
// Herramientas CRUD principales
server.tool("list_projects", ...);
server.tool("get_project", ...);
server.tool("create_project", ...);
server.tool("update_project", ...);
// Gestión de tareas
server.tool("list_tasks", ...); // con filtros para estado, asignado, proyecto
server.tool("create_task", ...);
server.tool("update_task", ...); // actualizar estado, asignado, prioridad
server.tool("add_comment", ...);
// Búsqueda e informes
server.tool("search", ...); // búsqueda de texto completo en proyectos y tareas
server.tool("get_project_stats", ...); // estadísticas de resumen para un proyecto
// Recursos
server.resource("workspace-info", ...); // configuración de workspace, miembros del equipo
// Mensajes
server.prompt("standup-report", ...); // generar un standup de actividad reciente
server.prompt("sprint-planning", ...); // ayudar a planificar un sprint
Eso es 12 herramientas, 1 recurso, y 2 mensajes. Suficiente para ser genuinamente útil sin abrumar la selección de herramientas de la IA.
La experiencia del usuario se ve así: alguien abre Claude Desktop y dice "¿Qué tareas están vencidas en el proyecto Backend Rewrite?" Claude invoca list_tasks con un filtro de estado y nombre de proyecto, obtiene los resultados, y los presenta en lenguaje natural. El usuario dice "Asigna la tarea de migración de autenticación a Sarah y aumenta su prioridad a alta." Claude invoca update_task. Se siente mágico, y realmente es solo plomería de protocolo.
Si estás construyendo algo como esto y quieres ayuda con el frontend de Next.js o la capa de CMS sin cabeza que a menudo acompaña estos proyectos, eso es algo que hacemos mucho en Social Animal. Pero el servidor MCP en sí, eso es algo que definitivamente puedes construir internamente con esta guía.
Preguntas Frecuentes
¿Cuál es la diferencia entre MCP e invocación de funciones? La invocación de funciones (como la invocación de funciones de OpenAI o el uso de herramientas de Claude) es cómo un modelo de IA decide invocar una función dentro de una sola llamada a API. MCP es el protocolo que permite que un cliente de IA descubra qué funciones están disponibles desde servidores externos. Trabajan juntos -- el cliente de IA usa la invocación de funciones internamente para decidir cuándo invocar una herramienta MCP. Piensa en MCP como la plomería entre sistemas, y en la invocación de funciones como el proceso de toma de decisiones del modelo.
¿Cuánto cuesta construir y ejecutar un servidor MCP? El servidor en sí es ligero. Para un producto SaaS típico con 10-20 herramientas, estás buscando algunos cientos de líneas de TypeScript. Los costos de alojamiento son $5-50/mes dependiendo de tu tráfico y plataforma. El costo real es el tiempo del desarrollador -- presupuesta 2-4 semanas para un servidor MCP de calidad de producción con autenticación, manejo de errores, monitoreo y pruebas. Si eso parece mucho, hemos ayudado a equipos a enviar estos más rápido. Consulta nuestra página de precios para detalles.
¿Puedo usar Python en lugar de TypeScript?
Absolutamente. El SDK oficial de Python (pip install mcp) es excelente y posiblemente tiene mejor ergonomía para definiciones de herramientas. Usa lo que tu equipo conoce. El protocolo es agnóstico al lenguaje. Si tu backend SaaS es Python (Django, FastAPI), construir el servidor MCP en Python tiene aún más sentido ya que puedes compartir modelos y lógica de validación.
¿Necesito modificar mi API existente? No. Tu servidor MCP es un servicio separado que llama a tu API existente. Es una capa adaptadora. Dicho esto, puedes encontrarte queriendo agregar algunos puntos finales de API específicamente para consumo de IA -- como un punto final de búsqueda que devuelve más contexto que tu UI necesita. Eso está bien. Pero es aditivo, no una modificación.
¿Cómo manejo operaciones de larga duración?
Algunas herramientas podrían disparar operaciones que tardan minutos (como generar un informe o procesar una gran importación). Usa la característica de notificación de progreso de MCP para mantener al cliente informado. Tu manejador de herramienta puede emitir actualizaciones de progreso mientras espera a que se complete la operación. Para operaciones muy largas (>30 segundos), considera devolver inmediatamente con un ID de trabajo y proporcionar una herramienta separada check_job_status.
¿Es MCP estable suficiente para producción? Sí, a mediados de 2025. La especificación alcanzó v1.0 en marzo de 2025, y la revisión 2025-03-26 (que agregó Streamable HTTP) es lo que los clientes principales han adoptado. Anthropic, Microsoft, Google, y OpenAI están todos invirtiendo en el protocolo. No va a desaparecer. Dicho esto, mantente atento a actualizaciones de especificación -- hay propuestas activas alrededor de flujos de autenticación mejores y comunicación de servidor a servidor.
¿Cuál es la mejor forma de manejar paginación en herramientas MCP?
Devuelve una página razonable de resultados (20-50 elementos) por defecto, y acepta parámetros cursor o page. Incluye metadatos de paginación en tu respuesta para que la IA sepa que hay más resultados. Algo como: { results: [...], next_cursor: "abc123", total_count: 342 }. La IA naturalmente pedirá la siguiente página si el usuario necesita más datos.
¿Puede un servidor MCP soportar múltiples clientes de IA simultáneamente? Sí, y debería. Tu servidor MCP es solo un servidor HTTP manejando solicitudes concurrentes. Cada solicitud incluye el token de autenticación del usuario, por lo que delimit as todo al inquilino correcto. No hay estado específico del cliente del que preocuparse si estás usando el transporte Streamable HTTP correctamente. Trátalo como cualquier otro servidor API sin estado.