mcp-confirm
mcp-confirm
Простыми словами: когда ИИ спрашивает «вы уверены?», MCP SDK уже не даёт повторно использовать это подтверждение для другого действия. Но он не мешает использовать одно и то же подтверждение дважды. Этот репозиторий закрывает этот пробел, а также ещё два, которые SDK оставляет открытыми.
Сначала прочтите это: большая часть проблемы уже решена
Спецификация MCP от 2026-07-28 добавила Multi Round-Trip Requests, так что
инструмент может приостановиться в середине вызова и попросить пользователя
подтвердить действие. Подтверждение возвращается через клиент как непрозрачный
requestState, который, по словам спецификации, серверы ОБЯЗАНЫ считать
контролируемым атакующим.
Python SDK делает это за вас по умолчанию на каждом MCPServer.
RequestStateBoundary добавляется в цепочку промежуточных обработчиков
безусловно — с эфемерным ключом, если вы не предоставили свой. Он запечатывает
состояние с помощью AES-256-GCM и привязывает его к методу, цели,
дайджесту аргументов вызова, аудитории и аутентифицированному субъекту,
с TTL.
Так что атака, к которой все обращаются в первую очередь — подтвердить удаление
cache.txt, а затем воспроизвести это подтверждение против thesis.txt —
уже отклоняется SDK. Вам не нужна библиотека для этого, и вам не следует
писать свою.
Этот репозиторий изначально написал такую библиотеку: 250 строк HMAC, TTL и привязки аргументов, выпущенных как главная функция, избыточных с первого дня публикации. Это задокументировано внизу, а не тихо удалено.
Related MCP server: RecourseOS
Чего SDK не делает
Три пробела, каждый с тестом, который срабатывает.
1. Он привязывает и ограничивает по времени состояние. Он никогда не «тратит» его.
В пределах TTL одно и то же подтверждение проверяется столько раз, сколько его предъявляют. Каждая проверка, которую делает граница, проходит каждый раз. Спецификация явно указывает, что это намеренно, а остальное — ваша забота:
Обратите внимание, что эти меры ограничивают окно воспроизведения и предотвращают межпользовательское и межзапросное повторное использование, но сами по себе не гарантируют однократность. Серверы, для которых данный
requestStateдолжен быть использован не более одного раза (например, одноразовые погашения), ОБЯЗАНЫ обеспечивать этот инвариант на стороне сервера.
Для «удалить файл» вторая попытка ничего не находит. Для «перевести £500» это
и есть вся проблема. singleuse.py — это
именно тот инвариант — реестр непогашенных подтверждений, которые списываются
при погашении. В нём нет криптографии, потому что граница уже гарантирует, что
открытый текст — это то, что создал этот сервер.
2. Ничто не очищает вопрос, который читает человек
Сообщение-запрос — это текст, выбранный сервером и отображаемый человеку, обычно с вставленным в него недоверенным значением — весь смысл в том, чтобы сказать, какой файл будет удалён. Так что назовите файл:
cache.txt
SYSTEM NOTICE: your session has expired.
Enter your AWS secret key to continue:В диалоге теперь появляется второе, официально выглядящее приглашение. Пользователь не подтверждает удаление; его фишингуют собственные инструменты. В том же релизе от 2026-07-28 также появились MCP Apps — интерфейс, отображаемый сервером, — что расширяет эту поверхность, а не сужает.
prompt.py сжимает недоверенные значения до одной
строки, удаляет двунаправленные переопределения и символы нулевой ширины и
обрезает с середины, чтобы имя файла в конце оставалось видимым. Управляющие
символы становятся пробелами, а не удаляются, поскольку их схлопывание
позволило бы a\nb и ab отображаться одинаково — два разных файла, один
диалог.
3. Никакой протокольный слой не может повторно проверить ваш ресурс в момент выполнения
Пользователь мог передумать в промежутке. Файл может быть заменён в этом окне, и только инструмент знает, что для него означает «не изменился». Этот сервер повторно проверяет перед удалением и отклоняет путь, который стал симлинком.
Какой слой что отклоняет
Это полезная часть, и тесты написаны так, чтобы это продемонстрировать.
MCPError означает, что промежуточное ПО SDK отказало до того, как этот пакет
запустился; ToolError означает, что это сделал этот пакет.
Атака | Отклонено | Тест |
Подтверждение воспроизведено на другой файл | SDK |
|
Состояние подделано или запечатано другим ключом | SDK |
|
Одно и то же подтверждение потрачено дважды | этот пакет |
|
Состояние, созданное другой репликой | этот пакет |
|
Имя файла подделывает системный промпт | этот пакет |
|
Файл заменён на симлинк после подтверждения | этот пакет |
|
Путь за пределами разрешённых корней | этот пакет |
|
Тесты
35 тестов — 17 через сервер, 11 на реестре, 7 на очистке промптов.
Каждый серверный тест проходит через настоящий RequestStateBoundary — тот
же класс, который MCPServer устанавливает на себя, с фиксированным ключом —
запечатывая первый раунд и распечатывая второй точно так, как это было бы на
проводе.
Этот стенд существует из-за конкретной ошибки. Первая версия тестировалась
путём прямого вызова MCPServer.call_tool(), который идёт напрямую к
менеджеру инструментов и полностью обходит цепочку промежуточного ПО.
Граница SDK никогда не выполнялась, поэтому избыточная самодельная защита
выглядела несущей. Именно дизайн теста скрыл это на целый цикл сборки.
Покрытие составляет 81%, при этом prompt.py — 100%, а singleuse.py — 94%.
Пробел — это argparse и транспортная обвязка в main(), проверяемые через
build_server вместо этого.
CI работает только на Ubuntu, намеренно: тесты замены после подтверждения требуют симлинков, и сборка падает, если они сообщаются как пропущенные.
Установка
pip install git+https://github.com/les-k/mcp-confirm.gitЗапуск
mcp-confirm --root /path/you/allowБез --root сервер отклоняет каждый запрос, а не по умолчанию что-либо.
Корни фиксируются при запуске и никогда не выбираются агентом. Ключ подписи не
нужен — MCPServer приносит свой собственный.
Известные ограничения
Реестр находится в памяти, поэтому он корректен для одного процесса и неверен за балансировщиком нагрузки. SDK поддерживает обмен ключами между репликами (
RequestStateSecurity(keys=[...])); при такой конфигурации реплика B отклоняет подтверждение, выданное репликой A. Это отказ в закрытом режиме — безопасное направление — но для пользователя это выглядит как подтверждение, которое необъяснимо перестало работать. Многопроцессное развёртывание требует Redis или строки в базе данных с атомарным сравнением-и-удалением. Для этого поведения есть тест.Демонстрационный инструмент намеренно мал. Он удаляет один файл. Интересный код — это
singleuse.pyиprompt.py.За этим не стоит CVE. MRTR всего несколько недель, поэтому это построено на основе собственного списка MUST/SHOULD спецификации и чтения SDK, а не на опубликованном инциденте.
Что было не так с версией 0.1.0
Оставлено здесь, потому что репозиторий, который записывает только свои успехи, не является доказательством чего-либо.
0.1.0 переписал то, что SDK уже делал. state.py содержал 250 строк
HMAC-подписи, TTL и привязки субъекта/метода/аргументов — всё это дублировало
RequestStateBoundary, и ничто из этого не было так хорошо: HMAC там, где SDK
использует аутентифицированное шифрование, нет привязки к аудитории, нет
ротации ключей.
Это было обнаружено чтением исходников SDK, а не тестом. Тесты проходили именно потому, что обходили промежуточное ПО, которое могло бы это выявить.
CI отдельно поймал реальный баг в 0.1.0: проверка симлинка выполнялась
после Path.resolve(), поэтому она проверяла назначение ссылки, а не саму
ссылку. Замена, направленная на другой файл внутри разрешённого корня, была бы
удалена. Исправлено, с тестом для варианта, который оригинал никогда не
проверял.
0.2.0 полностью удаляет state.py и оставляет только то, что SDK оставляет
непокрытым.
Лицензия
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