Skip to main content
Glama
hishamalward

mcpclerk

by hishamalward

mcpclerk

ci python license

Ein Governance-Proxy für MCP-Server: Er sitzt vor jedem MCP-Server, erzwingt eine Pro-Tool-Allowlist, hält Schreibwerkzeuge zur menschlichen Genehmigung zurück, wendet Pro-Tool-Kontingente an, schwärzt geheimnisaussehende Argumente und schreibt ein hash-verkettetes Audit-Log jedes Aufrufs.

Ein KI-Agent auf MCP-Servern kann jedes von ihnen bereitgestellte Werkzeug beliebig oft und mit beliebigen Argumenten aufrufen, und nichts zeichnet auf, was er getan hat, in einer Form, die jemand prüfen kann. In einem Unternehmen lautet die Frage nicht „Kann der Agent die Aufgabe erledigen?“, sondern was darf er tun, wer hat die gefährlichen Teile genehmigt, und was hat er tatsächlich getan?

mcpclerk beantwortet diese drei mit Code. Es ist selbst ein MCP-Server: Der Agent verbindet sich mit ihm, er verbindet sich mit den echten Servern und stellt deren Werkzeuge als upstream.tool erneut bereit. Jeder Aufruf durchläuft eine Pipeline: Allowlist, Kontingent, Schwärzung, Genehmigung, Weiterleitung, Protokollierung. Ein nicht gelistetes Werkzeug wird verweigert. Ein Schreibwerkzeug wartet auf eine menschliche Antwort y. Ablehnungen kommen als lesbare Fehler zurück. Das Log ist ein append-only JSON Lines, jeder Eintrag mit dem vorherigen gehasht, sodass eine Änderung an beliebiger Stelle die Kette bricht.

Die Demo umschließt den offiziellen Dateisystem-Server: Ein Lesevorgang wird durchgelassen, ein Schreibvorgang wird zurückgehalten und genehmigt, ein Verschieben wird verweigert, die vierte Suche innerhalb einer Minute wird wegen Kontingent verweigert, und das Log verifiziert. 49 Tests beweisen jede Kontrolle gegen einen Fake-Upstream, einschließlich der Tatsache, dass der Upstream immer die ungeschwärzten Argumente erhält.

demo

Installation

pip install mcpclerk          # Python 3.10+ (the MCP SDK requires it); pulls in mcp and pyyaml
mcpclerk --version

Aus dem Quellcode: git clone https://github.com/hishamalward/mcpclerk && cd mcpclerk && pip install -e ".[dev]" && pytest.

Related MCP server: Agentrim MCP

