mcp-permission-server
mcp-permission-server
Уровень разрешений перед инструментами MCP и журнал, который может доказать, что он решил.
Каждый журнал аудита фиксирует, что произошло. Почти ни один не фиксирует достаточно, чтобы пересчитать, почему было разрешено, и большинство фиксирует только успешные действия — поэтому, когда кто-то спрашивает «как агент добрался до этого файла», журнал не может ответить. Он может лишь повторить утверждение, что решение было принято.
NAIVE GUARDED
calls in the session 17 17
calls that must be denied 8 8
EXECUTED WITHOUT A GRANT 7 0
LEGITIMATE CALLS REFUSED 0 0 ← no over-blocking
DECISIONS THAT CANNOT BE 16 0 ← the number nobody reports
RECONSTRUCTED FROM THE LOG
denials that left no trace 1 0
secrets sitting in the log 2 0
entry removable unnoticed 1 0NAIVE — это модель разрешений, с которой поставляется MCP-сервер, когда разрешения
не были в центре внимания: список разрешённых инструментов и журнал того, что
успешно выполнено. Каждый вызов в этой сессии, кроме одного, проходит.
python3 demo.py
python3 gate.py --log audit.json # run the session
python3 gate.py --verify audit.json # re-decide every entry from the log alonechain: OK - the chain is intact
replay: 17/17 entries recompute to the verdict they record
secrets in the log: noneКод выхода: 0 — всё разрешено, 1 — что-то отклонено, 2 — журнал не проверяется.
Грант — это четыре вещи одновременно
То есть четыре отдельные причины, по которым живой грант всё ещё может не покрывать вызов, — и вопрос «есть ли у этого вызывающего разрешение на этот инструмент» — это неправильный вопрос.
{"id": "G1", "tool": "fs.read", "scope": "/data/reports",
"purpose": "q3-report", "expires_at": 100, "max_uses": 10}вызов | что с ним не так | что ещё могло бы его разрешить |
| другой каталог, начинающийся с тех же символов | инструмент ✓ область действия ✓ срок действия ✓ |
| тот же каталог, достигнутый через разрешённый | инструмент ✓ область действия ✓ срок действия ✓ |
| срок действия гранта истёк сорок тиков назад. С самим вызовом всё в порядке | инструмент ✓ область действия ✓ назначение ✓ |
третий | грант — это ещё и бюджет, а не только разрешение | инструмент ✓ область действия ✓ назначение ✓ |
| идентичен первому вызову в сессии, кроме причины, по которой он выполняется | инструмент ✓ область действия ✓ срок действия ✓ |
| шаблон подтягивает | инструмент ✓ область действия ✓ назначение ✓ срок действия ✓ |
| владение одним ключом — это не владение связкой ключей | инструмент ✓ назначение ✓ срок действия ✓ |
Есть тест, который для каждого из этих случаев отключает только ту проверку, что названа, и требует, чтобы вызов прошёл. Отказ, который ловят несколько проверок, — это отказ, который ничего не доказывает ни об одной из них.
Related MCP server: MCP Airlock
Запутанный посредник
render_template разрешён. /templates/leak.md находится внутри разрешённой области.
Шаблон содержит {{include:/etc/service-token}}.
Сервер обладает правами, которых нет у вызывающего, и его просят потратить их на ресурс, который вызывающий никогда не называл. Уровень разрешений, который проверяет ресурс в аргументах, проверяет не то именно для тех инструментов, где это важно.
Поэтому проверка смотрит на то, чего достигает вызов, до того, как он этого достигает, — и затем
всё равно говорит «да» для /templates/report.md, который достигает
/data/reports/q3.csv, на который у этого вызывающего действительно есть грант. Половина
проверки разрешений — это «да». Правило, которое блокирует каждый шаблон, потому что
шаблоны могут включать в себя что угодно, — это не правило.
Путевое вхождение — вот где обычно ошибаются
Обе ошибки — длиной в одну строку.
path.startswith(scope) # /data/reports-private is "inside" /data/reports
".." in path # and this breaks ordinary callers to catch nothingВторой случай заслуживает отдельного разбора. /data/reports/../reports/q3.csv
разрешается обратно внутрь разрешённой области, и в корпусе вызовов он есть
и должен быть разрешён. Запрет символов — это дешёвое исправление; разрешение
пути ловит всё, что запрет ловит, и ничего из того, что запрет не ловит.
Разрешение здесь — лексическое и никогда не касается файловой системы — есть тест,
который ищет realpath, os.stat и open(. Решение принимается до
вызова, на пути, который указал вызывающий, и оно не должно зависеть от того,
что произойдёт дальше.
Журнал — это продукт
Два свойства делают его доказательством, а не просто записями.
Он фиксирует отказы. Журнал того, что прошло, не может ответить на вопрос «что пытался сделать агент» — а это именно то, что спрашивают потом. Восемь из семнадцати записей — это отказы, и каждая содержит проверку, которая сработала, и причину на простом языке.
Его можно воспроизвести. Каждая запись несёт в себе грант, на основании которого принималось решение, и версию политики, так что решение можно пересчитать из журнала и сравнить с вердиктом:
python3 gate.py --verify audit.json
replay: 17/17 entries recompute to the verdict they recordПри выключенном поле replay это число — 0/17 — записи просто говорят,
что было решено, и не дают никому возможности это проверить.
А записи связаны хеш-цепочкой, так что удаление или изменение одной видно:
entry 6 does not follow the one before it -- something was removed or editedСекреты в него не попадают. Журнал аудита, хранящий секрет, — это вторая копия
секрета, в файле, который читает больше людей. secrets.get разрешён в этой
сессии, и его значение не появляется в журнале; наивный журнал содержал бы два.
Девять проверок и доказательство, что каждая на своём месте
check what turning it off lets through
grant 1 more unauthorised, 1 more unreconstructable, 1 more egress
scope 3 more unauthorised, 3 more unreconstructable
expiry 2 more unauthorised, 2 more unreconstructable
purpose 1 more unauthorised, 1 more unreconstructable
deputy 1 more unauthorised, 1 more unreconstructable
redact 1 more secret in the log
deny_log 8 more denials that left no trace
replay 17 more decisions that cannot be reconstructed
chain 1 more entry removable unnoticedpython3 ablate.pyОдна поправка, которую стоит записать: первая версия абляции scope не
отключала проверку — она подменяла сравнение строк на сравнение по префиксу, а это
другая проверка, а не отсутствие проверки, и она по-тихому продолжала
отклонять вызов, который должен был доказать, что проверка нужна. Отключить проверку —
значит действительно её отключить, иначе абляция измеряет не то.
Тот же эксперимент показал, что грант, рассчитанный так, что последний вызов был отклонён за превышение лимита использований, а не за то, что он не покрыт по имени ключа, — это была не та проверка, и она проходила. Исправление было в самой тестовой обвязке, и тест, который нашёл эту ошибку, — это тот, который требует, чтобы каждый отказ изолированно проверял свою собственную проверку.
Известные ограничения, заявленные открыто, а не скрытые
Это уровень решений, а не транспорт. Он принимает решения, исполняет их через внутрипроцессную таблицу инструментов и ведёт журнал. Подключение его к реальному MCP-транспорту — это обычная работа; то, что здесь есть, — это та часть, которой обычно не хватает, и она работает без сети и без ключей.
Гранты записываются, а не извлекаются. Реальное развёртывание спрашивает человека и сохраняет ответ. То, что должен содержать ответ, — инструмент, область действия, назначение, срок действия, счётчик использований, — это та часть, о которой здесь идёт спор.
purposeобъявляется вызывающим и принимается как есть. Вызывающий, который лжёт о своём назначении, обходит проверку. Но это всё равно даёт что-то реальное: объявленное назначение попадает в журнал, так что ложь остаётся в записи, и область действия гранта от этого не расширяется.Проверка посредника знает охват одного инструмента.
render_templateразбирает свои включения. Инструмент, охват которого сервер не может вычислить, нельзя проверить таким способом, и честный ответ для такого инструмента — более узкий грант, а не более широкая проверка.Цепочка доказывает порядок и целостность, а не хранение. Любой, кто может переписать весь файл, может переписать всю цепочку. Это делает выборочное изменение видимым — а это реальная угроза; append-only хранилище или внешнее якорение — это то, что делает видимым всё остальное, и это уже работа по развёртыванию.
Структура
fixtures/spec.py the session, the grants, the world, and the answer key
build_fixtures.py renders session.json, policy.json, world.json, truth.json
mcpgate/scope.py is this path inside that scope
mcpgate/policy.py a grant is four things at once
mcpgate/tools.py the tool table, and what a call actually reaches
mcpgate/audit.py the hash-chained, redacted, replayable log
mcpgate/server.py naive() and guarded(), and replay()
gate.py the CLI: run a session, or verify a log
score.py every number above
ablate.py each check off, one at a time
tests/ 38 testsЗапуск
python3 demo.py # both servers + ablation
python3 gate.py --log audit.json # the product
python3 gate.py --verify audit.json # the proof
python3 -m pytest tests -q # 38 testsDocker, с отключённой сетью:
docker compose run --rm gateТесты
Один проверяет, что разрешающий список по-прежнему пропускает почти всё. Один воспроизводит каждое зафиксированное решение и требует того же вердикта. Один требует, чтобы каждый отказ проходил только из-за той проверки, что названа, — чтобы числа абляции значили ровно то, что говорят. Один ищет в резолвере путей вызовы файловой системы.
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 gradedqualityCmaintenancePolicy enforcement gateway for MCP tool calls, evaluating every tool invocation against declarative YAML policies (allow/deny/escalate-to-human), generating cryptographic hash-chained audit receipts, and including built-in content safety scanning.2MIT- AlicenseCqualityDmaintenanceEnables secure, zero-trust access to MCP tools through short-lived, signed capability leases that bind tool execution to specific sessions, intents, and constraints. Prevents prompt injection attacks and privilege escalation with dynamic risk scoring, policy enforcement, and tamper-evident audit logging.41MIT
- AlicenseNot gradedqualityAmaintenanceAn authorizing reverse proxy for MCP servers that enforces per-call policy rules on tool arguments with audit logging, dry-run, and rate limiting.Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnforces identity-based access control and audit logging for MCP servers, letting you grant fine-grained tool permissions to users and systems while failing secure by default.MIT
Related MCP Connectors
Remote MCP for A2A caller identity, scope policy, verdict receipts, and audit history.
Runtime permission, approval, and audit layer for AI agent tool execution.
Remote MCP for Copilot CLI switch gate MCP, structured receipts, audit logs, and reviewer-ready evid
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/jigonyoo/mcp-permission-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server