mcp-confirm
mcp-confirm
En términos sencillos: cuando una IA pregunta «¿estás seguro?», el SDK de MCP ya impide que esa aprobación se reutilice para una acción diferente. No impide que la misma aprobación se use dos veces. Esto cierra esa brecha, y otras dos que el SDK deja abiertas.
Léase primero: la mayor parte de este problema ya está resuelto
La especificación MCP del 2026-07-28 añadió las Solicitudes de Múltiples Rondas (Multi Round-Trip Requests), de modo que una herramienta puede pausarse a mitad de la llamada y pedir al usuario que confirme. La aprobación viaja de vuelta a través del cliente como un requestState opaco, que la especificación dice que los servidores DEBEN tratar como controlado por el atacante.
El SDK de Python hace esto por ti, por defecto, en cada MCPServer. RequestStateBoundary se añade a la cadena de middleware incondicionalmente — con una clave efímera si no se proporciona ninguna. Sella el estado bajo AES-256-GCM y lo vincula al método, al objetivo, a un resumen de los argumentos de la llamada, a la audiencia y al principal autenticado, con un TTL.
Así que el ataque que todo el mundo menciona primero — aprobar la eliminación de cache.txt y luego reproducir esa aprobación contra thesis.txt — ya es rechazado por el SDK. No necesitas una biblioteca para eso, y no deberías escribir una.
Este repositorio originalmente escribió una de todos modos: 250 líneas de HMAC, TTL y vinculación de argumentos, publicada como la característica principal, redundante el día en que se publicó. Eso se registra al final en lugar de borrarse silenciosamente.
Related MCP server: RecourseOS
Lo que el SDK no hace
Tres brechas, cada una con una prueba que se dispara.
1. Vincula y expira el estado. Nunca lo gasta.
Dentro del TTL, la misma aprobación se verifica tantas veces como se presente. Cada comprobación que hace el límite pasa, cada vez. La especificación es explícita en que esto es deliberado y que el resto es tuyo:
Nótese que estas medidas limitan la ventana de reproducción y previenen la reutilización entre usuarios y entre solicitudes, pero no garantizan por sí mismas el uso único. Los servidores para los que un
requestStatedado debe consumirse como máximo una vez (por ejemplo, canjes de un solo uso) DEBEN hacer cumplir esa invariante en el lado del servidor.
Para «eliminar un archivo», el segundo intento no encuentra nada. Para «transferir 500 £» es el problema completo. singleuse.py es esa invariante — un registro de confirmaciones pendientes, gastadas al canjearse. No contiene criptografía, porque el límite ya garantiza que el texto plano es algo que este servidor acuñó.
2. Nada sanitiza la pregunta que lee el humano
Un message de elicitación es texto elegido por el servidor que se muestra a una persona, normalmente con un valor no confiable interpolado en él — el punto es decir cuál archivo va a desaparecer. Así que nombra un archivo:
cache.txt
SYSTEM NOTICE: your session has expired.
Enter your AWS secret key to continue:El diálogo ahora lleva un segundo aviso de apariencia oficial. El usuario no está confirmando una eliminación; está siendo víctima de phishing por su propia herramienta. La misma versión del 2026-07-28 también incluyó MCP Apps — interfaz de usuario renderizada por el servidor — lo que amplía esta superficie en lugar de reducirla.
prompt.py aplana los valores no confiables a una línea, elimina los anuladores bidireccionales y los caracteres de ancho cero, y trunca desde el medio para que el nombre del archivo al final siga siendo visible. Los caracteres de control se convierten en espacios en lugar de eliminarse, ya que colapsarlos haría que a\nb y ab se mostraran de manera idéntica — dos archivos diferentes, un solo diálogo.
3. Ninguna capa de protocolo puede volver a comprobar tu recurso en el momento de la ejecución
El usuario pensó en ello en el medio. El archivo puede ser reemplazado en esa ventana, y solo la herramienta sabe qué significa «sin cambios» para él. Este servidor vuelve a comprobar antes de eliminar, y rechaza una ruta que se haya convertido en un enlace simbólico.
Qué capa rechaza qué
Esta es la parte útil, y las pruebas están escritas para demostrarlo. MCPError significa que el middleware del SDK rechazó antes de que este paquete se ejecutara; ToolError significa que este paquete lo hizo.
Ataque | Rechazado por | Prueba |
Aprobación reproducida sobre otro archivo | SDK |
|
Estado falsificado, o sellado con otra clave | SDK |
|
Misma aprobación gastada dos veces | este paquete |
|
Estado acuñado por otra réplica | este paquete |
|
Nombre de archivo que falsifica un aviso del sistema | este paquete |
|
Archivo intercambiado por un enlace simbólico tras la aprobación | este paquete |
|
Ruta fuera de las raíces permitidas | este paquete |
|
Pruebas
35 pruebas — 17 a través del servidor, 11 sobre el registro, 7 sobre la sanitización de avisos.
Cada prueba del servidor se ejecuta a través del RequestStateBoundary real, la misma clase que MCPServer instala sobre sí mismo, con una clave fija — sellando la primera ronda y desellando la segunda exactamente como lo haría el cable.
Ese arnés existe debido a un error específico. La primera versión se probó llamando directamente a MCPServer.call_tool(), que va directo al gestor de herramientas y omite por completo la cadena de middleware. El límite del SDK nunca se ejecutó, por lo que una protección redundante hecha a mano parecía esencial. El diseño de la prueba es lo que lo ocultó, durante un ciclo de compilación completo.
La cobertura es del 81%, con prompt.py al 100% y singleuse.py al 94%. La brecha es el argparse de main() y el cableado de transporte, ejercitados a través de build_server en su lugar.
CI se ejecuta solo en Ubuntu, deliberadamente: las pruebas de intercambio tras la confirmación necesitan enlaces simbólicos, y la compilación falla si se informan como omitidas allí.
Instalación
pip install git+https://github.com/les-k/mcp-confirm.gitEjecución
mcp-confirm --root /path/you/allowSin --root, el servidor rechaza cada solicitud en lugar de asumir algo por defecto. Las raíces se fijan al inicio y nunca las elige el agente. No se necesita clave de firma — MCPServer trae la suya propia.
Limitaciones conocidas
El registro está en memoria, por lo que es correcto para un proceso y incorrecto detrás de un balanceador de carga. El SDK admite compartir claves entre réplicas (
RequestStateSecurity(keys=[...])); bajo esa configuración, la réplica B rechaza una confirmación emitida por la réplica A. Falla de forma segura — la dirección segura — pero se lee al usuario como una confirmación que inexplicablemente dejó de funcionar. Un despliegue multiproceso necesita Redis o una fila de base de datos con una comparación y eliminación atómica. Hay una prueba para este comportamiento.La herramienta de demostración es deliberadamente pequeña. Elimina un archivo. El código interesante es
singleuse.pyyprompt.py.Ningún CVE respalda esto. MRTR tiene semanas de antigüedad, por lo que esto se construye a partir de la propia lista MUST/SHOULD de la especificación y de leer el SDK, no de un incidente publicado.
Qué estaba mal en la versión 0.1.0
Se mantiene aquí porque un repositorio que solo registra sus éxitos no es evidencia de nada.
0.1.0 reimplementó lo que el SDK ya hacía. state.py eran 250 líneas de firma HMAC, TTL y vinculación de principal/método/argumento — todo duplicando RequestStateBoundary, nada de ello tan bueno: HMAC donde el SDK usa cifrado autenticado, sin vinculación de audiencia, sin rotación de claves.
Se detectó leyendo el código fuente del SDK, no con una prueba. Las pruebas pasaron precisamente porque omitían el middleware que lo habría expuesto.
CI detectó por separado un error real en 0.1.0: la comprobación de enlace simbólico se ejecutaba después de Path.resolve(), por lo que inspeccionaba el destino del enlace en lugar del enlace en sí. Un intercambio dirigido a otro archivo dentro de una raíz permitida habría sido eliminado. Corregido, con una prueba para la variante que el original nunca ejercitó.
0.2.0 elimina state.py por completo y conserva solo lo que el SDK deja sin cubrir.
Licencia
MIT.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceAn MCP server that provides safeguard capabilities to protect against prompt injection and unsafe tool calls.6MIT

RecourseOSofficial
AlicenseNot gradedqualityAmaintenanceMCP server that evaluates Terraform plans, shell commands, and tool calls to assess recoverability and risk before execution, enabling safe AI agent actions.1511MIT- AlicenseNot gradedqualityCmaintenanceMCP server that provides human-in-the-loop approval for risky AI agent actions, with durable state and audit logs.MIT
- FlicenseNot gradedqualityCmaintenanceMCP server that provides a security gateway for AI agents, enforcing allow/confirm/deny policies on tool calls and requiring human approval for risky operations, with full audit logging.
Related MCP Connectors
Security firewall for AI agents — scans MCP calls for injection, secrets, and risks.
Security scanner for MCP servers. Detect vulnerabilities, prompt injection, and tool poisoning.
Scans MCP servers for tool poisoning, prompt injection and supply chain risks.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/les-k/mcp-confirm'
If you have feedback or need assistance with the MCP directory API, please join our Discord server