MCP Gateway · v0.1.0
MCP tools are trusted on sight. Nothing checks that trust.
A tool becomes available in your agent's toolset, and its description gets read like any other trusted context — most MCP clients never scan it. The call's own arguments go straight to your dispatcher. The result comes back and gets acted on. And the tool itself can quietly change after you approved it, with nothing to catch it. MCP Gateway is four checkpoints for that exact lifecycle — admission, argument, result, and drift — composed into one guard object you wire into your existing tool-dispatch loop. Pip-installable, zero dependencies, works as a library or a CLI.
Where it's unguarded
Three points in an MCP tool call, unguarded.
- Admission
- A tool's description can carry an attack before it's ever called. An MCP server controls the natural-language description attached to every tool it offers. An agent reads that text just by having the tool listed as available — whether or not it ever calls it. A compromised or malicious server can embed an injection payload directly in a tool's own metadata.
- Call
- A tool call's own arguments can carry a dangerous shape. Once a tool is admitted, its call arguments are structured data from an untrusted source — the model, or whatever's upstream of it. A chained dunder-attribute gadget, a literal eval-looking string, a serialized-object marker: if your dispatcher ever does dynamic attribute lookup or deserialization on an argument, this is how that gets reached.
- Drift
- What you already approved can silently change. MCP tools are typically approved once, then trusted indefinitely. If the server serving a tool changes its description or schema later, a tool that was safe at review time can become a delivery vector — or start requesting more sensitive parameters — without ever being re-reviewed.
What it does
One object for the whole tool-call lifecycle.
- Admission · mcp_guard
-
In plain terms: before a tool is added to your agent's available toolset, its description gets scanned for injection the same way a spam filter checks an email before it hits your inbox — then checked against an allow/block policy.
Technically:
evaluate_tool_admission()runs the full prompt-injection detector against a tool's description and denies on a FLAGGED verdict regardless of the allowlist — an allowlisted NAME never launders a compromised description. A process-localToolCallLimiteris included for call-rate limiting per tool. - Argument · mcp_argument_guard
-
In plain terms: once a tool is admitted, every call's arguments get walked for shapes that would matter if your own dispatcher ever does anything clever with them — a review-trigger, not an automatic block, since a security-review tool's own legitimate queries about dangerous code trip the same patterns real injected code would.
Technically:
scan_tool_call_arguments()recursively walks a call's argument tree for chained dunder-attribute gadgets, eval/exec/os/subprocess-looking strings, and pickle/YAML deserialization markers — checked at every protocol version, not a hand-built prefix table. - Lifecycle · mcp_lifecycle
-
In plain terms:
McpLifecycleGuardis the object that actually ties this together —admit()once per tool, thenbefore_call()/after_call()around every invocation, instead of remembering to call three separate functions correctly at three separate points by hand.Also the third checkpoint:
after_call()runs the tool's RETURNED text through the same detector's output scanner — the mirror image of admission, catching exfiltration-shaped results (a credential-shaped string, a data-carrying URL) the tool itself produces. - Drift · mcp_tool_pinning
-
In plain terms: approve a tool's description and schema once; if it silently changes later — a compromised server, a poisoned registry update — the next check flags it before your agent trusts the new content.
Technically:
pin/checkhash-pin a tool's name, description, and schema with sorted-key canonical JSON, so incidental whitespace never masks a real change. Exits non-zero on any CHANGED tool — usable as a CI gate.
Real runs
Real runs, not a mockup.
Every line below is an actual terminal session against the real, shipped CLI — captured directly, not staged. Want both products in one place? The combined two-minute demo →
$ mcp-gateway scan-tools tools.json
[clean] search
[flagged] send_email
[agentic_hijack] 'AGENT INSTRUCTION:' marker - the shape of a payload smuggled into fetched content -> 'AGENT INSTRUCTION:'
[agentic_hijack] explicit instruction to act covertly -> 'silently exfiltrate'
mcp-gateway scan-tools: 2 tool(s) scanned, FLAGGED description(s) found
$ echo $?
1
$ mcp-gateway scan-call search arguments.json
[flagged] search
[dunder_chain] arguments.query: chained dunder-attribute access (SSTI/sandbox-escape gadget shape) -> 'x().__class__.__mro__[1].__subclasses__()'
$ echo $?
1
$ mcp-gateway pin tools.json tools.lock.json
mcp-gateway pin: pinned 2 tool(s) to tools.lock.json
$ mcp-gateway check tools.json tools.lock.json
mcp-pin: 2 tool(s), all unchanged.
$ echo $?
0
$ # ...the MCP server's own description for `search` changes later, unreviewed...
$ mcp-gateway check tools.json tools.lock.json
mcp-pin: 0 unchanged, 0 new, 1 CHANGED, 0 missing
CHANGED [search] pinned=f433516f9102... now=5b736edb6806... - definition differs
from what was approved at 2026-08-24T01:27:54Z. Re-review before trusting.
$ echo $?
1
Try it
Now run it on your own tool description.
Paste an MCP tool's description and scan it — live, in your browser, right now. A right-sized client-side subset of the real admission scanner's agentic-hijack patterns, not a canned response.
mcp_gateway.mcp_guard.scan_tool_description) checks 30+ categories across every kind of untrusted text this package handles, not just this one family — this is a taste of the mechanism, not the whole product.What's actually in it
What's actually in it.
- Pure standard library — zero pip dependencies to audit or update.
- Works as an importable library or a standalone
mcp-gatewayCLI. McpLifecycleGuard— one object,admit()→before_call()→after_call(), wired into your existing tool-dispatch loop.mcp-gateway pin/checkhash-pin an MCP tool list and flag any silent post-approval change.mcp-gateway scan-tools— a zero-setup first look at an unfamiliar MCP server's tool list, before you ever connect to it.mcp-gateway scan-call— one-off argument-shape scan for testing/demo use.- Fails closed on the one genuinely hard stop available: rate limiting. Argument findings are a review-trigger, never a silent auto-block.
- Ships the same adversarial self-red-team harness that guards the flagship detector — mutates known-caught attacks and fails CI on any new evasion.
- 1,399 tests, including a real standalone install-and-run check from outside the package's own source tree.
Pricing
One license, your whole org.
No per-seat fees. Unlimited use across your whole organization.
Flat. Unlimited environments and seats within your org. Covers unlimited use across your org: the library, the CLI, and updates for the year — admission + argument + result scanning, McpLifecycleGuard for your dispatch loop, drift pinning + CLI for CI gating.
Card checkout: clicking "Get it" opens Stripe's own hosted checkout page, and you're invited as a GitHub collaborator automatically on payment.
Trust & scope
What's real, and what this isn't.
Wiring an MCP client of your own?
No sales call needed to get started.