Skip to main content

mcp-gatehouse

CI PyPI Python License: MIT mcp-gatehouse MCP server

Permission tiers, approval gates, and audit logging for MCP servers. The server is the gatekeeper: you decide what an AI can read, what it can write, and what's off-limits — and every action gets logged.

Most MCP servers hand the model every tool at full strength and keep no record of what it did. That's fine for a demo. It's not fine the day an agent has write access to your CRM, your books, or your order system. mcp-gatehouse is the missing gate, enforced inside the server — no proxy, no external policy service, no dependencies beyond the official mcp SDK.

pip install mcp-gatehouse

What you get

Permission tiers Every tool is declared READ, WRITE, or DESTRUCTIVE — and the tier also emits honest spec ToolAnnotations (readOnlyHint / destructiveHint), which the wrapper won't let you override to lie.
Approval gates Tiers you choose require a sign-off before the tool runs. Your approver is any callable — a terminal prompt, a Slack ping, a ticket. Fails closed: a gated tool with no approver configured is denied, not waved through.
Audit log Append-only JSONL, one line per call — allowed, denied, or failed — with UTC timestamps and durations. The answer to "what did the AI actually do?" six months later.
Redaction Argument keys you name (api_key, password, token, … by default) are masked before they reach the log or the approver.
Denylist Block a tool outright, whatever its tier.

Quickstart

from mcp.server.mcpserver import MCPServer
from mcp_gatehouse import AccessTier, AuditLog, Gatehouse, Policy

mcp = MCPServer("order-desk")
gatehouse = Gatehouse(
    mcp,
    policy=Policy(approver=lambda req: input(f"allow {req.tool}? [y/N] ") == "y"),
    audit=AuditLog(path="audit.jsonl"),
)

@gatehouse.tool(tier=AccessTier.READ)
def lookup_order(order_id: str) -> str:
    """Look up an order's status."""
    ...

@gatehouse.tool(tier=AccessTier.DESTRUCTIVE)
def cancel_order(order_id: str) -> str:
    """Cancel an order. Runs only if the approver says yes."""
    ...

mcp.run()

That's the whole integration: build your MCPServer exactly as the SDK docs show, but register tools through the gatehouse. Schema generation, transports, and everything else work unchanged — the guard preserves the function's signature.

Under the default policy, DESTRUCTIVE requires approval and everything is audited. Gate writes too with one line:

Policy(require_approval=frozenset({AccessTier.WRITE, AccessTier.DESTRUCTIVE}), ...)

What the audit trail looks like:

{"ts": "2026-07-16T14:02:11+00:00", "tool": "lookup_order", "tier": "read", "outcome": "ok", "arguments": {"order_id": "4417"}, "duration_ms": 0.42}
{"ts": "2026-07-16T14:02:38+00:00", "tool": "add_note", "tier": "write", "outcome": "ok", "arguments": {"order_id": "4417", "note": "call back", "api_key": "«redacted»"}, "duration_ms": 1.08}
{"ts": "2026-07-16T14:03:05+00:00", "tool": "cancel_order", "tier": "destructive", "outcome": "denied", "reason": "approver refused", "arguments": {"order_id": "4417"}}

Try the demo

The package ships a runnable order-desk server with all three tiers wired up and a terminal-prompt approver:

mcp-gatehouse-demo

Point any MCP client at it over stdio (Claude Desktop, etc.), ask the model to cancel an order, and watch the approval land in your terminal — and the verdict land in audit.jsonl either way. examples/orders_server.py is the same server as a copyable template.

Design notes

  • Enforcement lives inside the server, at the tool boundary. A proxy can't see your tools' semantics, and a policy service is one more thing to deploy. A 40-person plant doesn't have a platform team; this is a few small classes and a JSONL file.
  • Fail closed. Security defaults that quietly allow are worse than none. That includes redaction: argument values the scrubber can't take apart (arbitrary objects, bytes) are replaced with an opaque placeholder rather than passed through, and exception messages stay out of the log — only the exception type is recorded, because error text loves to embed the very values you just redacted.
  • The audit log records denials and errors, not just successes — the calls that didn't happen are half the story.
  • A blocking terminal approver and the stdio transport don't mix — stdout/stdin are the protocol pipe. The demo's approver prompts on /dev/tty for exactly that reason (and denies when no terminal exists). Real deployments should approve out-of-band: Slack, a ticket, a queue.
  • What this is not: authentication, transport encryption, or a sandbox. It's a gate inside your server, not a perimeter around it. See SECURITY.md.

Compatibility

Targets the official mcp Python SDK v2.x (mcp>=2,<3) and Python 3.10+.

mcp-gatehouse SDK Server class
0.2.x mcp>=2,<3 MCPServer
0.1.x mcp>=1.27,<2 FastMCP

The public API (Gatehouse, Policy, AuditLog, AccessTier) is unchanged across that line, as promised. Porting a v1 server is two import edits — FastMCP became MCPServer and moved to mcp.server.mcpserver; see the SDK's migration guide.

Staying on SDK v1 needs no action: 0.1.x pins mcp<2, so pip keeps resolving it. That line is closed to features but still gets security fixes.

Who built this

Nick George — I design and run MCP servers in production for a mid-market reverse logistics-tech company, and build them for businesses at nickgeorgeai.com. This library is the permission-and-audit discipline from those builds, extracted.

If you're an owner or operator wondering what MCP even is, start with the plain-English guide: What is an MCP server?

License

MIT

Metadata

Release files for mcp-gatehouse 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcp-gatehouse 0.2.0
File Size Uploaded
mcp_gatehouse-0.2.0.tar.gz 16.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mcp-gatehouse 0.2.0
File Interpreter ABI Platform
mcp_gatehouse-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 30.6 kB

Release files / mcp_gatehouse-0.2.0.tar.gz

Download URL mcp_gatehouse-0.2.0.tar.gz
Size 16.9 kB
Tags Source
SHA-256 checksum
How to use checksums
2085ac38d1600b789285f99dc2a6b1ed5a5b2ce0233de31a44751c7be14004d7
BLAKE2b-256 checksum
How to use checksums
544522000c82ea329a28cc58c5650ae381f46ff823dc1780cef5b5849c4b4787
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / mcp_gatehouse-0.2.0-py3-none-any.whl

Download URL mcp_gatehouse-0.2.0-py3-none-any.whl
Size 13.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
aae24ade9123ad1986e12187d614a830c0aecdbf2641122b664d59608807ef50
BLAKE2b-256 checksum
How to use checksums
1b0e7c5dc9b99f54ecca7fd45ea2f86fd380e32b5bb7ff7da3fb42675b57d010
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

0.3.0

2 release files

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page