Fünf Minuten

  1. Schreiben Sie eine Richtlinie. Dies ist die aus der Demo (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
  2. Sehen Sie, was der Upstream bietet und was Ihre Richtlinie damit macht. Die eigenen Anmerkungen des Servers werden neben Ihrer Entscheidung angezeigt, so erkennen Sie, dass Sie ein destruktives Werkzeug erlaubt haben:

    $ 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
  3. Registrieren Sie den Proxy dort, wo Ihr Agent nach MCP-Servern sucht. Für Claude Code, examples/.mcp.json:

    { "mcpServers": { "fs-governed": {
        "command": "mcpclerk",
        "args": ["serve", "--policy", "examples/policy.filesystem.yaml"] } } }
  4. Warten Sie in einem zweiten Terminal auf Genehmigungen: mcpclerk approve. Wenn der Agent fs.write_file aufruft, sehen Sie den Aufruf mit bereits maskierten Geheimnissen und antworten mit y oder n.

  5. Danach: mcpclerk verify audit/mcpclerk.jsonl und mcpclerk report audit/mcpclerk.jsonl.

Die fünf Kontrollen

Kontrolle

Was sie tut

Was sie verhindert

Was sie nicht verhindern kann

Belegt durch

Allowlist

allow / deny / approve pro Werkzeug, zuerst exakter Name, dann längster Glob, dann defaults.unlisted (deny). Verweigerte und nicht gelistete Werkzeuge werden dem Agenten nicht einmal aufgelistet.

Dass der Agent ein Werkzeug verwendet, das niemand überprüft hat.

Eine schlechte Entscheidung in der Richtlinie selbst. mcpclerk tools zeigt die Read-only-/destruktiven Hinweise des Upstreams neben Ihrer Entscheidung, um das zu erschweren.

test_policy.py, test_pipeline.py::test_denied_hidden_tool_called_by_name_is_refused

Genehmigung

approve-Klassen-Aufrufe werden zurückgehalten. Die Anfrage wird mit geschwärzten Argumenten in approvals/<id>.json geschrieben; ein Mensch antwortet mit mcpclerk approve (oder durch Bearbeiten der Datei oder an einer Terminal-Eingabeaufforderung, falls der Proxy eine hat). Timeout ist eine Ablehnung.

Ein unbeaufsichtigter Schreibvorgang.

Ein Mensch, der ohne zu lesen genehmigt. --approve-session existiert für diesen Menschen und wird in jedem betroffenen Eintrag protokolliert.

test_approval.py, test_pipeline.py::test_approve_via_file_then_forward, test_approval_refused_and_timed_out

Kontingente

per_run und per_minute (gleitendes Fenster) pro Werkzeug. Über dem Kontingent wird mit dem Limit und den Sekunden bis zur Freigabe des Fensters abgelehnt. Abgelehnte Aufrufe verbrauchen kein Kontingent; genehmigte und dann vom Menschen abgelehnte Aufrufe schon.

Außer Kontrolle geratene Schleifen; ein billiges Werkzeug, das durch Menge teuer wird.

Verteilen einer Schleife über viele Werkzeuge oder über Proxy-Neustarts (per_run wird mit dem Prozess zurückgesetzt).

test_quota.py, test_pipeline.py::test_quota_exhaustion

Schwärzung

Schlüsselregeln (api_key, token, password, authorization, ...) ersetzen den gesamten Wert; Wertregeln (Bearer-Header, sk-/AKIA/ghp_/xox-Tokens, JWTs, PEM-Blöcke, URL-Benutzerinfo, password=...) ersetzen die Übereinstimmung. Angewendet auf das, was protokolliert und dem Menschen gezeigt wird. Der Upstream erhält die ursprünglichen Argumente.

Dass Geheimnisse im Log oder auf dem Bildschirm eines Genehmigers landen.

Ein Geheimnis, das wie nichts auf der Liste geformt ist. Erweitern Sie redaction.extend / extend_keys für Ihre eigenen Formen.

test_redact.py, test_pipeline.py::test_upstream_receives_unredacted_args

Audit-Log

Ein JSON-Lines-Eintrag pro Aufruf mit Zeitstempel, Upstream, Werkzeug, geschwärzten Argumenten, Entscheidung, wer genehmigt hat, Ergebnis, Latenz und hash = sha256(prev_hash + canonical(entry)). verify berechnet die Kette neu; report fasst sie zusammen.

Stilles Bearbeiten, Löschen oder Umordnen von Einträgen im Nachhinein; Abschneiden eines abgeschlossenen Laufs (run-end trägt die Anzahl).

Ein Angreifer, der die gesamte Kette von Anfang an neu schreibt (dies ist eine Kette, keine Signatur; siehe unten). Abschneiden eines Laufs, der mitten in der Ausführung abgebrochen wurde.

test_audit.py (edit, delete, reorder, truncate)

Ergebnisse werden nicht protokolliert, nur ihre Größe und Inhaltstypen. Das Log ist eine Prüfung von Entscheidungen, keine Kopie der Daten; das Speichern von Ergebnissen würde es zu einem zweiten Ort machen, an dem Geheimnisse lecken könnten.

Wie ein Aufruf abläuft

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]

Jeder Pfad, einschließlich jeder Ablehnung, endet in einem Logeintrag. Ablehnungen werden dem Agenten als normales Werkzeugergebnis mit is_error: true und einer einzeiligen Begründung zurückgegeben: mcpclerk: refused-quota fs.search_files: 3/min exhausted; retry after 60s.

Genehmigung im Detail

