Introducción
Imagine abrir Claude y preguntarle: "¿Qué landing pages están perdiendo más tráfico orgánico esta semana?" y obtener una respuesta real — con URLs específicas, porcentajes de caída y las keywords responsables. No una respuesta genérica sobre SEO. Sus datos reales.
Eso es lo que MCP permite. El Model Context Protocol es la capa que faltaba entre las herramientas de IA y los sistemas en los que realmente opera su negocio. Construí el servidor MCP para HeySeo, un SaaS de analítica SEO, y cambió la forma en que uso Claude, Cursor y Windsurf todos los días.
En esta guía explicaré qué es MCP, cómo funciona la arquitectura y cómo construir uno desde cero — con el código TypeScript real que uso en producción.
¿Qué es MCP?
MCP, abreviatura de Model Context Protocol, es un estándar abierto creado por Anthropic a finales de 2024. La idea central es simple: darle a los asistentes de IA una forma estandarizada de llamar herramientas y leer datos de sistemas externos.
Antes de MCP, cada integración de herramienta era personalizada. Usted escribía un plugin personalizado de Claude, una extensión de Cursor o una integración de Windsurf por separado. Cada una tenía su propio contrato de API, su propio esquema de autenticación, su propio formato para pasar contexto. Era el mismo problema de duplicación que tenían los clientes de APIs REST antes de OpenAPI — y MCP lo resuelve de la misma manera: con un protocolo compartido.
Un servidor MCP expone tools, resources y prompts sobre una interfaz JSON-RPC estándar. Un cliente de IA (Claude Desktop, Cursor, Windsurf o su propia aplicación) se conecta a ese servidor y usa las capacidades que expone. Construya el servidor una vez, conecte cada cliente.
Lo que puede exponer vía MCP:
- Tools — funciones que la IA puede invocar (obtener analíticas, buscar, ejecutar consultas)
- Resources — fuentes de datos legibles a las que la IA puede suscribirse (métricas en vivo, documentos)
- Prompts — plantillas de prompts pre-construidas que sus usuarios pueden invocar por nombre
Por qué MCP importa para los constructores
Si está construyendo un producto SaaS, una herramienta interna, o incluso una configuración de productividad personal, MCP es la forma más práctica de conectar IA a los datos que posee.
Antes de MCP, yo copiaba y pegaba datos en la ventana de contexto de Claude. ¿Reporte de tráfico mensual? Exportar a CSV, pegar las primeras filas, hacer preguntas. Tedioso, con pérdida de información, y limitado a lo que cabe en el contexto.
Después de MCP, le pregunto directamente a Claude. La IA llama a mi servidor MCP, que consulta las tablas correctas de la base de datos, ejecuta el análisis y devuelve datos estructurados. Claude luego sintetiza todo en una respuesta. Todo el ciclo toma segundos.
El cambio es significativo en tres aspectos:
- Frescura — la IA ve datos en vivo, no una exportación obsoleta del martes pasado
- Profundidad — la IA puede hacer consultas de seguimiento sin que yo busque manualmente más datos
- Consistencia — el mismo servidor funciona en todas las herramientas de IA que uso
Para HeySeo específicamente, esto significó que nuestros usuarios podían conectar Claude o Cursor a la analítica de su propio sitio y obtener asesoramiento SEO genuinamente contextual — no mejores prácticas genéricas.
Descripción general de la arquitectura
Antes de escribir cualquier código, ayuda entender cómo se conectan las piezas.
Arquitectura MCP
Cómo los clientes de IA se conectan a sus datos a través de un protocolo estándar
Funciones invocables
Flujos de datos legibles
Plantillas de prompts nombradas
El protocolo soporta dos modos de transporte:
- stdio — el cliente inicia el servidor como un subproceso y se comunica sobre stdin/stdout. Rápido, sin overhead de red, ideal para herramientas de desarrollo local como Claude Desktop, Cursor y Windsurf.
- HTTP con SSE — el servidor corre como un servicio HTTP estándar. Mejor para despliegues en producción donde múltiples usuarios necesitan acceso concurrente, o donde el servidor debe estar alojado separadamente del cliente.
Para HeySeo, usamos stdio para desarrolladores conectándose a través de su IDE y HTTP/SSE para el servidor de producción al que se conecta nuestro bot de Slack.
Construyendo un servidor MCP paso a paso
El SDK oficial para TypeScript es @modelcontextprotocol/sdk. Maneja el enmarcado del protocolo, el ciclo de vida de la conexión y la seguridad de tipos, para que pueda enfocarse en su lógica de negocio real.
Instalar dependencias
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsxInicializar el servidor
// 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: "heyseo",
version: "1.0.0",
});La clase McpServer es la base. Usted registra tools y resources en ella, luego le adjunta un transporte.
Registrar su primer tool
Un tool es una función que la IA puede llamar con parámetros tipados. Aquí hay un tool real de HeySeo que obtiene las keywords con mejor rendimiento para un sitio:
// src/tools/top-keywords.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { getTopKeywords } from "../data/keywords.js";
export function registerTopKeywordsTool(server: McpServer) {
server.tool(
"get_top_keywords",
"Fetch the top-ranking keywords for a site, with click and impression data from Google Search Console.",
{
siteUrl: z
.string()
.url()
.describe("The verified site URL, e.g. https://example.com"),
limit: z
.number()
.int()
.min(1)
.max(100)
.default(20)
.describe("Number of keywords to return"),
dateRange: z
.enum(["7d", "28d", "90d"])
.default("28d")
.describe("Date range for the data"),
},
async ({ siteUrl, limit, dateRange }) => {
const keywords = await getTopKeywords({ siteUrl, limit, dateRange });
return {
content: [
{
type: "text",
text: JSON.stringify(keywords, null, 2),
},
],
};
}
);
}El esquema Zod cumple doble función: valida las entradas en tiempo de ejecución y genera el JSON Schema que el cliente de IA usa para entender qué parámetros acepta el tool. Este es uno de esos detalles que importan en la práctica — si su esquema es vago, la IA llamará a su tool con argumentos incorrectos.
Registrar un resource
Los resources son diferentes de los tools. Un tool es imperativo (haga algo). Un resource es declarativo (aquí hay datos que puede leer). Piense en ello como un feed RSS al que la IA puede suscribirse.
// src/resources/site-overview.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { getSiteOverview } from "../data/overview.js";
export function registerSiteOverviewResource(server: McpServer) {
server.resource(
"site-overview",
"heyseo://sites/{siteUrl}/overview",
async (uri) => {
const siteUrl = extractSiteUrl(uri.href);
const overview = await getSiteOverview(siteUrl);
return {
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify(overview, null, 2),
},
],
};
}
);
}
function extractSiteUrl(href: string): string {
const match = href.match(/heyseo:\/\/sites\/([^/]+)\/overview/);
if (!match) {
throw new Error(`Invalid resource URI: ${href}`);
}
return decodeURIComponent(match[1]);
}Conectar el transporte e iniciar
// src/server.ts (continuación)
import { registerTopKeywordsTool } from "./tools/top-keywords.js";
import { registerSiteOverviewResource } from "./resources/site-overview.js";
registerTopKeywordsTool(server);
registerSiteOverviewResource(server);
const transport = new StdioServerTransport();
await server.connect(transport);
// Mantener activo - el servidor corre hasta que el cliente se desconectaPara despliegues HTTP en producción, cambie el transporte:
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from "express";
const app = express();
const transport = new SSEServerTransport("/messages", res);
app.get("/sse", (req, res) => server.connect(new SSEServerTransport("/messages", res)));
app.post("/messages", express.json(), (req, res) => transport.handlePostMessage(req, res));
app.listen(3000);Conectando a Claude Desktop
Claude Desktop tiene soporte nativo de MCP. Se configuran los servidores en ~/Library/Application Support/Claude/claude_desktop_config.json en macOS:
{
"mcpServers": {
"heyseo": {
"command": "node",
"args": ["/path/to/heyseo-mcp/dist/server.js"],
"env": {
"HEYSEO_API_KEY": "your-api-key"
}
}
}
}Reinicie Claude Desktop después de editar. Verá un pequeño ícono de herramientas en la barra de entrada cuando un servidor esté conectado. Desde ese punto, cualquier conversación puede llamar a sus tools — Claude decide autónomamente cuándo invocarlos basándose en el contexto.
En la práctica, hago preguntas como "¿Qué páginas se están canibalizando mutuamente para la keyword 'best crm for startups'?" y Claude llama a search_keyword_cannibalization en mi servidor MCP, obtiene datos estructurados, y sintetiza una recomendación clara. Sin necesidad de copiar y pegar.
Conectando a Cursor
Cursor añadió soporte MCP en la versión 0.43. La configuración se encuentra en ~/.cursor/mcp.json:
{
"mcpServers": {
"heyseo": {
"command": "node",
"args": ["/path/to/heyseo-mcp/dist/server.js"],
"env": {
"HEYSEO_API_KEY": "your-api-key"
}
}
}
}Dentro de Cursor, los tools MCP están disponibles en los paneles Composer (Cmd+I) y Chat (Cmd+L). La diferencia clave respecto a Claude Desktop es el contexto: cuando está trabajando en código en Cursor, la IA también tiene el contexto de sus archivos. Así que puede preguntar "basándose en la analítica de este componente, ¿cuál de estas keywords debería apuntar en el copy?" y la IA puede razonar tanto sobre el código en pantalla como sobre los datos en vivo de su servidor MCP simultáneamente.
Para HeySeo, uso esto al construir nuevas landing pages. El servidor MCP me dice las oportunidades actuales de keywords, y Cursor me ayuda a implementar la estructura de contenido que las apunta.
Conectando a Windsurf
Windsurf (el IDE agéntico de Codeium) usa el mismo formato de configuración. Abra Settings, navegue a MCP, y agregue la entrada de su servidor. El agente Cascade de Windsurf es particularmente bueno en tareas de múltiples pasos que combinan cambios de código con búsquedas de datos externos — que es exactamente lo que MCP habilita.
Algo que noté con Windsurf específicamente: su agente es más agresivo al llamar tools MCP automáticamente sin que usted lo pida. Cuando estoy trabajando en una página que tiene una URL que coincide con algo en nuestro mapa del sitio, Cascade proactivamente obtiene datos de rendimiento de keywords y los muestra en el panel de contexto. Es un detalle menor, pero significa que los datos están ahí antes de que se dé cuenta de que los necesita.
Ejemplo real: El servidor MCP de HeySeo
HeySeo es un SaaS de analítica SEO que se conecta a Google Search Console y GA4. La integración MCP que construimos expone las capacidades analíticas principales de la plataforma a cualquier herramienta de IA que el usuario prefiera.
Estos son los tools que proporcionamos:
| Tool | Qué hace |
|---|---|
get_top_keywords | Keywords principales por clics, impresiones, CTR |
get_landing_pages | Desglose de rendimiento a nivel de página |
get_ranking_history | Posición de keyword a lo largo del tiempo |
search_serp | Datos SERP en vivo para cualquier consulta |
get_opportunities | Brechas de keywords y victorias rápidas |
run_onpage_audit | Auditoría SEO técnica para una URL |
query_gsc | Consulta cruda de GSC con rango de fechas y dimensiones personalizadas |
Aquí está el buscador de oportunidades, que es uno de los tools más útiles en la práctica:
// src/tools/find-opportunities.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { findKeywordOpportunities } from "../data/opportunities.js";
export function registerFindOpportunitiesTool(server: McpServer) {
server.tool(
"find_opportunities",
"Find keyword opportunities for a site: queries with high impressions but low CTR (position 4-20), indicating quick wins with content improvements.",
{
siteUrl: z.string().url().describe("Site URL to analyze"),
minImpressions: z
.number()
.int()
.min(10)
.default(100)
.describe("Minimum impression threshold"),
maxPosition: z
.number()
.min(1)
.max(50)
.default(20)
.describe("Maximum average position to include"),
},
async ({ siteUrl, minImpressions, maxPosition }) => {
const opportunities = await findKeywordOpportunities({
siteUrl,
minImpressions,
maxPosition,
});
const formatted = opportunities.map((kw) => ({
query: kw.query,
impressions: kw.impressions,
clicks: kw.clicks,
ctr: `${(kw.ctr * 100).toFixed(1)}%`,
position: kw.position.toFixed(1),
estimatedTrafficGain: kw.estimatedGain,
}));
return {
content: [
{
type: "text",
text: JSON.stringify(
{
total: formatted.length,
opportunities: formatted.slice(0, 50),
},
null,
2
),
},
],
};
}
);
}La capa de datos (findKeywordOpportunities) consulta nuestra base de datos Firestore, que se sincroniza desde Google Search Console cada noche. El servidor MCP no se preocupa por la base de datos — simplemente llama funciones y devuelve datos estructurados. Esta separación mantiene el servidor simple y la lógica de negocio testeable.
La integración con Slack
Para el bot de Slack, ejecutamos el servidor MCP de HeySeo sobre HTTP/SSE y lo llamamos desde un manejador de slash commands de Slack. Los usuarios escriben /heyseo opportunities site:example.com en Slack, nuestro bot envía un POST al servidor MCP, obtiene los datos, y los formatea en un mensaje de Slack Block Kit.
Esto fueron aproximadamente 150 líneas de código sobre el servidor MCP existente. El servidor en sí no requirió ningún cambio — ese es el valor de la abstracción del protocolo.
Consideraciones de seguridad
Exponer datos de negocio a través de una interfaz de herramienta de IA introduce una superficie de seguridad real. Así es como lo pienso para HeySeo.
Autenticación. Para transportes stdio (Claude Desktop, Cursor, Windsurf), el servidor corre como el proceso del usuario. Variables de entorno para API keys son aceptables aquí — la key nunca sale de la máquina. Para transportes HTTP, use autenticación con bearer token en cada solicitud.
// src/middleware/auth.ts
export function requireApiKey(apiKey: string | undefined): void {
const expectedKey = process.env.HEYSEO_API_KEY;
if (!expectedKey) {
throw new Error("HEYSEO_API_KEY environment variable is not set");
}
if (apiKey !== expectedKey) {
throw new Error("Invalid API key");
}
}Limitación de alcance. Los tools solo deben exponer lo necesario. Si un usuario conecta su sitio personal, el servidor solo debe devolver datos de ese sitio — no de cualquier otro sitio en el sistema. Aplicamos esto vinculando cada API key a un siteUrl específico a nivel de base de datos.
Validación de entrada. Los esquemas Zod en las definiciones de sus tools son su primera línea de defensa. Trate todas las entradas de tools como no confiables y valídelas estrictamente. Nunca pase entradas de texto crudas directamente a una consulta de base de datos.
// Nunca haga esto
const results = await db.query(`SELECT * FROM keywords WHERE site = '${siteUrl}'`);
// Siempre haga esto
const results = await db.collection("keywords")
.where("siteUrl", "==", siteUrl)
.limit(100)
.get();Limitación de tasa. Las herramientas de IA pueden llamar a su servidor MCP agresivamente, especialmente durante ejecuciones de agentes de múltiples pasos. Agregue limitación de tasa por key para proteger su backend y sus cuotas de API.
// src/middleware/rate-limit.ts
const requestCounts = new Map<string, { count: number; resetAt: number }>();
export function checkRateLimit(apiKey: string, maxPerMinute = 30): void {
const now = Date.now();
const entry = requestCounts.get(apiKey);
if (!entry || entry.resetAt < now) {
requestCounts.set(apiKey, { count: 1, resetAt: now + 60_000 });
return;
}
if (entry.count >= maxPerMinute) {
throw new Error("Rate limit exceeded. Try again in a minute.");
}
requestCounts.set(apiKey, { ...entry, count: entry.count + 1 });
}Mensajes de error. No devuelva stack traces internos ni mensajes de error de base de datos al cliente de IA. Devuelva cadenas de error amigables que expliquen qué salió mal sin exponer detalles de implementación.
Despliegue en producción
Para un servidor local (transporte stdio), el despliegue es simplemente publicar el paquete npm o distribuir un binario. Los usuarios lo instalan y configuran su cliente.
Para un servidor alojado (transporte HTTP/SSE), ejecuto el servidor MCP de HeySeo en Railway con la siguiente configuración:
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY dist/ ./dist/
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "dist/server.js"]Algunas consideraciones de producción que vale la pena mencionar:
Apagado elegante. Los procesos del servidor MCP necesitan manejar SIGTERM limpiamente. Si un cliente está en medio de una conversación y su servidor muere sin una desconexión limpia, el cliente puede entrar en un estado roto.
process.on("SIGTERM", async () => {
await server.close();
process.exit(0);
});Logging estructurado. Para servidores stdio, toda la salida a stdout se convierte en parte del protocolo MCP. Nunca use console.log a stdout en un servidor stdio — use console.error o un logger estructurado que escriba a un archivo o stderr únicamente.
Health checks. Para despliegues HTTP, exponga un endpoint /health que verifique su conexión a la base de datos y cualquier dependencia de API upstream. Railway y Fly.io usarán esto para enrutar tráfico y reiniciar instancias no saludables.
Versionado. La versión del servidor MCP que declara en el constructor importa. Los clientes la usan para decidir si deben re-cachear los esquemas de tools. Incremente la versión cada vez que agregue, elimine o cambie la firma de un tool.
Qué viene para MCP
MCP alcanzó la versión 1.0 a principios de 2025 y el ecosistema ha crecido rápidamente. Algunas cosas que estoy siguiendo:
Composición multi-servidor. Hoy, cada cliente se conecta a servidores independientemente. Hay trabajo temprano en servidores proxy que agregan múltiples servidores MCP detrás de un solo endpoint — útil cuando tiene diez herramientas internas y no quiere configurar cada IDE por separado.
Sampling y streaming. El protocolo ya soporta respuestas en streaming, pero la mayoría de los servidores devuelven todo de una vez. A medida que los tamaños de datos crecen, el streaming incremental importará más. El SDK tiene los hooks; la mayoría de los servidores simplemente no los han implementado aún.
Auth estandarizada. La comunidad está convergiendo en OAuth 2.0 como el estándar para servidores MCP remotos, lo que hará que conectarse a servidores MCP de terceros sea mucho más simple que compartir API keys crudas.
Paneles nativos de IA en IDEs. Tanto Cursor como Windsurf están construyendo superficies de UI más profundas para datos MCP — no solo respuestas de texto, sino gráficos, diffs y vistas estructuradas. HeySeo está experimentando con devolver tablas Markdown y datos de gráficos que se renderizan directamente en el IDE.
La trayectoria es clara: MCP se está convirtiendo en la capa de plomería estándar entre las herramientas de IA y los sistemas con los que necesitan interactuar. Si está construyendo un producto SaaS en 2026, ofrecer un servidor MCP se está convirtiendo rápidamente en algo esperado — de la misma manera que ofrecer una API REST se volvió esperado en 2015.
Preguntas frecuentes
P: ¿Necesito construir un servidor MCP para usar MCP? R: No. Hay cientos de servidores MCP pre-construidos para herramientas comunes (GitHub, Notion, PostgreSQL, Slack y más). Consulte el registro oficial en modelcontextprotocol.io/servers. Solo necesita construir uno cuando tiene datos personalizados o APIs propietarias que exponer.
P: ¿MCP es solo para Claude de Anthropic? R: No. MCP es un estándar abierto y cualquier cliente de IA puede implementarlo. Cursor (que usa modelos de Claude y OpenAI), Windsurf (Codeium) y varios otros ya lo soportan. OpenAI no lo ha adoptado aún al momento de esta publicación, pero la presión de la comunidad está creciendo.
P: ¿Mi servidor MCP puede manejar múltiples usuarios? R: Sí, con transporte HTTP/SSE. Cada conexión de cliente obtiene su propia sesión. Se autentica por conexión y se limita el acceso a datos según los permisos de ese usuario. El transporte stdio es inherentemente de un solo usuario ya que corre como el propio proceso del usuario.
P: ¿Cómo se compara MCP con el function calling o la especificación de plugins de OpenAI? R: El function calling de OpenAI es una característica de API por solicitud. MCP es un protocolo de conexión de larga duración. MCP también funciona a través de múltiples proveedores de IA sin ningún cambio en su servidor, que es la principal ventaja práctica.
P: ¿Qué pasa cuando mi servidor MCP está caído? R: El cliente de IA falla elegantemente y le dice al usuario que la herramienta no está disponible. Claude Desktop, Cursor y Windsurf manejan esto correctamente. Implemente health checks y reinicios rápidos para minimizar el tiempo de inactividad.
Construir el servidor MCP de HeySeo tomó un fin de semana e inmediatamente cambió cómo el equipo usa las herramientas de IA. Si su producto tiene datos que podrían informar mejores decisiones asistidas por IA — analítica SEO, registros CRM, métricas de código, datos financieros — la inversión absolutamente vale la pena.
Si quiere hablar sobre su integración MCP, contáctenos y construyámoslo juntos.