mcpclerk
mcpclerk
Управляющий прокси для MCP-серверов: встаёт перед любым MCP-сервером, применяет разрешённый список инструментов, удерживает инструменты класса записи для одобрения человеком, применяет квоты на каждый инструмент, редактирует аргументы, похожие на секреты, и пишет хэш-цепочный журнал аудита каждого вызова.
ИИ-агент на MCP-серверах может вызывать любой инструмент, который они предоставляют, сколько угодно раз, с любыми аргументами, и ничто не фиксирует, что он делал, в форме, которую можно проверить. В корпоративной среде вопрос не в том, «может ли агент выполнить работу», а в том, что ему разрешено делать, кто одобрил опасные части и что он фактически сделал?
mcpclerk отвечает на эти три вопроса кодом. Он сам является MCP-сервером: агент подключается к нему, он подключается к реальным серверам и повторно предоставляет их инструменты как upstream.tool. Каждый вызов проходит через один конвейер: разрешённый список, квота, редактирование, одобрение, пересылка, журнал. Неперечисленный инструмент отклоняется. Инструмент класса записи ждёт, пока человек ответит y. Отказы возвращаются как читаемые ошибки. Журнал — это append-only JSON Lines, каждая запись хэшируется с предыдущей, так что любое изменение где-либо разрывает цепочку.
Демонстрация оборачивает официальный файловый сервер: чтение проходит, запись удерживается и одобряется, перемещение отклоняется, четвёртый поиск в минуту отклоняется по квоте, и журнал проверяется. 49 тестов доказывают каждый контроль против фейкового upstream, включая то, что upstream всегда получает нередактированные аргументы.

Установка
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
Пять минут
Напишите политику. Вот та, что из демонстрации (
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Посмотрите, что предлагает upstream и что ваша политика с ним делает. Собственные аннотации сервера показаны рядом с вашим решением, что позволяет заметить, что вы разрешили разрушительный инструмент:
$ 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"] } } }Во втором терминале ждите одобрений:
mcpclerk approve. Когда агент вызываетfs.write_file, вы видите вызов с уже замаскированными секретами и отвечаетеyилиn.После:
mcpclerk verify audit/mcpclerk.jsonlиmcpclerk report audit/mcpclerk.jsonl.
Пять контролей
Контроль | Что делает | Что предотвращает | Что не может предотвратить | Доказано |
Разрешённый список |
| Использование агентом инструмента, который никто не проверял. | Плохое решение в самой политике. |
|
Одобрение | Вызовы класса | Несанкционированная запись. | Человек, который одобряет, не читая. |
|
Квоты |
| Бесконечные циклы; дешёвый инструмент, становящийся дорогим по объёму. | Распределение цикла по многим инструментам или по перезапускам прокси ( |
|
Редактирование | Правила ключей ( | Попадание секретов в журнал или на экран утверждающего. | Секрет, не похожий ни на что из списка. Расширьте |
|
Журнал аудита | Одна запись JSON Lines на вызов с временной меткой, upstream, инструментом, редактированными аргументами, решением, кто одобрил, результатом, задержкой и | Тихое редактирование, удаление или переупорядочивание записей после факта; усечение завершённого запуска ( | Атакующий, который переписывает всю цепочку с начала (это цепочка, а не подпись; см. ниже). Усечение запуска, убитого на полпути. |
|
Результаты не записываются, только их размер и типы содержимого. Журнал — это аудит решений, а не копия данных; хранение результатов сделало бы его вторым местом утечки секретов.
Как движется вызов
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 и однострочной причиной: mcpclerk: refused-quota fs.search_files: 3/min exhausted; retry after 60s.
Одобрение, подробно
Прокси обычно запускается MCP-клиентом агента, а MCP SDK запускает stdio-серверы в новой сессии, поэтому у прокси обычно нет собственного терминала. Именно поэтому механизм — это файловая очередь, а терминальное приглашение — её клиент:
approvals/<id>.jsonзаписывается для каждого удержанного вызова, с редактированными аргументами,requested_at,expires_atи"approved": null.mcpclerk approve(в любом терминале, на той же машине) показывает ожидающие запросы и записывает ваш ответ.--onceотвечает на один и выходит; без него продолжает следить.Ручное редактирование файла до
"approved": trueтоже работает — так делают headless-задачи или скрипты.Если у прокси всё же есть управляющий терминал (вы запустили его вручную), он также запрашивает там. Оба пути соревнуются; первый ответ побеждает.
Отсутствие ответа в течение
approval_timeout_s— это отказ, записываемый какrefused-timeout. Тишина на запись означает «нет».serve --approve-sessionавтоматически одобряет все вызовы класса approve для этого процесса. Он печатает предупреждение при запуске, записьrun-startфиксирует это, каждая затронутая запись говоритapproved_by: session-flag, аreportкричит об этом. Это нельзя задать в файле политики; это акт при каждом вызове того, кто запускает процесс.
Журнал аудита
{"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— только время upstream; время размышлений человека — этоheld_ms, так что p95 задержки вreportозначает инструмент, а не человека.Событийные записи (
run-startс SHA-256 политики и флагами,discoverсо счётчиками exposed/hidden,run-endсо счётчиком записей) используют ту же цепочку.verifyзавершается с кодом 0 иOK n entries, chain intactили с кодом 1 иFAIL at line N: <what>. Попробуйте: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 — ок, 1 — verify не прошёл или политика недействительна, 2 — использование. Политика проверяется при запуске, и любая проблема (неизвестный ключ, плохое решение, неустановленный ${ENV_VAR}, stdio-upstream без command) останавливает прокси до того, как он начнёт обслуживать что-либо.
Справочник по политике
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 }Предшественники и чем это является вместо них
Шлюзы для MCP существуют и делают больше: Lasso Security's mcp-gateway, IBM's mcp-context-forge и Docker's MCP Gateway приносят реестры, мультитенантную аутентификацию, плагинные конвейеры и наблюдаемость. mcpclerk не претендует на новизну. Он претендует на малость и проверяемость: одноцелевой, читаемый, локальный прокси, вся поверхность которого — пять контролей выше и журнал, который можно проверить. Это около 1000 строк Python, которые можно прочитать за вечер, с одной зависимостью помимо MCP SDK (парсер YAML).
Что он (пока) не делает
Идентичность и политики для каждого пользователя. Предполагается один оператор; журнал фиксирует что человек одобрил, а не какой человек.
Веб-интерфейс или удалённые каналы одобрения (Slack, email).
mcpclerk approve— это локальный терминал.Наследование политик или шаблонизация для вышестоящих систем.
Ресурсы и промпты. v0.1 проксирует только инструменты;
resources/listиprompts/listпусты.HTTP-апстримы, которым нужны заголовки запросов. HTTP-транспорт SDK в этой версии не принимает их; политика, устанавливающая
headers, громко завершается с ошибкой, а не молча ничего не отправляет.Windows: файловая очередь и
mcpclerk approveработают; внутрипроцессный терминальный промпт — нет (нет/dev/tty). CI запускает Windows по принципу best-effort.
Модель угроз, честно
Что атакующий, находящийся на месте агента, попробует в первую очередь — вызвать инструмент по имени, скрытый из списка. Это отклоняется и записывается в журнал (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.-