Der Proxy wird normalerweise vom MCP-Client des Agenten gestartet, und das MCP-SDK startet Stdio-Server in einer neuen Sitzung, sodass der Proxy normalerweise kein eigenes Terminal hat. Deshalb ist der Mechanismus eine Datei-Warteschlange und die Terminal-Eingabeaufforderung ein Client davon:

  • approvals/<id>.json wird für jeden zurückgehaltenen Aufruf geschrieben, mit den geschwärzten Argumenten, requested_at, expires_at und "approved": null.

  • mcpclerk approve (in einem beliebigen Terminal auf derselben Maschine) zeigt ausstehende Anfragen und schreibt Ihre Antwort. --once beantwortet eine und beendet sich; ohne das beobachtet es weiter.

  • Das manuelle Bearbeiten der Datei auf "approved": true funktioniert ebenfalls, was ein Headless-Job oder ein Skript tut.

  • Falls der Proxy doch ein steuerndes Terminal hat (Sie haben ihn von Hand gestartet), fragt er auch dort nach. Beide Pfade laufen parallel; die erste Antwort gewinnt.

  • Keine Antwort innerhalb von approval_timeout_s ist eine Ablehnung, protokolliert als refused-timeout. Schweigen bei einem Schreibvorgang bedeutet Nein.

  • serve --approve-session genehmigt automatisch jeden Approve-Klassen-Aufruf für diesen Prozess. Es gibt beim Start eine Warnung aus, der run-start-Eintrag zeichnet es auf, jeder betroffene Eintrag sagt approved_by: session-flag, und report schreit darüber. Es kann nicht in der Richtliniendatei festgelegt werden; es ist eine Handlung pro Aufruf durch denjenigen, der den Prozess startet.

Das 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 ist eines von allowed, approved, refused-denied, refused-unknown, refused-quota, refused-timeout, refused-by-human.

  • latency_ms ist nur die Upstream-Zeit; die Denkzeit des Menschen ist held_ms, sodass die p95-Latenz in report das Werkzeug bedeutet, nicht die Person.

  • Ereigniseinträge (run-start mit der SHA-256 der Richtlinie und den Flags, discover mit exponierten/versteckten Zählungen, run-end mit der Eintragszahl) teilen sich dieselbe Kette.

  • verify beendet sich mit 0 und OK n entries, chain intact oder mit 1 und FAIL at line N: <what>. Probieren Sie es aus: sed -i '' 's/allowed/approved/' examples/audit.demo.jsonl && mcpclerk verify examples/audit.demo.jsonl.

Das Beispiel-Log in examples/audit.demo.jsonl ist die echte Ausgabe des Demo-Laufs. Es ist konstruktionsbedingt sicher zu veröffentlichen: Die Schwärzungstests beweisen es, und die Demo schreibt einen gefälschten API-Schlüssel in eine Datei, genau damit das Log [REDACTED:kv-secret] zeigen kann, wo er gewesen wäre.

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]

Exit-Codes: 0 ok, 1 Verifizierung fehlgeschlagen oder Richtlinie ungültig, 2 Verwendung. Die Richtlinie wird beim Start validiert, und jedes Problem (unbekannter Schlüssel, schlechte Entscheidung, nicht gesetzte ${ENV_VAR}, ein Stdio-Upstream ohne command) stoppt den Proxy, bevor er irgendetwas bedient.

Richtlinienreferenz

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 }

Vorherige Arbeiten und was dies stattdessen ist

Gateways für MCP existieren und tun mehr als dies: Lasso Security's mcp-gateway, IBM's mcp-context-forge und Docker's MCP Gateway bringen Registries, Multi-Tenant-Auth, Plugin-Pipelines und Beobachtbarkeit. mcpclerk beansprucht keine Neuheit. Es beansprucht Kleinheit und Verifizierbarkeit: ein zweckgebundener, lesbarer, lokaler Proxy, dessen gesamte Oberfläche die fünf obigen Kontrollen und ein überprüfbares Log ist. Es umfasst etwa 1.000 Zeilen Python, die man an einem Nachmittag lesen kann, mit einer Abhängigkeit über das MCP-SDK hinaus (ein YAML-Parser).

