Migrar una API de WordPress existente a MCP: un plan de 4 semanas
Construir un servidor MCP desde cero es sencillo. Migrar desde una REST API de WordPress existente y en funcionamiento, mientras sigue atendiendo tráfico de producción, es una forma más difícil. Este plan es el que uso para pasar de “tenemos /wp-json/ y un consumidor socio” a “tenemos /wp-json/, ese socio y un servidor MCP pensado para LLM delante”, sin romper nada.
El artículo forma parte del pilar desarrollo de servidores MCP.
TL;DR
- Cuatro semanas: auditoría, estructura, ejecución en paralelo, cambio.
- REST sigue activa para los consumidores existentes; MCP es un añadido, no un sustituto.
- Los esquemas Zod salen de la auditoría de REST, no de una lista de deseos.
- Ejecución en paralelo con un agente interno antes de que el tráfico externo llegue a MCP.
- Logpush captura cada llamada a herramienta; las discrepancias entre las respuestas de MCP y de REST aparecen como errores de esquema.
Cómo auditar los endpoints de la REST API de WordPress
La auditoría es una hoja de cálculo. Una fila por endpoint, con estas columnas:
| Columna | Qué anotamos |
|---|---|
| Endpoint | /wp-json/wp/v2/posts, /wp-json/wc/v3/products, etc. |
| Verbos HTTP | GET, POST, PUT, DELETE admitidos |
| Consumidores actuales | Tienda, ERP del socio, receptores de webhooks |
| Volumen de tráfico | Peticiones al día según los registros de acceso |
| Sensibilidad de los datos | Públicos, de clientes, solo administración |
| ¿Modifica el estado? | Sí/no |
| ¿Corresponde a una herramienta MCP? | Nombre propuesto de la herramienta e intención |
| Notas | Plugins implicados, formas de campos personalizados, trampas |
En una tienda WooCommerce típica la hoja tiene de 20 a 60 filas. La mayoría se corresponde limpiamente con herramientas MCP; unas pocas (internas de administración, endpoints específicos de plugins, receptores de webhooks) se quedan solo en REST.
El resultado de la semana 1 son dos artefactos:
- Un inventario de herramientas propuesto:
catalogue.list,product.detail,order.intent,order.status,inventory.check, más lo que sea específico de tu desarrollo. - Un primer borrador de los esquemas Zod para las entradas y salidas de cada herramienta, derivado de las respuestas REST reales capturadas durante la auditoría.
Capturo las respuestas REST con curl y jq para inspeccionar la forma, o con una colección de Postman que comparte el equipo. Se trata de la verdad empírica, no de lo que dice el README. Los plugins de WordPress tienen fama de añadir campos que la documentación nunca recoge; la auditoría los detecta.
Cómo montar un servidor MCP en Cloudflare Workers
El resultado de la semana 2 es un servidor MCP que funciona, desplegado en un entorno de Cloudflare Workers que no es de producción, con el inventario de herramientas de la semana 1 implementado como adaptadores delgados sobre los endpoints REST existentes.
El esqueleto del servidor:
import { createMcpHandler, hostHeaderValidationResponse, McpServer } from "@modelcontextprotocol/server";
import { catalogueListInput, catalogueListOutput, handleCatalogueList } from "./tools/catalogue-list.js";
import { productDetailInput, productDetailOutput, handleProductDetail } from "./tools/product-detail.js";
// ... un import por herramienta
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// rechazar peticiones con una cabecera Host ajena (protección contra DNS rebinding)
const rejected = hostHeaderValidationResponse(request, [env.MCP_HOSTNAME]);
if (rejected) return rejected;
const handler = createMcpHandler(() => {
const server = new McpServer({ name: "wppoland-mcp", version: "0.1.0" });
server.registerTool(
"catalogue.list",
{
description: "List WooCommerce products with filters",
inputSchema: catalogueListInput,
outputSchema: catalogueListOutput,
annotations: { readOnlyHint: true },
},
(input) => handleCatalogueList(input, env),
);
server.registerTool(
"product.detail",
{
description: "Return one product by SKU",
inputSchema: productDetailInput,
outputSchema: productDetailOutput,
annotations: { readOnlyHint: true },
},
(input) => handleProductDetail(input, env),
);
// ... una llamada a registerTool() por herramienta
return server;
});
return handler.fetch(request);
},
};El esqueleto sigue la versión 2 del SDK oficial de TypeScript (@modelcontextprotocol/server), que implementa la revisión 2026-07-28 de la especificación. createMcpHandler devuelve un objeto con un método fetch, justo la forma que un módulo de Cloudflare Workers espera como exportación por defecto, y la factoría crea un McpServer nuevo en cada petición (guía del SDK para runtimes web-standard). La misma guía advierte de que el handler no valida Host ni Origin por sí solo, y por eso el guardián va delante. Los proyectos en @modelcontextprotocol/sdk 1.x siguen recibiendo correcciones de errores y de seguridad durante al menos seis meses después del lanzamiento de la v2 el 27 de julio de 2026 (README del SDK), así que una migración que empiece ahora debería apuntar a la v2.
Cada handler es un adaptador delgado. Recibe la entrada validada, construye la query string, hace fetch a /wp-json/, mapea la respuesta a una forma de salida alineada con schema.org, llama a parse sobre la salida y la devuelve. La lógica de negocio se queda en WordPress; el handler es una traducción mecánica.
La semana 2 incluye también la estructura de autenticación de la guía de patrones de autenticación MCP. Para la ejecución en paralelo de la semana 3 uso un único token de prueba con todos los scopes; los scopes de producción los ajusto en la semana 4.
El wrangler.toml del entorno de preview apunta a una instalación de staging de WordPress o a una copia de producción. El servidor MCP es accesible en una URL *.mcp-staging.wppoland.workers.dev disponible solo para el equipo.
Cómo probar el servidor MCP en paralelo con la REST API
En la semana 3 salen los bugs. El patrón:
Construye un arnés de comparación. Un pequeño script de TypeScript que recibe el nombre de una herramienta y una entrada, llama al servidor MCP, después llama directamente al endpoint REST equivalente y compara las dos respuestas. La diferencia se registra junto con el nombre de la herramienta, la entrada y una ruta al estilo JSON pointer hacia cada discrepancia.
async function compareToolToRest(toolName: string, input: unknown) {
const mcpResponse = await callMcp(toolName, input);
const restResponse = await callEquivalentRest(toolName, input);
const diff = jsonDiff(restResponse, mcpResponse);
if (diff.length > 0) {
await logMismatch({ toolName, input, diff });
}
}Cada herramienta contra un conjunto representativo de entradas. Para catalogue.list: query vacía, query que devuelve cientos de productos, query con filtro de categoría, query con rango de precios, query sin resultados. Para product.detail: SKU conocido, SKU desconocido, SKU con variaciones, SKU con campos personalizados. Cubre los casos límite de la auditoría.
Apunta un agente interno al servidor MCP. Claude Desktop con el servidor MCP configurado como fuente remota de herramientas es la configuración que uso. Alguien del equipo dedica 30 minutos al día durante una semana a interactuar con su propia tienda a través del agente y anota las sorpresas en un documento compartido.
Los mismos prompts de prueba en tres clientes. Luca Rossi señala en el ensayo How to design a good MCP server, de octubre de 2026, escrito con el equipo de Manufact, que el mismo servidor puede dar resultados muy distintos en ChatGPT, en Claude y en la larga lista de otros clientes. Por eso, un conjunto corto y fijo de prompts de prueba (golden prompts) en varios clientes reales debe formar parte de las pruebas normales del producto. La lista de comprobación del ensayo añade una persona responsable del estado del servidor MCP, que lo vuelve a revisar después de cada versión importante de MCP, Claude o ChatGPT. En este plan, eso significa que las sesiones con el agente de la semana 3 pasan por Claude Desktop y por otros dos clientes que tus usuarios usan de verdad, y que vuelves a ejecutar el script de comparación tras cada revisión del protocolo, igual que tras una actualización de WordPress.
Ajusta los esquemas según lo que vuelve. Cada fallo de validación de Zod en el registro es un bug de esquema o un bug de mapeo. Corrígelo, vuelve a desplegar el Worker de preview y repite el arnés.
El resultado de la semana 3 es un arnés que pasa con cero diferencias y un inventario de herramientas que ha sobrevivido a cinco días de 30 minutos diarios de interacción real con el agente. Si falta cualquiera de los dos, la semana 4 no empieza.
Cómo desplegar el servidor MCP en producción y monitorizarlo
El cambio no tiene drama si las semanas 1 a 3 han ido bien.
Despliega los Workers de producción. wrangler deploy --env production. El servidor MCP de producción apunta al origen WordPress de producción, con scopes de producción en los tokens que emite la administración de WordPress.
Emite tokens para el primer runtime real de agente. Normalmente uno de estos: un agente interno para empleados, una integración de un socio que pide una superficie MCP, un asistente público en la tienda. Empieza por el consumidor más pequeño y de menor riesgo.
Conecta Cloudflare Logpush a un almacenamiento a largo plazo. Los campos de registro descritos en el artículo sobre cómo construir un servidor MCP para WooCommerce son los valores por defecto adecuados: nombre de la herramienta, hash de la entrada, latencia, resultado de la validación, scope del token. El almacenamiento es el que tu equipo ya usa (BigQuery, ClickHouse, S3 + Athena).
Construye un panel de observación. Tres consultas desde el primer día:
- Llamadas a herramientas por minuto, desglosadas por nombre de herramienta. Detecta picos de carga.
- Tasa de fallos de validación por herramienta, desglosada por código (
input_invalid,output_invalid). Detecta regresiones. - Latencia p50/p95/p99 por herramienta. Detecta un WordPress lento más arriba en la cadena.
Mantén REST viva e intacta. La tienda existente, las integraciones de socios existentes y los receptores de webhooks existentes siguen usando /wp-json/ exactamente igual que antes. MCP es un añadido, no un sustituto. Es la regla más importante de la migración.
Los errores más comunes al migrar la REST API a MCP
Seis cosas que he visto fallar en migraciones reales:
Un campo de la respuesta REST que la auditoría no detectó. Un plugin añade meta_data: [...] a las respuestas de productos. El esquema de salida de MCP no lo conoce. El parse de Zod falla con datos reales. Solución: repite la auditoría sobre tráfico de producción, amplía el esquema o descarta el campo de forma explícita con .transform().
Permalinks distintos entre staging y producción. La herramienta MCP product.detail devuelve el campo permalink de WooCommerce. Los permalinks de staging son https://staging.example.com/...; los de producción, https://example.com/.... Los datos de prueba pasan; producción falla en el validador de URL. Solución: configura el Worker de staging con un paso de reescritura de permalinks que replique el comportamiento de producción.
Gestión de variaciones de WooCommerce. La auditoría capturó la forma de la respuesta de un producto simple. Las respuestas de las variaciones son distintas (los SKU de las variaciones están en /wp-json/wc/v3/products/<id>/variations). Solución: trata las variaciones como un fetch independiente en product.detail y mapéalas a hasVariant de schema.org.
El token de autenticación se filtra a los registros. Un handler registra la cabecera Authorization completa para depurar. El token acaba en el almacenamiento de registros. Solución: oculta la cabecera en la capa de registro; rota todos los tokens emitidos antes de que la ocultación estuviera activa. Relevante para el cumplimiento del artículo 32 del RGPD.
La actualización de un plugin rompe la respuesta REST. WooCommerce 9.x cambia el nombre de un campo, el handler de MCP sigue esperando el nombre antiguo y el parse de Zod falla. Solución: fija la versión de WooCommerce en staging, ejecuta el arnés de comparación en cada actualización de WordPress y trata el arnés como parte de la barrera de actualización.
El agente entra en bucle con una llamada a herramienta mal formada. Un agente defectuoso reintenta order.intent 100 veces por minuto cuando la entrada no supera la validación. Sin rate limit, el origen WordPress recibe 100 llamadas en cascada. Solución: rate limit por principal, como en la guía de patrones de autenticación MCP, y devuelve retry_after_seconds en el sobre de error.
Cuánto tarda migrar la API de WordPress a MCP
Cuatro semanas es la opción por defecto. Dos ajustes:
Superficie más pequeña, dos semanas. Tres herramientas, un consumidor, sin complejidad de autenticación. Comprime la auditoría y la estructura en la semana 1, y la ejecución en paralelo y el cambio en la semana 2. El mismo patrón, un calendario más corto.
Superficie más grande, seis semanas. Una docena de herramientas, varios modos de autenticación, acciones sensibles que modifican datos. Añade una semana entre la estructura y la ejecución en paralelo para una revisión de seguridad. Añade otra entre la ejecución en paralelo y el cambio para un lanzamiento suave con un único usuario autorizado por OAuth antes de un despliegue más amplio.
Las cuatro fases mantienen el mismo orden sea cual sea el presupuesto de tiempo.
Cambia WordPress después de adoptar MCP
Dicho sin rodeos, después de la migración:
- La interfaz de administración de WordPress no cambia.
- El Block Editor no cambia.
- El sistema de autenticación de usuarios no cambia.
- Los endpoints REST existentes no cambian y siguen atendiendo a sus consumidores.
- La lógica que dispara los webhooks no cambia.
- La capa de datos (
wp_posts,wp_postmeta, tablas de WooCommerce) no cambia.
Lo que se añade: el servidor MCP en Cloudflare Workers, una interfaz para emitir tokens en la administración de WordPress (un pequeño plugin o una función del tema) y la configuración de Cloudflare Logpush. Todo lo demás sigue igual.
Eso es justo lo que hace que la migración tenga poco riesgo. Si el servidor MCP tiene un mal día, apagas el Worker. La superficie para agentes desaparece. La tienda sigue funcionando, las integraciones de socios siguen funcionando y los pedidos siguen entrando.
Cómo mapear los verbos REST a anotaciones de herramientas MCP
La hoja de auditoría ya tiene una columna que indica si el endpoint modifica el estado. En MCP esa columna se convierte en anotaciones de la herramienta. El esquema de la especificación 2026-07-28 define cuatro indicaciones booleanas en ToolAnnotations: readOnlyHint (por defecto false), destructiveHint (por defecto true), idempotentHint (por defecto false) y openWorldHint (por defecto true). Las indicaciones de destructiva e idempotente solo significan algo cuando readOnlyHint es false (esquema MCP).
Un mapeo que sale directamente de las columnas de la auditoría:
| Verbo REST en la auditoría | Anotaciones de la herramienta MCP |
|---|---|
GET sobre /wp/v2/posts o /wc/v3/products | readOnlyHint: true |
| POST que crea un registro (intención de pedido, borrador de entrada) | readOnlyHint: false, destructiveHint: false |
| PUT o PATCH sobre un ID conocido | readOnlyHint: false, idempotentHint: true |
| DELETE | readOnlyHint: false, destructiveHint: true |
Los valores por defecto son el motivo para fijarlas. Una herramienta registrada sin anotaciones cuenta como posiblemente destructiva y no idempotente, así que una herramienta de catálogo de solo lectura sin readOnlyHint le parece al cliente más arriesgada de lo que es. La guía del SDK describe el efecto práctico: un host puede aprobar automáticamente una herramienta de solo lectura y pedir confirmación antes de una destructiva (guía del SDK sobre herramientas). La especificación añade que los clientes deben tratar las anotaciones como no fiables salvo que vengan de un servidor de confianza, así que las anotaciones nunca sustituyen la comprobación de permisos del lado de WordPress.
La nomenclatura sale de la misma página de la especificación: los nombres de herramienta deberían tener entre 1 y 128 caracteres, distinguir mayúsculas de minúsculas y usar solo letras ASCII, dígitos, guion bajo, guion y punto (herramientas MCP). catalogue.list y product.detail cumplen la regla; un nombre copiado de una ruta REST con barras, no.
Cómo devolver a un agente los errores de la REST API de WordPress
Un error de la REST API de WordPress llega como JSON con code, message y data.status. La especificación MCP separa dos tipos de fallo. Los errores de protocolo (herramienta desconocida, petición mal formada, error del servidor) vuelven como errores JSON-RPC estándar. Los errores de ejecución de la herramienta (fallos de la API, errores de validación de la entrada, errores de lógica de negocio) vuelven dentro del resultado de la herramienta con isError: true, y los clientes deberían pasárselos al modelo para que corrija la llamada y vuelva a intentarlo (herramientas MCP, gestión de errores).
Para un handler apoyado en REST, eso significa que un 404 por un ID de producto desconocido o un 400 por un parámetro no válido es un error de ejecución. Devuelve isError: true con un bloque de texto que le diga al modelo qué cambiar, por ejemplo que ningún producto tiene ese SKU y que catalogue.list devuelve SKU válidos. Solo un fallo de la propia capa MCP se convierte en error JSON-RPC. Convertir cada 4xx de WordPress en un error JSON-RPC le oculta la causa al modelo, y el agente reintenta a ciegas, justo el bucle de la lista de errores de más arriba.
La misma página fija dos reglas para los resultados correctos. Cuando una herramienta declara un outputSchema, el servidor tiene que devolver resultados estructurados conformes a ese esquema. Una herramienta que devuelve structuredContent debería devolver también el JSON serializado en un bloque de texto, por compatibilidad con versiones anteriores. La versión 2 del SDK de TypeScript valida structuredContent contra outputSchema antes de que el resultado salga del servidor (guía del SDK sobre herramientas), la misma comprobación en la que se apoya el arnés de la semana 3.
Qué credencial envía el servidor MCP a WordPress
En esta arquitectura se cruzan dos credenciales, y la especificación las mantiene separadas. La autorización es opcional en MCP. Cuando un servidor sobre un transporte HTTP la admite, actúa como servidor de recursos OAuth 2.1: tiene que comprobar que cada token de acceso se emitió para él como destinatario (RFC 8707), tiene que responder HTTP 401 a los tokens no válidos o caducados y no puede aceptar ni reenviar ningún otro token (autorización MCP). Los servidores sobre el transporte stdio deberían saltarse ese flujo y leer las credenciales del entorno.
Para un Worker delante de /wp-json/, eso descarta reenviar el token del agente a WordPress. El token del agente se queda en el Worker. El Worker llama a WordPress con una credencial propia, normalmente una contraseña de aplicación (Application Password) de un usuario de WordPress dedicado que solo tiene las capacidades que necesita el inventario de herramientas. Las contraseñas de aplicación vienen con WordPress desde la versión 5.6 y se envían con autenticación HTTP Basic sobre HTTPS (manual de la REST API, autenticación). Guarda esa contraseña como secreto del Worker con wrangler secret put, no como variable normal en wrangler.toml (secretos en Cloudflare Workers).
La separación tiene una ventaja práctica en la semana 4. Rotar la credencial de WordPress no toca los tokens de los agentes, y revocar un token de agente no toca WordPress. Las capacidades del usuario dedicado las decide la columna de sensibilidad de los datos de la auditoría: un inventario que solo cubre el catálogo no necesita ninguna capacidad sobre pedidos.
Cuándo WordPress MCP Adapter es el mejor camino
Este plan pone MCP en el borde de la red, en un Worker. WordPress tiene también un camino oficial que pone MCP dentro del propio WordPress. MCP Adapter, parte de la iniciativa AI Building Blocks for WordPress, expone como herramientas, recursos y prompts MCP las habilidades (abilities) registradas con la WordPress Abilities API. En wordpress.org el plugin está en la versión 0.7.1, requiere WordPress 6.9 y PHP 7.4 y se actualizó por última vez el 16 de octubre de 2026 (página del plugin).
Lo que dice la documentación del adaptador sobre los puntos que deciden la elección:
- Las habilidades son privadas por defecto. Una habilidad llega a MCP solo cuando
meta.public(ometa.mcp.public) valetrue. En WordPress 6.9 y 7.0 esa marca solo controla MCP, y el acceso por REST sigue necesitandometa.show_in_rest; el núcleo aplicameta.publica REST a partir de la 7.1. - El servidor por defecto expone tres metaherramientas (
mcp-adapter/discover-abilities,mcp-adapter/get-ability-info,mcp-adapter/execute-ability) y responde por HTTP en/wp-json/mcp/mcp-adapter-default-server. wp mcp-adapter serve --user=<id>sirve un servidor por stdio a través de WP-CLI.- Sin un callback de permisos de transporte propio, un servidor admite a los usuarios con sesión iniciada (
is_user_logged_in()). - Las anotaciones escritas en el formato de la Abilities API (
readonly,destructive,idempotent) se convierten enreadOnlyHint,destructiveHinteidempotentHint.
El adaptador encaja cuando las herramientas son lógica de WordPress que ya vive en PHP y los clientes agénticos llegan directamente al sitio. El camino del Worker encaja cuando la superficie MCP necesita su propia emisión de tokens, límites en el borde y Logpush, o tiene que seguir respondiendo cuando el origen WordPress va lento. La auditoría de la semana 1 sirve para los dos caminos: cada fila que corresponde a una herramienta se convierte en un handler del Worker o en una habilidad registrada.
Actualización, 24 de octubre de 2026: en el benchmark Building with WordPress de Andy Peatling de octubre, Opus 5.5 en Claude Code y GPT-6.1-Sol en Codex completaron 0 de 42 tareas de propietarios de sitios cuando el adaptador era la única vía de acceso, y 33 de 42 con una contraseña de aplicación para la REST API. Las tres abilities del propio WordPress solo leen información del sitio, del usuario y del servidor, y las únicas escrituras llegaron de las abilities de productos de WooCommerce. En el issue #402, Peatling planifica abilities experimentales del núcleo en cuatro etapas para cerrar esa brecha. El 21 de octubre, el equipo de IA de WordPress dejó constancia de la aprobación para que MCP forme parte de la experiencia predeterminada de WordPress, con la 7.3 como posible objetivo y sin compromiso de versión (resumen semanal). Mientras no existan las abilities de escritura, la vía del adaptador obliga a escribirlas tú, y los endpoints REST de la auditoría de la semana 1 ya funcionan.
Actualización, 24 de octubre de 2026: cada plugin que lleva MCP al interior de WordPress añade su propia superficie de ataque. Wordfence publicó tres entradas de este tipo el 15 y el 16 de octubre de 2026: a AI Engine hasta la versión 3.8.3 le faltaba una comprobación de permisos, así que un colaborador (contributor) con sesión iniciada podía ejecutar una acción no autorizada (CVE-2026-110888); la herramienta MCP wsp_get_posts de WSP MCP hasta la versión 2.9.5 permitía a cualquier suscriptor con sesión iniciada leer títulos, autores y extractos de borradores y de entradas pendientes o programadas (CVE-2026-109382); y xSpeed Cache hasta la versión 1.4.2, un plugin de caché con funciones MCP, permitía cross-site scripting almacenado por parte de colaboradores (CVE-2026-109985). Ninguna de las tres requiere una cuenta de administrador. Registrar abilities dentro de WordPress traslada la misma pregunta a tu propio código: cada ability necesita un callback de permisos que compruebe una capability concreta, y no solo si el usuario ha iniciado sesión.
Qué versión de la especificación MCP adoptar
La revisión actual de la especificación es la 2026-07-28, y dos de sus reglas afectan a este plan. Primero, MCP no tiene sesión a nivel de protocolo, así que un servidor no puede apoyarse en el estado de la conexión para relacionar una llamada a herramienta con la siguiente. Para estado como un carrito, la especificación recomienda devolver un identificador explícito desde una herramienta de creación, aceptar ese identificador en las llamadas siguientes, comprobar la autorización de quien llama frente al identificador en cada llamada e indicar la vida útil del identificador en la descripción de la herramienta (herramientas MCP, herramientas con estado). En order.intent, el ID de la intención es ese identificador.
Segundo, cada petición lleva la versión del protocolo y las capacidades del cliente en campos _meta del cuerpo del mensaje, y el transporte Streamable HTTP los replica en cabeceras HTTP para que los intermediarios puedan enrutar peticiones sin analizar el cuerpo (transportes MCP). Del lado del adaptador, wp mcp-adapter list --protocol=2026-07-28 cuenta los componentes disponibles en esa revisión; el adaptador acepta ahí 2025-11-25 y 2026-07-28.
Temas relacionados
Este artículo trata la forma de la migración. La implementación se describe en el artículo sobre cómo construir un servidor MCP para WooCommerce. La estrategia de autenticación se trata en la guía de patrones de autenticación MCP. El diseño de herramientas tipadas se trata en el artículo sobre herramientas de catálogo tipadas con Zod para MCP. La decisión a nivel de protocolo se trata en la comparación entre MCP y REST. La página del servicio es desarrollo de servidores MCP.
El presupuesto es individual, porque el alcance de la migración depende del número de endpoints de la auditoría, de la complejidad de la autenticación y del número de consumidores.
En la parte de implementación, este tema encaja en la migración a Next.js o Astro.







