Ahmed Doghri Logo Image
Ahmed Doghri

mcpinterlock

Agents shouldn't inherit ambient authority. mcpinterlock checks every tool call against an explicit policy and denies with evidence: one demo call comes back with three violations, each pinned to the field that caused it.

mcpinterlock agent tool-call safety interlock
mcpinterlock working browser demo

Tool Access Is Authority

An agent that can call a tool can cross a trust boundary: read a secret, write outside its workspace, reach an internal endpoint, or run a destructive operation without meaningful approval.

mcpinterlock validates JSON-RPC tool calls against an explicit policy: allowlisted tools, approval for destructive ones, path roots, HTTPS and network-target rules, and secret-shaped argument checks.

Deny With Evidence

Every decision carries stable violation codes and field paths, and can be written to SQLite for later review. The same policy engine runs through the CLI, the API, the local workbench, the tests, and the container.

The Demo

An ordinary-looking fetch request is denied three times over: the URL isn't HTTPS, it resolves to a non-public address, and the output path escapes the configured workspace. All three come back from a single check, with the field that caused each one.

The firewall mediates the calls presented to it. Real isolation still needs operating-system permissions, network controls, authenticated approval channels, and a carefully written server.

Two Findings in My Own Firewall

The first was a real gap in the address check. It blocked anything Python's ipaddress module called private, loopback, link-local, or reserved. Those four sound exhaustive and aren't: RFC 6598 carrier-grade-NAT space, 100.64.0.0/10, is used inside real cloud networks and is explicitly non-public, yet Python doesn't call it private. A hostname resolving there was allowed. Multicast had the same hole. The check now uses not is_global, the actual inverse of publicly routable unicast, plus an explicit multicast test, and every previously blocked target still gets blocked, including the IPv4-mapped IPv6 form.

The second turned up while auditing this page. The demo request targets a metadata endpoint over plain HTTP, so it was denied for the scheme and returned immediately. The address check never ran, and the violation list never mentioned that the target was also a non-public address. For a tool whose selling point is denying with evidence, reporting one reason out of two is the wrong half of the job. It now returns every applicable reason, the demo denies with three violations instead of two, and CI asserts that count so it can't quietly drop back.

The tests mock DNS resolution rather than reaching the network, so the whole suite runs offline on Python 3.10 through 3.13, and CI builds the image and loads the workbench before calling the build green.

Security Basis

The policy maps to MCP's own security guidance on confused-deputy risks, SSRF, least privilege, local-server compromise, and explicit user consent. Read the MCP security best practices.

Try It

Edit a tool call against a policy and watch the violations appear, with nothing leaving your tab: ahmeddoghri.github.io/mcpinterlock

No dependencies:

git clone https://github.com/ahmeddoghri/mcpinterlock && cd mcpinterlock
pip install -e .
mcpinterlock demo              # one call, three violations, each with its field
mcpinterlock serve             # workbench on :8090

Put your own policy and a real tool call in one JSON file and run mcpinterlock analyze call.json, then read the violation codes before you wire it into an agent.

Tools Used

Python
MCP
JSON-RPC
Policy Engine
SSRF Defense
Network Security
SQLite
Docker