Skip to main content
Glama
hishamalward

mcpclerk

by hishamalward

mcpclerk

ci python license

MCP 서버를 위한 거버넌스 프록시: 모든 MCP 서버 앞에 위치하여, 도구별 허용 목록(allowlist)을 적용하고, 쓰기(write) 계열 도구는 사람의 승인을 받도록 보류하며, 도구별 할당량(quota)을 적용하고, 비밀번호처럼 보이는 인자를 마스킹하며, 모든 호출의 해시 체인 감사 로그를 기록합니다.

MCP 서버 위의 AI 에이전트는 노출된 어떤 도구든, 원하는 만큼 자주, 어떤 인자로든 호출할 수 있으며, 누구나 감사할 수 있는 형태로 무엇을 했는지 기록하는 것은 없습니다. 기업 환경에서 질문은 "에이전트가 일을 할 수 있는가"가 아니라 무엇을 하도록 허용되었는가, 위험한 부분은 누가 승인했는가, 실제로 무엇을 했는가입니다.

mcpclerk는 이 세 가지 질문에 코드로 답합니다. mcpclerk 자체가 MCP 서버입니다. 에이전트는 mcpclerk에 연결하고, mcpclerk는 실제 서버에 연결하여 그 도구들을 upstream.tool로 다시 노출합니다. 모든 호출은 하나의 파이프라인을 거칩니다: 허용 목록, 할당량, 마스킹, 승인, 전달, 로깅. 목록에 없는 도구는 거부됩니다. 쓰기 계열 도구는 사람이 y라고 답할 때까지 대기합니다. 거부는 읽을 수 있는 오류로 반환됩니다. 로그는 추가 전용(append-only) JSON Lines이며, 각 항목은 이전 항목과 해시로 연결되므로 어디든 수정하면 체인이 끊어집니다.

데모는 공식 파일시스템 서버를 감쌉니다: 읽기는 통과하고, 쓰기는 보류 후 승인되며, 이동(move)은 거부되고, 1분 내 네 번째 검색은 할당량으로 거부되며, 로그는 검증됩니다. 49개의 테스트가 가짜 업스트림에 대해 각 통제를 증명하며, 업스트림이 항상 마스킹되지 않은 원본 인자를 받는다는 것도 포함합니다.

demo

설치

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: Agentrim MCP

