mcpclerk
mcpclerk
MCPサーバー用のガバナンスプロキシ: 任意のMCPサーバーの前に立ち、ツールごとの許可リストを強制し、書き込みクラスのツールを人間の承認のために保留し、ツールごとのクォータを適用し、秘密情報に見える引数をマスクし、すべての呼び出しのハッシュチェーン監査ログを書き込みます。
MCPサーバー上のAIエージェントは、公開されている任意のツールを、好きなだけ、任意の引数で呼び出すことができ、誰も監査できる形で何をしたかを記録するものはありません。エンタープライズにおいて問題となるのは「エージェントが仕事をできるか」ではなく、何を許可されているか、危険な部分を誰が承認したか、そして実際に何をしたかです。
mcpclerkはこれら3つの問いにコードで答えます。それ自体がMCPサーバーであり、エージェントはそれに接続し、mcpclerkは実際のサーバーに接続して、それらのツールをupstream.toolとして再公開します。すべての呼び出しは、許可リスト、クォータ、マスキング、承認、転送、ログという1つのパイプラインを通過します。リストにないツールは拒否されます。書き込みクラスのツールは、人間がyと答えるのを待ちます。拒否は読みやすいエラーとして返されます。ログは追記専用のJSON Linesで、各エントリは前のエントリとハッシュチェーンで結ばれているため、どこかを編集するとチェーンが壊れます。
デモは公式のファイルシステムサーバーをラップしています: 読み取りは通過し、書き込みは保留されて承認され、移動は拒否され、1分以内の4回目の検索はクォータで拒否され、ログは検証されます。49のテストが、偽のアップストリームに対して各コントロールを証明しており、アップストリームが常にマスクされていない引数を受け取ることも含まれます。

