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サーバーが備えている権限モデルです。許可されたツール名のリストと、成功したことのログです。このセッションの呼び出しは、1つを除いてすべてそれを通過します。
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 はログが検証できない。
グラントは一度に4つのものである
これは、有効なグラントが呼び出しをカバーしない4つの別々の方法であり、「この呼び出し元はこのツールの権限を持っているか」 が間違った質問である理由です。
{"id": "G1", "tool": "fs.read", "scope": "/data/reports",
"purpose": "q3-report", "expires_at": 100, "max_uses": 10}呼び出し | 何が問題か | 他に何が許可したか |
| 同じ文字で始まる別のディレクトリ | ツール ✓ 目的 ✓ 有効期限 ✓ |
| 許可されたディレクトリを経由して到達した同じディレクトリ | ツール ✓ 目的 ✓ 有効期限 ✓ |
| グラントは40ティック前に切れた。呼び出し自体には何も問題がない | ツール ✓ スコープ ✓ 目的 ✓ |
2回有効なグラントに対する3回目の | グラントは権限であると同時に予算でもある | ツール ✓ スコープ ✓ 目的 ✓ |
| セッションの最初の呼び出しと、それが行われた理由以外は同一 | ツール ✓ スコープ ✓ 有効期限 ✓ |
| テンプレートが | ツール ✓ スコープ ✓ 目的 ✓ 有効期限 ✓ |
| 1つのキーに対するグラントを持っていることは、キーリングを持っていることではない | ツール ✓ 目的 ✓ 有効期限 ✓ |
これらのそれぞれについて、指定されたチェックだけをオフにして、呼び出しが通過することを要求するテストがあります。複数のチェックが捕捉する拒否は、それらのいずれについても何も証明しない拒否です。
Related MCP server: MCP Airlock
混乱した代理人
render_template は許可されています。/templates/leak.md は許可されたスコープ内にあります。テンプレートには {{include:/etc/service-token}} が含まれています。
サーバーは呼び出し元が持っていない権利を保持しており、呼び出し元が名前を挙げたことのないリソースにそれらを使うよう求められています。引数内のリソースをチェックする権限レイヤーは、それが重要となるツールに対してまさに間違ったものをチェックしています。
したがって、チェックは呼び出しが到達する前に、それが何に到達するかを調べます。そして、/templates/report.md には依然として「はい」と言います。これは /data/reports/q3.csv に到達し、この呼び出し元はそのグラントを確かに持っています。権限チェックの半分は「はい」です。 テンプレートが何かを含むことができるからといって、すべてのテンプレートをブロックするルールはルールではありません。
パス包含、ここが通常間違われるところ
両方の間違いは1行です。
path.startswith(scope) # /data/reports-private is "inside" /data/reports
".." in path # and this breaks ordinary callers to catch nothing2つ目はそれ自体がケースに値します。/data/reports/../reports/q3.csv は許可されたスコープの内側に解決され、許可されなければならない呼び出しとしてコーパスに含まれています。文字を禁止するのは安易な修正です。パスを解決することは、禁止が捕まえるであろうすべてを捕まえ、捕まえないであろうものは何も捕まえません。
ここでの解決は字句的であり、ファイルシステムには決して触れません。realpath、os.stat、open( をgrepするテストがあります。決定は呼び出しの前に、呼び出し元が提供したパスに対して実行され、何が存在するかによって異なる答えになってはなりません。
ログが製品である
2つの特性がそれを日記ではなく証拠にします。
それは拒否を記録します。 成功したことのログは「エージェントが何を試みたか」に答えることができません。これは誰もが後で実際に尋ねる質問です。ここでは、17のエントリのうち8つが拒否であり、それぞれにそれを引き起こしたチェックと平易な英語での理由が含まれています。
それは再生可能です。 すべてのエントリは、それが決定されたグラントとポリシーバージョンを保持しているため、ログだけから決定を再計算し、それが記録する判定と比較できます:
python3 gate.py --verify audit.json
replay: 17/17 entries recompute to the verdict they recordreplay フィールドをオフにすると、その数は 0/17 です。エントリは依然として何が決定されたかを示していますが、誰にもそれを確認する方法を与えません。
そして、エントリはハッシュチェーンされているため、削除または編集されたものは見えます:
entry 6 does not follow the one before it -- something was removed or edited秘密は決してそれに入りません。 秘密を保持する監査証跡は、より多くの人が読めるファイル内の秘密の2番目のコピーです。secrets.get はこのセッションで許可されており、その値はログのどこにも現れません。ナイーブなログには2つ含まれています。
9つのチェック、そしてそれぞれがその場所を得る証拠
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記録に値する1つの修正: scope アブレーションの最初のバージョンはチェックをオフにしませんでした。それは文字列プレフィックス比較を代わりに使用しました。これはチェックの欠如ではなく異なるチェックであり、チェックが必要であることを証明するために存在した呼び出しを静かに拒否し続けました。チェックをオフにすることは、それをオフにすることを意味しなければなりません。そうでなければ、アブレーションは交換を測定していることになります。
同じセッションでは、最後の呼び出しが、カバーしていないキーを指定したためではなく、1回多すぎるために拒否されたサイズのグラントも明らかになりました。それは間違ったチェックをテストして合格していました。修正はフィクスチャにあり、それを見つけたテストは、各拒否が独自のチェックを分離することを要求するテストです。
既知の制限、隠すのではなく明記
これは決定レイヤーであり、トランスポートではありません。 それは決定し、プロセス内ツールテーブルに対して実行し、ログを記録します。実際のMCPトランスポートに配線することは通常の作業です。ここにあるのは通常欠けている部分であり、ネットワークもキーもなしで実行されます。
グラントは書き留められ、引き出されるものではありません。 実際のデプロイメントは人に尋ねて答えを保存します。その答えが含まなければならないもの(ツール、スコープ、目的、有効期限、使用回数)は、これが論じる部分です。
purposeは呼び出し元によって宣言され、額面どおりに受け取られます。 目的について嘘をつく呼び出し元はチェックを無効にします。それでも実際の何かを得ます: 宣言された目的はログに残るため、嘘は記録に残り、グラントのスコープはそれに対応するために広がりませんでした。代理人チェックは1つのツールの到達範囲を知っています。
render_templateはそのインクルードを解析します。サーバーが到達範囲を計算できないツールはこの方法でチェックできません。そのようなツールに対する正直な答えは、より広いチェックではなく、より狭いグラントです。チェーンは順序と整合性を証明し、保管を証明しません。 ファイル全体を書き換えられる人は誰でもチェーン全体を書き換えられます。それは選択的な編集を可視化します。これが現実的な脅威です。追記専用ストアまたは外部アンカーが残りを可視化するものであり、それはデプロイメント作業です。
レイアウト
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テスト
1つは、許可リストが依然としてほとんどすべてを通過させることを主張します。1つは、記録されたすべての決定を再生し、同じ判定を要求します。1つは、各拒否が指定されたチェックのためだけに生き残ることを要求し、アブレーションの数値が意味することを意味します。1つは、パスリゾルバーをファイルシステム呼び出しについてgrepします。
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