5분 가이드

  1. 정책을 작성합니다. 데모에서 사용한 정책입니다 (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. 업스트림이 제공하는 것과 정책이 그것에 대해 무엇을 하는지 확인합니다. 서버 자체의 주석이 여러분의 결정 옆에 표시되며, 이를 통해 파괴적인 도구를 허용했음을 알아차릴 수 있습니다:

    $ 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. 에이전트가 MCP 서버를 찾는 곳에 프록시를 등록합니다. Claude Code의 경우, examples/.mcp.json:

    { "mcpServers": { "fs-governed": {
        "command": "mcpclerk",
        "args": ["serve", "--policy", "examples/policy.filesystem.yaml"] } } }
  4. 두 번째 터미널에서 승인을 기다립니다: mcpclerk approve. 에이전트가 fs.write_file을 호출하면, 비밀이 이미 마스킹된 호출이 표시되고 y 또는 n으로 답합니다.

  5. 이후: mcpclerk verify audit/mcpclerk.jsonlmcpclerk report audit/mcpclerk.jsonl.

다섯 가지 통제

통제

기능

방지하는 것

방지할 수 없는 것

증명

허용 목록

도구별 allow / deny / approve, 정확한 이름 우선, 그다음 가장 긴 glob, 마지막으로 defaults.unlisted (거부). 거부되거나 목록에 없는 도구는 에이전트에게 목록조차 표시되지 않습니다.

아무도 검토하지 않은 도구를 에이전트가 사용하는 것.

정책 자체의 잘못된 결정. mcpclerk tools는 업스트림의 읽기 전용/파괴적 힌트를 여러분의 결정 옆에 표시하여 이를 어렵게 만듭니다.

test_policy.py, test_pipeline.py::test_denied_hidden_tool_called_by_name_is_refused

승인

approve 계열 호출은 보류됩니다. 요청은 마스킹된 인자와 함께 approvals/<id>.json에 기록되고, 사람이 mcpclerk approve로 답합니다 (또는 파일을 직접 편집하거나, 프록시에 터미널이 있으면 터미널 프롬프트에서). 시간 초과는 거부입니다.

감독 없는 쓰기.

읽지 않고 승인하는 사람. --approve-session은 그런 사람을 위한 것이며 영향을 받는 모든 항목에 기록됩니다.

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

할당량

도구별 per_runper_minute (슬라이딩 윈도우). 할당량 초과는 한도와 윈도우가 해제될 때까지의 초와 함께 거부됩니다. 거부된 호출은 할당량을 소비하지 않습니다. 승인 후 사람이 거부한 호출은 소비합니다.

무한 루프; 저렴한 도구가 양으로 인해 비싸지는 것.

여러 도구에 걸친 루프 분산, 또는 프록시 재시작에 걸친 분산 (per_run은 프로세스와 함께 초기화됨).

test_quota.py, test_pipeline.py::test_quota_exhaustion

마스킹

키 규칙(api_key, token, password, authorization, ...)은 전체 값을 대체합니다. 값 규칙(bearer 헤더, sk-/AKIA/ghp_/xox 토큰, JWT, PEM 블록, URL userinfo, password=...)은 일치 부분을 대체합니다. 로그와 사람에게 표시되는 것에 적용됩니다. 업스트림은 원본 인자를 받습니다.

비밀이 로그나 승인자의 화면에 노출되는 것.

목록의 어떤 형태와도 일치하지 않는 비밀. redaction.extend / extend_keys로 자신만의 형태를 추가하세요.

test_redact.py, test_pipeline.py::test_upstream_receives_unredacted_args

감사 로그

호출당 하나의 JSON Lines 항목: 타임스탬프, 업스트림, 도구, 마스킹된 인자, 결정, 승인자, 결과, 지연 시간, hash = sha256(prev_hash + canonical(entry)). verify는 체인을 재계산하고, report는 요약합니다.

사후 항목의 조용한 편집, 삭제 또는 재정렬; 완료된 실행의 잘림(run-end가 개수를 담고 있음).

제네시스부터 전체 체인을 다시 쓰는 공격자 (이것은 체인일 뿐 서명이 아닙니다. 아래 참조). 중간에 죽은 실행의 잘림.

test_audit.py (편집, 삭제, 재정렬, 잘림)

결과는 로그에 기록되지 않으며, 크기와 콘텐츠 유형만 기록됩니다. 로그는 결정에 대한 감사이지 데이터의 사본이 아닙니다. 결과를 저장하면 비밀이 새어나갈 두 번째 장소가 됩니다.

호출이 진행되는 방식

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로 편집하는 것도 작동하며, 이것이 헤드리스 작업이나 스크립트가 하는 방식입니다.

  • 프록시에 제어 터미널이 있다면 (수동으로 시작한 경우), 그곳에서도 프롬프트가 표시됩니다. 두 경로가 경쟁하며, 먼저 온 답변이 승리합니다.

  • 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…"}
  • decisionallowed, approved, refused-denied, refused-unknown, refused-quota, refused-timeout, refused-by-human 중 하나입니다.

  • latency_ms는 업스트림 시간만 포함합니다. 사람의 사고 시간은 held_ms이므로, report의 p95 지연 시간은 사람이 아닌 도구를 의미합니다.

  • 이벤트 항목(run-start는 정책의 SHA-256과 플래그 포함, discover는 노출/숨김 개수 포함, run-end는 항목 개수 포함)은 같은 체인을 공유합니다.

  • verifyOK 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 정상, 1 검증 실패 또는 정책 오류, 2 사용법 오류. 정책은 시작 시 검증되며 어떤 문제든 (알 수 없는 키, 잘못된 결정, 설정되지 않은 ${ENV_VAR}, command 없는 stdio 업스트림) 아무것도 서빙하기 전에 프록시를 중지시킵니다.

정책 참조

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의 mcp-gateway, IBM의 mcp-context-forge, Docker의 MCP Gateway는 레지스트리, 멀티 테넌트 인증, 플러그인 파이프라인 및 관측성을 제공합니다. mcpclerk는 새로움을 주장하지 않습니다. 작음과 검증 가능성을 주장합니다: 단일 목적의, 읽기 쉬운, 로컬 프록시로서 전체 표면이 위의 다섯 가지 통제와 확인할 수 있는 로그입니다. 약 1,000줄의 Python으로 오후에 읽을 수 있으며, MCP SDK 외에 의존성이 하나뿐입니다 (YAML 파서).

아직 하지 않는 것

  • 신원 및 사용자별 정책. 운영자는 한 명으로 가정합니다. 로그에는 어떤 사람이 승인했는지가 아니라 승인했다는 사실만 기록됩니다.

  • 웹 UI 또는 원격 승인 채널(Slack, 이메일). mcpclerk approve는 로컬 터미널입니다.

  • 업스트림 간 정책 상속 또는 템플릿화.

  • 리소스 및 프롬프트. v0.1은 도구만 프록시합니다. resources/listprompts/list는 비어 있습니다.

  • 요청 헤더가 필요한 HTTP 업스트림. 이 버전의 SDK HTTP 전송은 헤더를 받지 않습니다. 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.

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