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: approval-gate
Чего 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 deployed
Maintenance
Related MCP Connectors
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Security & DLP proxy for MCP: tool-poisoning scans, PII redaction on tool args/results. Beta.
Find, vet, and run MCP tools through a secure audited gateway with prompt-injection risk scoring
Email safety MCP server. Detects phishing, prompt injection, CEO fraud for AI agents.
Related MCP Servers
- AlicenseAqualityAmaintenanceAn MCP server that provides safeguard capabilities to protect against prompt injection and unsafe tool calls.7111 PyPI11MIT
- 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.-
- AlicenseNot gradedqualityAmaintenanceMCP server that gates agent tool calls by normalizing intent, denying unknown/unattended destructive actions, and requiring HITL prove approval for high-risk operations. It maintains an append-only hash-chained Action Ledger and exposes gate_check and ledger_verify tools without ever executing tools.MIT