Install
pip install mcpclerk # Python 3.10+ (the MCP SDK requires it); pulls in mcp and pyyaml
mcpclerk --versionソースから: git clone https://github.com/hishamalward/mcpclerk && cd mcpclerk && pip install -e ".[dev]" && pytest。
Related MCP server: mcp-policy-gateway
Five minutes
ポリシーを書きます。これはデモのものです(
examples/policy.filesystem.yaml):version: 1 defaults: unlisted: deny # a tool not named here is an unreviewed tool approval_timeout_s: 120 # a call nobody answers in time is refused, and logged as such upstreams: fs: transport: stdio command: npx args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp/mcpclerk-demo-sandbox"] tools: "read_*": allow list_directory: allow search_files: { decision: allow, quota: { per_minute: 3 } } write_file: approve edit_file: approve create_directory: approve move_file: deny # the filesystem server has no delete; move is its destructive opアップストリームが提供するものと、ポリシーがそれに対して何をするかを確認します。サーバー自身の注釈があなたの決定の隣に表示され、破壊的なツールを許可してしまったことに気づくことができます:
$ mcpclerk tools --policy examples/policy.filesystem.yaml tool decision rule read_only destructive quota fs.read_file allow glob:read_* True None -/- fs.write_file approve exact False True -/- fs.move_file deny exact False True -/- fs.search_files allow exact True None -/3エージェントがMCPサーバーを探す場所にプロキシを登録します。Claude Codeの場合は、
examples/.mcp.json:{ "mcpServers": { "fs-governed": { "command": "mcpclerk", "args": ["serve", "--policy", "examples/policy.filesystem.yaml"] } } }2番目のターミナルで承認を待ちます:
mcpclerk approve。エージェントがfs.write_fileを呼び出すと、シークレットがすでにマスクされた呼び出しが表示され、yまたはnで答えます。その後:
mcpclerk verify audit/mcpclerk.jsonlとmcpclerk report audit/mcpclerk.jsonl。
The five controls
コントロール | 機能 | 防止するもの | 防止できないもの | 証明方法 |
許可リスト | ツールごとに | 誰もレビューしていないツールをエージェントが使うこと。 | ポリシー自体の誤った決定。 |
|
承認 |
| 監視なしの書き込み。 | 読まずに承認する人間。 |
|
クォータ | ツールごとに | 暴走ループ、安価なツールが量によって高価になること。 | ループを多くのツールに分散させること、またはプロキシの再起動をまたぐこと( |
|
マスキング | キールール( | シークレットがログや承認者の画面に表示されること。 | リストにない形のシークレット。独自の形のために |
|
監査ログ | 呼び出しごとに1つのJSON Linesエントリ。タイムスタンプ、アップストリーム、ツール、マスクされた引数、決定、承認者、結果、レイテンシ、そして | 事後のエントリの静かな編集、削除、並べ替え。完了した実行の切り詰め( | ジェネシスからチェーン全体を書き換える攻撃者(これはチェーンであり署名ではありません。下記参照)。途中で強制終了された実行の切り詰め。 |
|
結果はログに記録されず、サイズとコンテンツタイプのみが記録されます。ログは決定の監査であり、データのコピーではありません。結果を保存すると、シークレットが漏れる2番目の場所になります。
How a call moves
agent ──tools/call fs.write_file──▶ mcpclerk ──▶ [namespace] ──▶ [allowlist] ──▶ [quota] ──▶ [redact for log]
│ │ │
refused-unknown refused-denied refused-quota
│
┌── decision = approve ──▶ [hold: approvals/<id>.json] ──▶ y ─┐
│ │ n / timeout │
│ refused-by-human / refused-timeout │
└── decision = allow ────────────────────────────────────────┤
▼
[forward with ORIGINAL args] ──▶ upstream ──▶ result
│
[append log entry, hash-chained]すべての経路は、すべての拒否を含めて、ログエントリで終わります。拒否は、is_error: trueと1行の理由を伴う通常のツール結果としてエージェントに返されます: mcpclerk: refused-quota fs.search_files: 3/min exhausted; retry after 60s。
Approval, in detail
プロキシは通常、エージェントのMCPクライアントによって起動され、MCP SDKはstdioサーバーを新しいセッションで起動するため、プロキシは通常独自のターミナルを持ちません。そのため、メカニズムはファイルキューであり、ターミナルプロンプトはそのクライアントです:
保留されたすべての呼び出しについて、マスクされた引数、
requested_at、expires_at、"approved": nullを含むapprovals/<id>.jsonが書き込まれます。mcpclerk approve(同じマシンの任意のターミナルで)は保留中のリクエストを表示し、あなたの回答を書き込みます。--onceは1つに答えて終了します。それがないと、監視を続けます。ファイルを手動で
"approved": trueに編集することも機能します。これはヘッドレスジョブやスクリプトが行うことです。プロキシが制御端末を持っている場合(手動で起動した場合)、そこでもプロンプトが表示されます。両方の経路が競合し、最初の回答が優先されます。
approval_timeout_s以内に回答がない場合は拒否となり、refused-timeoutとして記録されます。書き込みに対する沈黙は「いいえ」を意味します。serve --approve-sessionは、そのプロセスのすべてのapproveクラスの呼び出しを自動承認します。起動時に警告を出力し、run-startエントリがそれを記録し、影響を受けるすべてのエントリにapproved_by: session-flagと表示され、reportがそれについて強調表示します。ポリシーファイルで設定することはできません。プロセスを起動する人による呼び出しごとの行為です。
The audit log
{"kind":"call","ts":"2026-08-24T01:14:40.822Z","run_id":"20260824T011440Z-3e1c","id":"20260824T011440Z-0002",
"name":"fs.write_file","upstream":"fs","tool":"write_file","rule":"exact",
"args":{"content":"# notes\n[REDACTED:kv-secret]\n","path":"/tmp/mcpclerk-demo-sandbox/notes.md"},
"decision":"approved","approved_by":"file","held_ms":253.7,"outcome":"ok","is_error":false,
"latency_ms":7.7,"content_bytes":57,"content_types":["text"],
"seq":4,"prev_hash":"5c0e…","hash":"b41a…"}decisionはallowed、approved、refused-denied、refused-unknown、refused-quota、refused-timeout、refused-by-humanのいずれかです。latency_msはアップストリームの時間のみです。人間の思考時間はheld_msであり、reportのp95レイテンシはツールを意味し、人間を意味しません。イベントエントリ(ポリシーのSHA-256とフラグを持つ
run-start、公開/非公開のカウントを持つdiscover、エントリ数を持つrun-end)は同じチェーンを共有します。verifyはOK n entries, chain intactで終了コード0、またはFAIL at line N: <what>で1を返します。試してみてください:sed -i '' 's/allowed/approved/' examples/audit.demo.jsonl && mcpclerk verify examples/audit.demo.jsonl。
examples/audit.demo.jsonlのサンプルログは、デモ実行の実際の出力です。構造上、公開しても安全です。マスキングテストがそれを証明しており、デモは偽のAPIキーをファイルに書き込むことで、ログが本来あるべき場所に[REDACTED:kv-secret]を表示できるようにしています。
CLI
mcpclerk serve --policy policy.yaml [--log audit/mcpclerk.jsonl] [--approvals approvals] [--approve-session] [--no-tty]
mcpclerk approve [--approvals approvals] [--once] [--wait 60]
mcpclerk tools --policy policy.yaml [--json]
mcpclerk verify audit/mcpclerk.jsonl
mcpclerk report audit/mcpclerk.jsonl [--json]終了コード: 0はOK、1は検証失敗またはポリシー無効、2は使用方法の誤り。ポリシーは起動時に検証され、問題(不明なキー、不正な決定、未設定の${ENV_VAR}、commandのないstdioアップストリーム)があると、プロキシは何も提供する前に停止します。
Policy reference
version: 1
namespace_separator: "." # "__" for clients that reject dots in tool names
defaults:
unlisted: deny # allow | deny | approve
approval_timeout_s: 120
quota: { per_run: null, per_minute: null }
redaction:
extend: ['(?i)my[-_ ]?internal[-_ ]?token\s*[:=]\s*\S+'] # value regexes, added to the built-ins
extend_keys: [client_secret] # key names, added to the built-ins
replace_builtin: false # true: only your patterns (warned about)
upstreams:
<name>: # [a-z0-9_-]+ ; becomes the prefix in <name>.<tool>
transport: stdio | http
command: ... args: [...] env: { KEY: "${FROM_PROXY_ENV}" } cwd: ... # stdio
url: https://... # http
tools:
<tool or glob>: allow | deny | approve
<tool>: { decision: approve, quota: { per_run: 10, per_minute: 3 }, approval_timeout_s: 60 }Prior art, and what this is instead
MCP用のゲートウェイは存在し、これ以上のことを行います: Lasso Securityのmcp-gateway、IBMのmcp-context-forge、DockerのMCP Gatewayは、レジストリ、マルチテナント認証、プラグインパイプライン、可観測性を提供します。mcpclerkは新規性を主張しません。それは小ささと検証可能性を主張します: 単一目的で読みやすく、ローカルなプロキシであり、その全体の表面は上記の5つのコントロールと、確認できるログです。MCP SDK以外に1つの依存関係(YAMLパーサー)を持つ、約1,000行のPythonで、午後で読めます。
What it does not do (yet)
アイデンティティとユーザー別ポリシー。オペレーターは1人と想定される。ログは「人間が承認した」ことだけを記録し、「どの人間が」は記録しない。
Web UI、またはリモート承認チャネル(Slack、メール)。
mcpclerk approveはローカルターミナルである。アップストリーム間でのポリシーの継承またはテンプレート化。
リソースとプロンプト。v0.1はツールのみをプロキシする。
resources/listとprompts/listは空である。リクエストヘッダーを必要とするHTTPアップストリーム。このバージョンのSDKのHTTPトランスポートはヘッダーを一切受け取らない。
headersを設定するポリシーは、何も送信せずに黙って通すのではなく、明示的に失敗する。Windows:ファイルキューと
mcpclerk approveは動作する。インプロセスターミナルプロンプトは動作しない(/dev/ttyがない)。CIはWindowsをベストエフォートで実行する。
脅威モデル(正直に言うと)
エージェントの座を奪った攻撃者が最初に試みるのは、リストから隠されたツールを名前で呼び出すことだ。これは拒否され、ログに記録される(refused-unknownまたはrefused-denied)。これで防げないのは:許可されたツールが有害な目的に使われること(ポリシーはあなたの判断であり、mcpclerkはそれを強制する)、ゴム印を押すだけの承認者、そしてログファイルへの書き込み権限を持つ者が最初のエントリからチェーン全体を書き換えることだ。チェーンは気づかれない改ざんを防ぐ。これが現実的な脅威である。署名または外部アンカー(毎日の先頭ハッシュを自分が管理しない場所に公開すること)が次のステップになるが、v0.1には含まれない。
開発
pip install -e ".[dev]"
pytest -q # 49 tests, all in-process, no network, no subprocesses
python examples/demo_driver.py --approve-via-file # the demo against the real filesystem server (needs npx)
vhs examples/demo.tape # re-record the GIFテストは両側でMCP SDKのインメモリトランスポートを使用する:Client(proxy) → proxy → Client(fake_upstream)。フェイクアップストリーム(tests/fake_upstream.py)は、受け取ったものをそのまま返すsecret_sinkツールを持つ。これにより、テストスイートはアップストリームがマスクされていない引数を見る一方、ログにはそれが記録されないことを証明する。
関連:toilscan(同じ書き込み安全性の考え方を開発者ツールに適用したもの)、agent-slots(並行エージェントのランタイム分離)、およびagentkeel(プロセス側:エージェントが書いたコードのゲートと爆発半径。進行中)。
次にこのプロジェクトを引き継ぐ人へ:docs/learning/how-it-works.htmlはツアーである(呼び出し順のコード、コントロール、インタビュー回答)。docs/spec.mdは契約である。
ライセンス
MIT.
This server cannot be deployed
Maintenance
Related MCP Connectors
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
MCP server for mandates, delegation, policy-gated execution, credential grants, and audit.
Security & DLP proxy for MCP: tool-poisoning scans, PII redaction on tool args/results. Beta.
- gatewayOAuthai.sealgate
MCP gateway with runtime security policy, tool-call-level control, and audit of agent actions.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceA least-privilege enforcement proxy for MCP servers. It sits between MCP clients and upstream servers, enforcing tool policies, hiding denied tools, requiring human approval for risky actions, and providing a structured audit trail.MIT
- 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
- AlicenseAqualityCmaintenanceProvides a security governance layer for AI agents to safely access upstream MCP servers, enforcing tool-level RBAC, parameter constraints, authentication via static tokens or OIDC, and tamper-evident audit logging.21Apache 2.0
- FlicenseNot gradedqualityBmaintenanceEnforces default-deny policies, budgets, and tamper-evident audit logging for MCP tool calls before they reach upstream servers.-