Patrones de autenticación MCP: OAuth, tokens y cuándo usar cada uno
MCP define un perfil de autorización OAuth para transportes HTTP. Los servidores públicos de solo lectura pueden ser anónimos. Las herramientas protegidas requieren autenticación, autorización e identidad verificada. Los tokens propios de socios también necesitan un contrato explícito de compatibilidad con el cliente MCP.
Este artículo forma parte del pilar sobre desarrollo de servidores MCP.
TL;DR
- La lectura de datos públicos con rate limiting es el único caso legítimo de acceso anónimo.
- Los tokens de API con scopes, guardados con hash y con TTL corto, cubren B2B y headless.
- OAuth 2.1 con PKCE cubre los asistentes de consumo que actúan en nombre de un usuario autenticado.
- El rate limiting se asocia al principal; las herramientas que modifican estado reciben un bucket más estrecho.
- Cada evento de autenticación se registra: emitido, usado, revocado, rechazado.
Cómo elegir el método de autenticación MCP
Antes de elegir un patrón de autenticación, repaso siempre las mismas cinco preguntas:
- ¿Alguna herramienta modifica estado? Si es así, el acceso anónimo queda descartado.
- ¿El agente actúa en nombre de un usuario humano concreto? Si es así, OAuth.
- ¿El agente actúa como integración B2B sin usuario humano? Si es así, token de API con scopes.
- ¿La superficie de datos es accesible desde la Internet pública? Si es así, el rate limiting es obligatorio.
- ¿Se mezclan en un mismo servidor herramientas que modifican estado y herramientas de solo lectura? Si es así, scope de autenticación por herramienta, no por servidor.
El árbol se traduce en tres patrones:
| Patrón | Cuándo usarlo | Implementación |
|---|---|---|
| Anónimo + rate limit por IP | Catálogo público, solo lectura, carga acotada | El Worker solo comprueba el bucket de la IP |
| Token de API con scopes | Integración B2B, runtime de agente headless, sin usuario humano | Token opaco aleatorio, hash almacenado, caducidad y rotación |
| OAuth 2.1 + PKCE | Agente de consumo para un usuario autenticado | Authorization code flow estándar |
Los patrones no se excluyen. Un servidor de producción real suele ejecutar los tres, y cada herramienta lleva la etiqueta del scope que requiere.
Acceso MCP anónimo de solo lectura con rate limiting
Expón solo campos públicos del catálogo aprobados expresamente. La API de gestión WooCommerce wc/v3 requiere credenciales en el servidor y no es un origen público anónimo. Utiliza un endpoint público revisado o un adapter que excluya campos privados.
Implementación:
async function checkAnonymousRateLimit(request: Request, env: Env): Promise<boolean> {
const ip = request.headers.get("CF-Connecting-IP") ?? "unknown";
const { success } = await env.AUTH_RATE_LIMIT.limit({ key: `anonymous:${ip}` });
return success;
}60 peticiones por minuto por IP es mi valor por defecto. Un contador basado en KV es aproximado (las escrituras en KV son eventually consistent); para límites estrictos, Durable Objects o el binding nativo de Rate Limiting de Cloudflare son la herramienta adecuada.
Lo que este patrón no cubre: ninguna herramienta que devuelva datos específicos de un usuario, ninguna que modifique estado, ninguna que revele el stock de productos no públicos. Esas requieren un principal real.
Tokens de API con scopes para integraciones B2B con MCP
El caso B2B: la integración de un socio aporta un agente que llama a tu servidor MCP en nombre de la organización del socio, no de un usuario humano concreto. Ejemplos: un agente de sincronización de stock en un mayorista, una integración de marketplace en un agregador, un agente de analítica que informa de tendencias de pedidos al BI.
El flujo:
- Un administrador en el backend de WordPress crea un token con un nombre, un conjunto de scopes (
catalogue:read,inventory:read,orders:write) y una fecha de caducidad (por defecto, 90 días). - El token se muestra al administrador una sola vez y después se guarda como hash SHA-256 más el conjunto de scopes más la caducidad.
- El socio configura su cliente MCP con el token en la cabecera
Authorization: Bearer <token>. - El Worker calcula el hash del token entrante y lo busca en KV. Si el hash coincide, la caducidad está en el futuro y el scope que exige la herramienta está en el conjunto del token, la llamada continúa.
async function verifyApiToken(authHeader: string | null, env: Env): Promise<TokenContext | null> {
if (!authHeader?.startsWith("Bearer ")) return null;
const token = authHeader.slice(7);
const hash = await sha256(token);
const record = await env.TOKENS.get(`tok:${hash}`, "json") as TokenRecord | null;
if (!record) return null;
if (record.expiresAt < Date.now()) return null;
return { tokenId: record.id, scopes: record.scopes, principal: record.principal };
}
function requireScope(ctx: TokenContext, scope: string): void {
if (!ctx.scopes.includes(scope)) throw new Error("Insufficient scope");
}Dos hábitos operativos mantienen el patrón honesto:
Rotación, no eternidad. Un token que nunca caduca es un token que dentro de seis meses acaba en un repositorio público de GitHub. 90 días por defecto, con un flujo de renovación en el que el token antiguo y el nuevo conviven durante 7 días, es el patrón que sobrevive a socios reales.
Almacenamiento con hash, no en texto plano. Si tu KV se filtra, los hashes sin el token original no sirven para nada. Si guardaste los tokens en texto plano, todas las integraciones de socios necesitan una rotación inmediata. La diferencia de coste en el momento de la emisión es una llamada a sha256.
OAuth 2.1 con PKCE en un servidor MCP
El caso de consumo: el usuario abre Claude Desktop, conecta su cuenta de tu tienda mediante OAuth y le pide al agente “muéstrame mi último pedido”. El agente tiene que llamar ahora a tu servidor MCP con credenciales que digan “actúo en nombre del usuario 4231”.
OAuth 2.1 (draft-ietf-oauth-v2-1) es el perfil consolidado. PKCE (RFC 7636) es obligatorio para clientes públicos (aquí entran los asistentes de escritorio sin una parte de servidor confidencial).
El flujo en cinco pasos:
- El host MCP abre el navegador en tu endpoint de autorización con
response_type=code,code_challenge=<hash S256 del verifier>y los scopes solicitados. - El usuario inicia sesión en tu sitio WordPress (o en tu proveedor de autenticación) y acepta los scopes.
- Tu endpoint de autorización redirige al host con un código de autorización de un solo uso.
- El host intercambia el código más el
code_verifieroriginal en tu endpoint de tokens por un access token (TTL corto, 1 hora) y un refresh token (TTL más largo, 30 días). - El host llama al servidor MCP con
Authorization: Bearer <access_token>. El Worker verifica la firma del token, la caducidad y el scope frente a la herramienta invocada.
Un token OAuth puede ser opaco o JWT. El ejemplo utiliza un emisor configurado y la biblioteca jose mantenida para validar firma y claims:
import { createRemoteJWKSet, jwtVerify } from "jose";
const issuer = "https://identity.example.org";
const audience = "https://mcp.example.org/mcp";
const jwks = createRemoteJWKSet(new URL(issuer + "/.well-known/jwks.json"));
async function verifyJwt(token: string) {
const { payload } = await jwtVerify(token, jwks, {
issuer,
audience,
algorithms: ["RS256"],
requiredClaims: ["sub", "exp"],
});
if (typeof payload.sub !== "string" || typeof payload.scope !== "string") {
throw new Error("Invalid identity or scope claims");
}
return { subject: payload.sub, scopes: payload.scope.split(" ").filter(Boolean) };
}Los scopes del JWT reflejan los del patrón de token con scopes: catalogue:read, orders:read, orders:write. El ID del usuario de WordPress va en el claim sub, así que una llamada orders:read devuelve solo los pedidos de ese usuario.
Cómo combinar OAuth, tokens y acceso anónimo en un servidor MCP
Un servidor MCP real para WooCommerce suele exponer:
catalogue.listyproduct.detail: anónimo + rate limit por IP.inventory.check(para socios): token con scopeinventory:read.order.status(para el usuario autenticado): OAuth conorders:read.order.intent(para el usuario autenticado): OAuth conorders:write, más un rate limit más estrecho.
La lógica de pre-dispatch en el Worker recorre los patrones:
Autentica HTTP antes del dispatch MCP. Comprueba después los scopes de la herramienta y la propiedad del recurso. Rechaza herramientas desconocidas y políticas ausentes. Los fallos de autenticación requieren challenges bearer HTTP en vez de códigos JSON-RPC inventados como texto.
El mapa TOOL_AUTH_REQUIREMENTS es la única fuente de verdad sobre qué herramienta necesita qué modo de autenticación. Ninguna herramienta se añade sin una entrada explícita.
Límites de peticiones MCP por IP, token y usuario
El tráfico anónimo recibe un bucket por IP. El tráfico con token recibe un bucket por token. El tráfico OAuth recibe un bucket por usuario. Las herramientas que modifican estado reciben un techo más bajo sea cual sea el principal.
Los siguientes valores son ejemplos de configuración, no mediciones ni límites predeterminados de producción. Define los límites reales según la capacidad del origen y el tráfico observado.
| Categoría de herramienta | Anónimo | Token | OAuth |
|---|---|---|---|
catalogue.* (lectura) | 60 / minuto / IP | 600 / minuto / token | 120 / minuto / usuario |
inventory.* (lectura) | no permitido | 300 / minuto / token | no permitido |
order.status (lectura) | no permitido | 60 / minuto / token | 60 / minuto / usuario |
order.intent (escritura) | no permitido | 30 / minuto / token | 10 / minuto / usuario |
Las cifras son un punto de partida; los valores correctos salen de observar tráfico real durante dos semanas y ajustar. Lo que cuenta es la estructura.
Qué eventos de autenticación MCP registrar
Cada evento relevante para la autenticación acaba en el registro:
- Token emitido. Administrador, principal de destino, scopes, caducidad.
- Token usado. ID del token, nombre de la herramienta, principal, latencia, éxito/fallo.
- Token revocado. ID del token, quién lo revocó, por qué.
- Token rechazado. Motivo (caducado, falta scope, el hash no coincide), IP, user agent.
- Intercambio de código OAuth. ID del usuario, scopes concedidos, refresh token emitido.
- Refresh de OAuth. ID del usuario, nuevo access token, token antiguo sustituido.
Los registros van por Cloudflare Logpush a un almacenamiento a largo plazo. Una consulta de dashboard que sigue los “tokens usados en las últimas 24 horas que no se habían usado en los 90 días anteriores” detecta un probable robo de token. Una consulta de “verificaciones de token fallidas por IP” detecta intentos de credential stuffing.
Discovery e identidad del recurso protegido
La especificación de autorización MCP de 2025-11-25 define un flujo OAuth para transportes HTTP. La autorización es opcional, pero un servidor HTTP protegido e interoperable necesita discovery y validación conforme a ese perfil. Stdio tiene otra frontera de despliegue y obtiene credenciales del entorno. Los tokens propios pueden servir a una integración controlada con socios. No presupongas que cualquier host MCP sabe descubrir u obtener esas credenciales. Prueba el host y el transporte que se utilizarán.
Separa los roles del servidor de recursos MCP y del servidor de autorización, aunque compartan aplicación. El primero ejecuta herramientas y valida el acceso. El segundo gestiona login, consentimiento y emisión de tokens. Publica metadatos del recurso protegido con su identidad canónica y los servidores de autorización. Una respuesta HTTP 401 puede señalar esos metadatos mediante WWW-Authenticate. Las direcciones deben funcionar detrás del reverse proxy y con un prefijo de ruta. Que el navegador muestre una página de login no demuestra que el host haya encontrado al emisor correcto.
El cliente solicita un access token destinado al recurso MCP mediante el parámetro resource. El servidor valida esa asociación. Un token emitido para otra API no resulta aceptable solo porque lo firmó correctamente un emisor conocido. Rechaza audience e issuer inesperados antes de ejecutar herramientas. Usa el mismo URI canónico en metadatos, solicitud de autorización y validación. No derives la audience esperada de un encabezado Host no fiable ni de un parámetro libre del cliente.
Validación completa y credenciales de servicios externos
Los access tokens OAuth pueden ser opacos o JWT. El protocolo no obliga a todos los emisores a usar JWT. Un token opaco requiere el mecanismo de verificación admitido por su emisor. Un verificador JWT comprueba firma, algoritmo permitido, issuer, audience y fechas de validez. Después valida los claims de aplicación que utiliza. Una aserción de tipos TypeScript no hace comprobaciones en runtime. JSON malformado, ausencia de subject o scope con tipo incorrecto debe causar una denegación controlada. Usa una biblioteca mantenida y una fuente de confianza configurada en vez de un verificador parcial con Web Crypto.
Obtén las claves de la dirección JWKS configurada para el emisor, no de una URL arbitraria incluida en el token. Prepara la rotación de claves y la indisponibilidad del emisor. Un fallo al obtener una clave no puede admitir un token sin verificar. Una clave fiable ya guardada puede funcionar según la política definida, pero una clave desconocida exige una decisión explícita. Prueba tokens caducados, todavía no válidos, algoritmos distintos y tokens de otro entorno. Las credenciales de staging no deben funcionar por accidente en producción.
Las directrices de seguridad MCP prohíben token passthrough. Un bearer token dirigido al servidor MCP no puede enviarse a WooCommerce o ERP como credencial de esos servicios. Guarda las claves de origen en el servidor y autoriza la acción antes de utilizarlas. Si el servicio requiere identidad delegada, establece su flujo compatible de delegación o intercambio de tokens. Copiar el encabezado Authorization recibido no constituye un modelo de delegación.
Scopes y propiedad de los pedidos
Autentica la petición HTTP y comprueba después los scopes de la operación. Una verificación de token correcta no elimina la comprobación de permisos de la herramienta. El registro debe indicar los derechos de cada operación. Una entrada desconocida se rechaza. Los cambios requieren pruebas de llamadas permitidas y denegadas. La lista de discovery también necesita una política para descripciones y parámetros sensibles. Conocer el nombre de una herramienta no concede el derecho a ejecutarla.
orders:read define una categoría de acciones, no el propietario del pedido. Asocia el subject verificado a una cuenta interna mediante un mapeo fiable y comprueba la propiedad al cargar el pedido. Un identificador de cliente enviado por el agente no demuestra autorización. El socio con acceso a toda una organización necesita una concesión separada y una frontera de tenant definida. Un distribuidor español que importa existencias y un cliente que consulta la entrega tienen identidades diferentes aunque accedan al mismo servidor MCP.
Gestiona los fallos de bearer authentication en la capa HTTP correspondiente. Un token ausente o inválido requiere denegación de autenticación. Los scopes insuficientes necesitan el challenge adecuado. Un access token caducado no debe parecer un resultado comercial de falta de existencias. Separar estos casos permite al host renovar acceso o solicitar consentimiento, en vez de repetir una acción con credenciales inalteradas.
Revocación, cuotas y pruebas de publicación
Genera tokens opacos de socios con aleatoriedad criptográfica suficiente. Guardar hashes ayuda solo cuando el secreto original es difícil de adivinar. Separa el identificador público del secreto y usa una implementación de verificación revisada. Define la propagación de la revocación. Workers KV tiene consistencia eventual, por lo que eliminar un registro no garantiza revocación global inmediata. Las operaciones sensibles pueden necesitar una consulta a la fuente autoritativa u otra frontera de consistencia. Una caducidad corta limita exposición, pero no sustituye revocación.
El rate limiting también tiene un modelo de consistencia. El binding de rate limiting Workers limita volumen de peticiones, pero no es un presupuesto transaccional global para pedidos u operaciones pagadas. Utiliza un coordinador autoritativo adecuado para cuotas comerciales. Leer y después escribir en KV no constituye un incremento atómico. Las peticiones concurrentes pueden perder actualizaciones. Los reintentos del agente no deben generar más operaciones que la acción prevista.
Antes de publicar, prueba discovery desde el hostname de producción, consentimiento, caducidad, refresh, revocación y scopes insuficientes con el host MCP elegido. Incluye acceso al pedido de otro cliente y a otro tenant. Confirma que los resultados privados no llegan a una caché compartida. Registra identificador de token, identidad, herramienta y resultado sin secretos ni payloads de clientes. Separa eventos de seguridad de trazas de diagnóstico y define la retención de cada almacén. Un login correcto es solo el comienzo de la prueba de control de acceso.
Temas relacionados
Este artículo trata la autenticación. El refuerzo más amplio de WordPress está en la guía de seguridad de WordPress. La página del servicio es desarrollo de servidores MCP.
Presupuesto individual, porque el alcance de la autenticación depende de los patrones que exige tu entorno; un servidor de solo lectura en modo anónimo es un proyecto distinto de una superficie completa que emite OAuth.







