# Errores y eventos

> Cada código de error JSON-RPC y estado HTTP que devuelve MCP Shield, por qué, y el evento que deja cada uno.

En modo aplicar MCP Shield responde él mismo a algunos mensajes. Esta página recoge cada respuesta, por qué tiene esa forma y el evento que deja. En modo registro no se envía ninguna: el mensaje pasa y solo queda el evento, con `blocked: false`.

## Por qué casi todos los bloqueos son errores JSON-RPC

Los clientes MCP tratan un estado HTTP de error como un fallo del transporte: la llamada en curso falla, y algunos clientes tiran o reintentan la sesión entera. Un **error JSON-RPC con el id de la petición** se entrega en cambio a esa llamada concreta. El asistente enseña «blocked by OpenWAF MCP Shield: …» en esa llamada a la herramienta y la sesión sigue funcionando.

Por eso los bloqueos de política y de revisión responden HTTP `200` con un error JSON-RPC. Solo se usa un estado HTTP de error cuando no hay id de petición al que responder (un cuerpo que no se puede leer) o cuando el problema es el cliente entero (ritmo y tamaño).

## Todas las respuestas

| Situación (aplicar) | HTTP | Código JSON-RPC | Tipo de evento | Id de detección |
|---|---|---|---|---|
| Más de `rate_per_token` peticiones por minuto | `429` con `Retry-After` | `-32000` | `mcp_rate_limited` | `mcp.rate_limited` |
| Cuerpo de más de `max_request_kb` | `413` | `-32600` | `mcp_invalid` | `mcp.too_large` |
| El cuerpo no es JSON | `400`, id `null` | `-32700` | `mcp_invalid` | `mcp.parse_error` |
| JSON pero no JSON-RPC 2.0, o un lote vacío | `400`, id `null` | `-32600` | `mcp_invalid` | `mcp.invalid_request` |
| Método fuera de `methods_allowed` | `200` | `-32601` | `mcp_blocked_method` | `mcp.method_not_allowed` |
| Herramienta en `tools_denied`, o ausente de un `tools_allowed` no vacío | `200` | `-32602` | `mcp_blocked_tool` | `mcp.tool_denied`, `mcp.tool_not_allowed` |
| Inyección o credencial en la petición, `inspect_requests: block` | `200` | `-32001` | `mcp_injection_request`, `mcp_dlp` | Los ids de los detectores |
| Inyección o credencial en la respuesta, `inspect_responses: block` | El de tu servidor | `-32001`, en lugar de ese mensaje de respuesta (mismo id; en un flujo, ese evento) | `mcp_injection_response`, `mcp_dlp` | Los ids de los detectores |
| Una notificación bloqueada | `202`, sin cuerpo | — | `mcp_blocked_method` | `mcp.method_not_allowed` |

Las peticiones que el servidor manda al cliente (`sampling/createMessage`, `elicitation/create`) solo se anotan; nunca se sustituyen.

## Qué recibe el cliente

El mensaje es `blocked by OpenWAF MCP Shield:` seguido de los ids de detección separados por comas. `error.data.openwaf` repite el tipo de evento y los ids, para que un cliente o un agente distinga un bloqueo del escudo de un error de tu servidor:

```json
{"jsonrpc": "2.0", "id": 7,
 "error": {"code": -32602, "message": "blocked by OpenWAF MCP Shield: mcp.tool_denied",
           "data": {"openwaf": {"kind": "mcp_blocked_tool", "detections": ["mcp.tool_denied"]}}}}
```

Un resultado de herramienta sustituido por una detección tiene la misma forma, con el código `-32001`, el id de la petición original y los ids de los detectores, por ejemplo `blocked by OpenWAF MCP Shield: pi.override.en, exfil.markdown_image`. El límite de ritmo responde `-32000` con el mensaje `rate limit exceeded (OpenWAF MCP Shield)`.

## Lotes

Si se bloquea algún mensaje de un lote, el borde responde el lote entero y no llega nada a tu servidor. La respuesta es un array con `200`: cada mensaje bloqueado lleva su error y cada una de las demás peticiones con id lleva `-32600` `batch rejected by OpenWAF MCP Shield`. Un lote sin ids de petición (solo notificaciones) recibe `202`.

## Eventos

Cada mensaje bloqueado o con detecciones deja un evento entre los de Bots y agentes de IA, uno por tipo y mensaje (muestreados a como mucho 200 por sitio y minuto):

| Tipo | Cuándo |
|---|---|
| `mcp_blocked_method` | Un método fuera de `methods_allowed` |
| `mcp_blocked_tool` | Una herramienta denegada o no permitida |
| `mcp_injection_request` | Un detector de inyección encontró algo en la petición |
| `mcp_injection_response` | Un detector de inyección encontró algo en la respuesta |
| `mcp_dlp` | Un patrón de credenciales encontró algo en la petición o en la respuesta |
| `mcp_rate_limited` | Se pasó el límite por token |
| `mcp_invalid` | El cuerpo era demasiado grande, no era JSON o no era JSON-RPC |

Cada evento lleva las detecciones (id, etiqueta y un fragmento de como mucho 160 bytes, redactado en las credenciales) y un objeto `mcp`:

| Campo | Qué es |
|---|---|
| `method` | El método JSON-RPC, vacío si no se conoce (cuerpo no válido, flujo `GET`) |
| `tool` | El nombre de la herramienta de `tools/call`, la URI de `resources/read` (recortada) o el nombre del prompt |
| `token_fp` | Una huella de la cabecera `Authorization`: los 8 primeros bytes de su SHA-256, en hexadecimal. Nunca el token |
| `blocked` | `true` si el borde respondió o sustituyó el mensaje, `false` si solo se anotó |

Cada uno es también un evento de seguridad con la regla **MCP Shield**, acción `block` o `log`, en la vista de eventos en directo y en **Control de rastreadores IA → MCP Shield**. Desde la CLI:

```sh
openwaf-cloud ctl ai report example.com --view events --kind mcp_injection_response,mcp_dlp --limit 50
```

## Contadores

Por endpoint, el informe cuenta las peticiones, los mensajes JSON-RPC del cliente, los mensajes bloqueados (respondidos con un error del escudo o respuestas sustituidas), los mensajes con al menos una detección de inyección o de credenciales y las respuestas que pasaron **sin revisar** (demasiado grandes o todavía comprimidas). Por herramienta, cuenta llamadas, bloqueos y detecciones, hasta 50 herramientas por endpoint; el resto se suma en `*`. Los datos se guardan 30 días.
