La mayoría de las guías sobre Model Context Protocol dan por supuesto que estás construyendo un servidor delante de una tienda o una base de datos, algo con pedidos e inventario. Nosotros hicimos lo menos obvio: convertimos un sitio de marketing estático en un servidor MCP en vivo y de solo lectura. Cualquier cliente MCP puede ahora hacer POST a un único endpoint y preguntar qué servicios existen o cómo iniciar una consulta, en JSON-RPC tipado, sin analizar una sola página de HTML.
Este es un relato práctico de esa implementación. Es deliberadamente acotado: dos herramientas de solo lectura, sin base de datos, sin escrituras. Esa estrechez es el punto, y de ella surgen la mayoría de las decisiones interesantes. Lo que sigue explica por qué un sitio de contenido se beneficia de un endpoint MCP en absoluto, por qué el endpoint es una Cloudflare Pages Function en lugar de una ruta de framework, por qué escribimos JSON-RPC a mano en lugar de tirar del SDK, y el único fallo de la barra final que rompe en silencio cada POST si lo pasas por alto.
Por qué poner un servidor MCP en un sitio de marketing
Una tienda expone MCP porque un agente necesita realizar una acción: comprobar stock, montar un carrito, hacer un pedido. Un sitio de marketing no tiene carrito. Entonces, ¿para qué sirve la superficie de herramientas?
La respuesta honesta es el descubrimiento por agentes. Nuestro sitio ya ejecutaba una capa de descubrimiento por agentes: un llms.txt, un conjunto de documentos .well-known y una tarjeta de servidor MCP en /.well-known/mcp/server-card.json. Esa tarjeta describía un servidor, su transporte y sus capacidades. Incluso declaraba una capacidad tools. Había un problema: nada vivía detrás de ella. La tarjeta anunciaba un servidor que no existía. Un agente que seguía la tarjeta e intentaba conectarse no obtenía nada.
Ese es un estado común para estos archivos. Se generan, se anuncian en una cabecera Link y después apuntan a un recurso que nunca se construyó. La tarjeta es una promesa sin implementación.
Así que el objetivo no era inventar una nueva superficie. Era hacer real una superficie existente y ya anunciada. Las herramientas se derivan directamente de para qué sirve un sitio de marketing:
check_servicesdevuelve el catálogo de servicios: id, nombre, descripción, categoría y URL canónica.request_quotedevuelve la URL de contacto localizada y las instrucciones para enviar una consulta.
Dos herramientas, ambas de solo lectura. Un agente puede ahora enumerar lo que hacemos y dar al usuario el siguiente paso correcto, de forma determinista, en lugar de adivinar a partir de la prosa.
La decisión de arquitectura: una Pages Function, no una ruta de Astro
El sitio está construido con Astro en modo de salida estática. No hay adaptador SSR. Cada página se prerenderiza a HTML en el momento de la compilación, y los archivos de estilo API que sirve (el JSON del catálogo de servicios, el perfil del agente) son archivos estáticos bajo public/, no rutas de servidor.
Ese único hecho determina todo el diseño. Un endpoint dinámico que lee un cuerpo de petición y devuelve una respuesta calculada no puede ser una ruta de Astro aquí, porque no hay servidor que la ejecute. Añadir uno significaría cambiar el modo de salida a híbrido o de servidor y asociar un adaptador, lo cual es un cambio grande y arriesgado en una compilación que ya produce miles de páginas.
El sitio, sin embargo, ya ejecuta Cloudflare Pages Functions escritas a mano: un middleware que gestiona redirecciones y unos pocos manejadores API pequeños. Esa es la costura. Un endpoint MCP dinámico es solo una Pages Function más, functions/mcp.ts, situada junto a las demás. Se despliega con la compilación normal, no necesita adaptador y no cambia nada de cómo se renderizan las páginas.
La lección se generaliza. Cuando quieras añadir un endpoint dinámico a un sitio por lo demás estático, no recurras al modo de servidor del framework. Recurre al primitivo de función edge que tu proveedor ya te ofrece. En Cloudflare Pages eso es una Function. El coste es un archivo, no una reconstrucción de tu modelo de renderizado.
JSON-RPC escrito a mano supera al SDK para dos herramientas
El reflejo cuando lees “servidor MCP” es instalar @modelcontextprotocol/sdk. Para un servidor stdio con muchas herramientas, recursos y prompts, ese reflejo es correcto. Para dos herramientas de solo lectura en una función edge, no lo es.
El protocolo de transporte de MCP es JSON-RPC 2.0. Un servidor que responde a un cliente MCP necesita gestionar un pequeño conjunto de métodos:
initialize, donde el servidor devuelve su versión de protocolo, sus capacidades y su identidad.tools/list, donde devuelve las definiciones de herramientas con su JSON Schema.tools/call, donde ejecuta una herramienta con nombre y devuelve el resultado envuelto en contenido MCP.- notificaciones como
notifications/initialized, que no llevan id y no esperan respuesta.
Esa es toda la superficie para un servidor de solo lectura. Implementarla a mano son aproximadamente 120 líneas. El despacho es un simple switch:
switch (method) {
case "initialize":
return ok({ protocolVersion, capabilities: { tools: { listChanged: false } }, serverInfo });
case "tools/list":
return ok({ tools: TOOLS });
case "tools/call":
return ok({ content: [{ type: "text", text: callTool(name, args, services).text }] });
default:
return err(-32601, `Method not found: ${method}`);
}
Compara eso con el SDK. El SDK está diseñado para stdio y para el protocolo completo. En un runtime edge heredas sus supuestos de empaquetado y su maquinaria de transporte para funciones que no estás usando. Para dos herramientas de lectura, una dependencia sobre la que tienes que razonar es un peor trato que 120 líneas que controlas por completo. Esta es la misma contención de solo lectura primero aplicada a las dependencias: expón el mínimo, controla el mínimo.
Lo único que merece la pena hacer con cuidado a mano es el contrato de errores. JSON-RPC tiene códigos de error definidos, y los clientes MCP los esperan: -32700 para un error de parseo, -32601 para un método desconocido, -32602 para parámetros incorrectos. Acertar con esos es lo que hace que un servidor escrito a mano se comporte como uno real ante un cliente estricto.
Solo lectura primero es una decisión de seguridad, no una limitación
La tentadora tercera herramienta es una que capta un lead: recoger un nombre, un correo y un mensaje, y enviárnoslo por email. No la construyas como una herramienta MCP abierta.
Un endpoint público con una herramienta escribible es un sumidero de spam. Cualquiera en internet puede llamar a tools/call con submit_quote y una carga falsa, y ahora tu bandeja de entrada, tu CRM o tu base de datos es un objetivo sin fricción y sin ningún humano en el proceso. En el momento en que una herramienta escribe, el endpoint necesita autenticación, limitación de velocidad y gestión de abusos, lo cual es una superficie grande para que un sitio de marketing la defienda.
Así que request_quote no escribe nada. Devuelve la URL de contacto localizada y una guía estructurada:
if (name === "request_quote") {
const lang = LOCALES.includes(String(args?.lang)) ? String(args.lang) : "en";
return { text: JSON.stringify({
contact_url: CONTACT_URLS[lang],
method: "web-form",
note: "Read-only endpoint. Submit the inquiry through the contact form; this tool does not send it for you.",
reply_time: "within one working day",
}, null, 2) };
}
El agente obtiene todo lo que necesita para hacer avanzar al usuario: la página de contacto correcta para el idioma del usuario, y una declaración clara de que el envío se realiza a través del formulario. El humano permanece en el proceso. El endpoint no tiene nada de lo que abusar.
Este no es un diseño más débil forzado por la cautela. Es la misma postura que recomendamos a los clientes que construyen MCP para sus propios sistemas: solo lectura primero, añade escrituras solo detrás de autenticación cuando haya una razón concreta. Un servidor de solo lectura es honesto sobre lo que es, y es seguro dejarlo abierto.
| Elección de diseño | Herramienta abierta de lectura y escritura | Traspaso de solo lectura |
|---|---|---|
| Autenticación requerida | Sí | No |
| Superficie de spam | Alta | Ninguna |
| Humano en el proceso | Opcional | Siempre |
| Código que defender | Límite de velocidad, validación, gestión de abusos | Ninguno |
| Idoneidad para un sitio de marketing público | Mala | Buena |
La trampa de la barra final que descarta el cuerpo de tu POST
Esta costó tiempo real de depuración, así que merece la pena decirlo con claridad.
Nuestro sitio canonicaliza las URLs con una barra final. El middleware redirige con un 301 cualquier ruta sin ella a la versión con barra. Eso está bien para las páginas. Es un desastre silencioso para un endpoint JSON-RPC.
Un POST /mcp choca con la redirección y devuelve un 301 a /mcp/. Muchos clientes HTTP, al seguir la redirección, no reenvían el cuerpo del POST, o degradan el método. El cliente MCP ve una respuesta vacía o fallida y concluye que el servidor está roto. El servidor está bien. La petición nunca llegó con su cuerpo.
La solución no es luchar contra el middleware. Es anunciar la URL que el sitio realmente sirve. La tarjeta del servidor MCP y todas las referencias de descubrimiento apuntan a /mcp/, con la barra final, de modo que un cliente que se comporte bien haga POST directamente a la URL canónica y nunca toque la redirección. Si tu framework o proveedor normaliza las barras, decide qué forma es canónica y anuncia exactamente esa forma en todas partes.
Puedes verificar el endpoint en vivo de la misma forma que nosotros:
curl -s -X POST https://wppoland.com/mcp/ \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Un POST sin barra al mismo host te mostrará el 301 en su lugar.
Cómo encaja esto con la preparación para agentes y la GEO
La generative engine optimization consiste sobre todo en ser legible y verificable para los modelos. Datos estructurados, entidades claras, afirmaciones consistentes entre tus propias superficies y las externas. Un endpoint MCP en vivo es una versión fuerte de esa legibilidad: no es una pista sobre tu contenido, es una interfaz invocable a él.
Dos cosas hacen que valga el pequeño esfuerzo incluso mientras la adopción es temprana. Primero, cierra la brecha entre lo que la capa de descubrimiento anuncia y lo que existe. Una tarjeta que apunta a un servidor funcional es una señal coherente; una tarjeta que apunta a nada es una señal rota, y las señales rotas son peores que las ausentes. Segundo, para cualquiera que venda esta capacidad, el endpoint es la prueba. Construimos servidores MCP para clientes, y la demostración más creíble de ello es uno público, de solo lectura, corriendo en nuestro propio dominio, junto al servidor MCP de WooCommerce de código abierto que publicamos.
Ningún proveedor importante de IA se compromete formalmente hoy a leer tarjetas de servidor MCP o llms.txt. Esa es una advertencia justa y la exponemos con claridad. El endpoint es barato de ejecutar, hace verdadera una promesa anunciada, y es un artefacto que funciona en lugar de una afirmación. Esos tres juntos superan el listón.
Lo que dejamos fuera deliberadamente
Una lista breve, porque saber qué omite una implementación es tan útil como saber qué incluye.
- Todavía sin herramienta
get_case_studies. Un agente puede leer los casos de estudio desdellms.txty las páginas enlazadas. Añadimos la herramienta cuando un cliente real la pide, no antes. - Sin capacidad
resourcesniprompts. El servidor declara solotools, porque eso es todo lo que implementa. Declarar una capacidad que no sirves es peor que omitirla: un cliente que llama aresources/listobtiene un error en lugar de un limpio “no soportado”. - Sin SDK, como se cubrió arriba.
- Sin escrituras, como se cubrió arriba.
Cada omisión es una decisión, no un descuido. El endpoint hace exactamente lo que las dos intenciones requieren y nada más, por lo que es lo bastante pequeño para razonar sobre él de una sentada y lo bastante seguro para dejarlo abierto a internet.
A dónde ir después
Si quieres la versión más completa de estas decisiones, el grupo de contenidos en torno a este artículo cubre el terreno adyacente: construir un servidor MCP para WooCommerce para el caso con estado y orientado a tienda, MCP frente a REST: cuándo gana cada uno para saber si necesitas MCP en absoluto, y patrones de autenticación MCP para el momento en que añades una herramienta escribible y necesitas autenticación. La versión comercial de este trabajo vive en la página de servicio de desarrollo de servidores MCP.
La versión corta cabe en una frase: un sitio de marketing puede ser un servidor MCP, el endpoint es una función edge, mantén cada herramienta como solo lectura, y anuncia la URL que tu proveedor realmente sirve.