Was es (noch) nicht tut

  • Identität und benutzerbezogene Richtlinien. Es wird ein Bediener angenommen; das Protokoll zeichnet auf, dass ein Mensch genehmigt hat, nicht welcher Mensch.

  • Eine Weboberfläche oder entfernte Genehmigungskanäle (Slack, E-Mail). mcpclerk approve ist ein lokales Terminal.

  • Richtlinienvererbung oder -vorlagen über Upstreams hinweg.

  • Ressourcen und Prompts. v0.1 proxyt nur Tools; resources/list und prompts/list sind leer.

  • HTTP-Upstreams, die Anforderungsheader benötigen. Der HTTP-Transport des SDK akzeptiert in dieser Version keine; eine Richtlinie, die headers setzt, schlägt laut fehl, anstatt stillschweigend nichts zu senden.

  • Windows: Die Dateiwarteschlange und mcpclerk approve funktionieren; die prozessinterne Terminaleingabeaufforderung nicht (kein /dev/tty). CI führt Windows nach bestem Bemühen aus.

Bedrohungsmodell, ehrlich

Was ein Angreifer mit dem Sitz des Agenten zuerst versuchen würde, ist, ein Tool mit einem Namen aufzurufen, das in der Liste verborgen ist. Das wird verweigert und protokolliert (refused-unknown oder refused-denied). Was dies nicht verhindert: ein Tool, das erlaubt ist, für etwas Schädliches verwendet wird (die Richtlinie ist Ihr Urteil, mcpclerk setzt sie durch), ein Genehmiger, der blind absegnet, und jeder mit Schreibzugriff auf die Protokolldatei, der die gesamte Kette ab dem ersten Eintrag neu schreibt. Die Kette schützt vor stillen Änderungen, was die realistische Bedrohung ist; Signaturen oder ein externer Anker (Veröffentlichung des täglichen Kopf-Hashs an einem Ort, den Sie nicht kontrollieren) wären der nächste Schritt und sind in v0.1 nicht enthalten.

Entwicklung

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

Tests verwenden den In-Memory-Transport des MCP SDK auf beiden Seiten: Client(proxy) → proxy → Client(fake_upstream). Der Fake-Upstream (tests/fake_upstream.py) hat ein secret_sink-Tool, das genau das zurückgibt, was es empfangen hat. So beweist die Testsuite, dass der Upstream unredigierte Argumente sieht, während das Protokoll dies nicht tut.

Verwandt: toilscan (derselbe Schreibsicherheitsinstinkt, angewendet auf ein Entwicklerwerkzeug), agent-slots (Laufzeitisolierung für parallele Agenten) und agentkeel (die Prozessseite: Tore und Auswirkungsradius für von Agenten geschriebenen Code; in Arbeit).

Für den, der das als Nächstes übernimmt: docs/learning/how-it-works.html ist die Tour (der Code in Aufrufreihenfolge, die Steuerungen, die Interviewantworten); docs/spec.md ist der Vertrag.

Lizenz

MIT.

A
license - permissive license
Not graded
quality - not tested
A
maintenance

Maintenance

Maintainers
Response time
Release cycle
1Releases (12mo)
Commit activity

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

  • A
    license
    B
    quality
    C
    maintenance
    Security gateway that wraps any MCP server with per-tool policies, approval gates, and optional Ed25519-signed decision receipts. Shadow mode logs every tool call without blocking; enforce mode applies block, rate-limit, and minimum-tier rules. Receipts are independently verifiable offline with no accounts needed.
    5
    469
    9
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    A 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
  • A
    license
    A
    quality
    A
    maintenance
    An MCP proxy that enforces policy on every tool call, blocking or flagging actions before they reach downstream MCP servers.
    1
    249
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    An 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

View all related MCP servers

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.

  • A paid remote MCP for CLI tool MCP, built to return verdicts, receipts, usage logs, and audit-ready

View all MCP Connectors

Latest Blog Posts

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/hishamalward/mcpclerk'

If you have feedback or need assistance with the MCP directory API, please join our Discord server