Skip to content

MCP Phase 2a: X-Ghpool-Key agent authn + default-deny tool allowlist#26

Merged
chaodu-agent merged 1 commit into
mainfrom
mcp-agent-authn
Jul 13, 2026
Merged

MCP Phase 2a: X-Ghpool-Key agent authn + default-deny tool allowlist#26
chaodu-agent merged 1 commit into
mainfrom
mcp-agent-authn

Conversation

@chaodu-agent

Copy link
Copy Markdown
Contributor

Summary

Slice 2a of #17 (per the delivery plan): per-agent authentication via X-Ghpool-Key and a default-deny tool allowlist enforced at the proxy. Read-only scope is unchanged — writes remain gated on slice 2b.

Checklist items closed from #17: [2a] Agent authentication · [2a] Per-agent default-deny policy (tool allowlist; repo allowlist is 2b) · [2a] Outbound header allowlist (verified by test) · [2a] tools/list filtering via X-MCP-Tools · [2a] TLS guidance (docs).

Design

  • Backward compatible: no [[mcp.agents]] configured → Phase 1 network-trust mode, zero behavior change (verified by existing tests remaining green unmodified).
  • Authn: any agent configured → every /mcp request requires a valid X-Ghpool-Key. Keys use the existing secret-reference formats (env:/aws:secretsmanager:/k8s:), resolved at config load. Comparison is on SHA-256 digests so equality-check timing leaks nothing about the configured key. Failures: 401 with a JSON-RPC error body, audit-logged.
  • Authz: tools/call for a tool not on the agent's allowlist → 403 at the proxy, never reaches GitHub. Empty allowlist = deny all calls. The allowlist is also injected upstream as X-MCP-Tools (exact per-tool filtering, verified native in the MCP proxy Phase 0: compliance & compatibility spike (hosted endpoint contract, App tokens) #22 spike) so tools/list shows the agent only its tools — defense-in-depth, with the proxy check authoritative. Deliberately not X-MCP-Toolsets, which fails open on invalid names (MCP proxy Phase 0: compliance & compatibility spike (hosted endpoint contract, App tokens) #22 finding F).
  • Audit: every frame logs [agent=<id> via <identity>]; denials log WARN … DENIED.
  • The client's X-Ghpool-Key never goes upstream (header set is built from an allowlist — test-asserted).

Tested

  • 41 tests (10 new): keys_match, Phase 1 open mode, 401 missing/wrong key (+ upstream never called), allowed call passes with X-MCP-Tools injected and global toolsets suppressed, denied call 403 (+ upstream never called), empty allowlist denies all, non-tools/call methods pass, multi-agent policy resolution. Clippy -D warnings clean.
  • Live on zz k3s (deployed via ConfigMap TOML + key in a k8s Secret):
    • no key → 401, wrong key → 401 (JSON-RPC error bodies)
    • valid key → session; tools/list filtered 27 → 4 tools by X-MCP-Tools
    • allowed issue_read200; denied search_code403
    • audit log: MCP tools/call issue_read [agent=kiro-zz via zz] [session=…] and MCP tools/call search_code DENIED [agent=kiro-zz]
    • kiro-cli end-to-end: agent pod's mcp.json gained one headers line; kiro read issue MCP proxy Phase 2 gate: agent authn + default-deny policy + GitHub App backend + writes #17's title through the authenticated proxy

Docs

config.example.toml [[mcp.agents]] reference; README "Per-agent authentication (Phase 2a)" section with key-delivery and TLS-termination guidance.

Out of scope (2b, per #17)

Repo allowlists + resource resolution, GitHub App credential backend, session-to-agent binding, fail-closed durable audit, write classification/safe-retry — writes stay off until all of 2b lands.

…wlist

Slice 2a of #17 (read-only scope; writes remain gated on 2b):

- [[mcp.agents]] config: id + key (secret-ref resolved at load) + tools
  allowlist. Empty = Phase 1 network-trust mode, fully backward compatible.
- authenticate(): with agents configured, every /mcp request requires a
  valid X-Ghpool-Key (sha256-digest comparison); 401 JSON-RPC error
  otherwise, audit-logged.
- Default-deny enforcement: tools/call not on the agent's allowlist -> 403
  at the proxy, never reaches upstream. Empty allowlist denies all calls.
- Per-agent X-MCP-Tools injection (exact allowlist, verified native
  filtering in #22) as defense-in-depth; deliberately NOT X-MCP-Toolsets
  (fails open on invalid names, #22 finding F). Global toolsets only apply
  in Phase 1 mode.
- Audit attribution: [agent=<id> via <identity>] on every frame; DENIED
  warns for policy rejections.
- Docs: config.example.toml [[mcp.agents]] + README Phase 2a section.

41 tests (10 new). Live-verified on zz k3s: 401 both paths, tools/list
filtered 27->4, allowed call 200, denied call 403, kiro-cli end-to-end
with key in mcp.json headers.
@chaodu-agent chaodu-agent merged commit 8153ee3 into main Jul 13, 2026
1 check passed
@chaodu-agent chaodu-agent deleted the mcp-agent-authn branch July 13, 2026 03:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant