mcp-assure
Open-source security CLI + runtime for MCP-style agent tool calls.
The model proposes. The gate decides. Receipts remember.
mcp-assure is a local control plane you put in front of tool execution: policy catalog, argument constraints, optional resource/audience binding checks, velocity and blast limits, freeze mode, proactive campaign scoring, and hash-chained decision receipts. It is not a full SOC, not a hosted vuln scanner, and not a claim that all agent misuse is impossible.
Built by Alex Price / StellarRequiem. Apache-2.0. Zero runtime dependencies (core).
v0.3.0 — security CLI surface (status / check / evaluate) aligned with the open-security-CLI moment, without cloning cloud scanners. See docs/VS_CODEX_SECURITY.md.
Public page: xclusivexo.com/mcp-assurance/#mcp-assure
Why this exists
MCP hosts give agents tools. Tools are power. Under MCP 2026-07-28, more security responsibility sits with implementers. Code scanners help at build time; runtime authorization is still required at the moment of tools/call. Open security CLIs that only find bugs in git trees leave the live agent loop ungated — this project fills that layer.
Install
pip install mcp-assure
# optional FastMCP middleware:
pip install "mcp-assure[fastmcp]"
# from git (development tip):
pip install "git+https://github.com/StellarRequiem/mcp-assure"
# from a checkout:
pip install -e ".[dev,fastmcp]"
Security CLI (local, no API key)
mcp-assure status # what this is / is not
mcp-assure check # purple + synthetic campaign detector (CI entry)
mcp-assure evaluate --tool echo --args-json '{"text":"hi"}' --pack baseline
mcp-assure evaluate --tool read_file --args-json '{"path":"/proc/self/environ"}' --adaptive
mcp-assure purple
mcp-assure campaign
mcp-assure packs
mcp-assure verify-receipts ./mcp-assure-receipts.jsonl
mcp-assure demo
mcp-assure check exits non-zero if control-plane fixtures fail — use it in CI the way other security CLIs use scan, but for gate health, not SAST.
Real host demo (local)
Simulates an MCP host tools/call path with AssuredToolDispatcher:
python examples/host_demo.py
Expect: ALLOW for allowlisted tools, DENY for unknown tools / smuggled args, receipts verify.
60-second integration
from mcp_assure import (
AssureEngine,
AssuredRunner,
ToolCall,
ToolPolicy,
ToolPolicyRegistry,
)
registry = ToolPolicyRegistry([
ToolPolicy(
name="read_file",
required_args=("path",),
allowed_args=("path",),
forbidden_args=("token", "password"),
max_blast=1,
),
])
engine = AssureEngine(registry, receipts_path="./mcp-assure-receipts.jsonl")
def read_file(args):
# your real implementation
return open(args["path"], encoding="utf-8").read()
runner = AssuredRunner(engine, handlers={"read_file": read_file})
out = runner.invoke(ToolCall(tool="read_file", arguments={"path": "README.md"}))
# out["executed"] is True only if the gate ALLOWed
# out["verdict"]["receipt_hash"] is the audit seal for this decision
Property: on DENY / DRY_RUN, the handler is never called.
What it enforces (tested)
| ID | Property |
|---|---|
| P1 | Unknown tool → DENY |
| P2 | Empty catalog → DENY |
| P3–P4 | DENY/DRY_RUN never invoke handlers |
| P5–P6 | Velocity / blast limits |
| P7 | lab_only tools require lab_mode |
| P8 | Model notes cannot flip DENY→ALLOW |
| P9 | Receipt chain verifies; tamper fails |
| P10 | Freeze mode blocks non-allowlisted tools |
| P11 | Forbidden / disallowed args → DENY |
| P12 | Resource/audience mismatch → DENY when configured |
See THREAT_MODEL.md and CLAIMS.md.
Policy from JSON
import json
from mcp_assure import AssureEngine, ToolPolicyRegistry
with open("examples/policy.example.json") as f:
reg = ToolPolicyRegistry.from_mapping(json.load(f))
engine = AssureEngine(reg)
MCP-shaped payloads
from mcp_assure.mcp_types import tool_call_from_mcp
from mcp_assure import AssureEngine, ToolPolicy, ToolPolicyRegistry
engine = AssureEngine(ToolPolicyRegistry([ToolPolicy(name="echo")]))
call = tool_call_from_mcp({"name": "echo", "arguments": {"text": "hi"}})
print(engine.evaluate(call).as_dict())
Policy packs
from mcp_assure import AssureEngine, load_pack
engine = AssureEngine(load_pack("baseline"))
# also: mcp_authz_boundaries, strict_local
python -m mcp_assure packs
mcp_authz_boundaries encodes runtime gates for resource/audience-style failures (the class of bugs mcp-bench measures in scanners) — call-time enforcement, not a scanner replacement.
Host / FastMCP integration
from mcp_assure.integrations import AssuredToolDispatcher, assure_callable
# See mcp_assure/integrations/fastmcp_notes.py for patterns.
Dispatcher is the usual host hook for tools/call. Decorators wrap kwargs-style tool functions before registration.
Grok Build / cannot-bypass host
python examples/grok_host_wire.py
# AdaptiveGate + AssuredToolDispatcher(adaptive=True): handlers only via call_tool
from mcp_assure.integrations import AssuredToolDispatcher
host = AssuredToolDispatcher(engine, handlers, adaptive=True, source="grok-build")
Grok skill: ~/.grok/skills/mcp-assure/SKILL.md (/mcp-assure).
FastMCP middleware (on_call_tool)
Requires pip install "mcp-assure[fastmcp]" (FastMCP ≥2.9):
from fastmcp import FastMCP
from mcp_assure import AssureEngine
from mcp_assure.packs import load_pack
from mcp_assure.integrations import build_assure_middleware
engine = AssureEngine(load_pack("baseline"), receipts_path="receipts.jsonl")
mcp = FastMCP("secured")
mcp.add_middleware(build_assure_middleware(engine))
@mcp.tool
def echo(text: str) -> dict:
return {"echo": text}
On DENY the middleware raises ToolError and does not call the tool handler.
Demo: python examples/fastmcp_assured.py
Proactive campaign watch (adaptive)
Static allowlists are necessary but not sufficient: agentic campaigns hide in
volume and shape. Wrap the engine with AdaptiveGate:
from mcp_assure import AssureEngine, AdaptiveGate, ToolCall
from mcp_assure.packs import load_pack
engine = AssureEngine(load_pack("agent_eval_strict"), freeze_path="./FREEZE")
gate = AdaptiveGate(engine, auto_freeze=True)
out = gate.evaluate(ToolCall(tool="echo", arguments={"text": "ok"}))
print(out.snapshot.recommendation, out.verdict.code)
- Pre-block: path/IMDS, template/RCE-class, gzip+base64 packer markers →
PROACTIVE_ARG_BLOCK - Window score: swarm sources, tool spray, unknown-tool burst, probe-dominated traffic
- Adapt:
escalate(human before execute) orfreeze(touch freeze file; only freeze-allow tools)
python -m mcp_assure campaign-demo
See docs/PROACTIVE_DEFENSE.md.
Purple stress suite
Synthetic adversarial sequences (no network), including adaptive fixtures:
python -m mcp_assure purple
Optional verity hook
If verity-core is installed, claim-like tool results can be soft-checked:
from mcp_assure.verity_hook import maybe_verify_tool_result
maybe_verify_tool_result({"accuracy": 0.99, "sample_size": 5})
Not required; no-ops cleanly when verity is absent.
CLI
python -m mcp_assure demo
python -m mcp_assure packs
python -m mcp_assure purple
python -m mcp_assure verify-receipts ./mcp-assure-receipts.jsonl
Adversarial stance
This package is designed to survive hostile review:
- Explicit threat model and claim gate
- Properties P1–P12 locked to unit tests
- No runtime deps in the TCB surface
- Residual risk documented (host must not bypass the runner)
pytest -q
What this is not
- Not a replacement for OAuth authorization servers
- Not host EDR / network IDS
- Not “stops all prompt injection”
- Not a full enterprise SOC platform
Related work (StellarRequiem)
- mcp-bench — do scanners catch authz-logic bugs?
- scope-gate — deny-by-default research authorization
- verity-core — refuse bad claims; audit chains
License
Apache-2.0. Copyright 2026 Alex Price / StellarRequiem.
Metadata
Release files for mcp-assure 0.3.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mcp_assure-0.3.2.tar.gz | 49.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcp_assure-0.3.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 97.3 kB
Release files / mcp_assure-0.3.2.tar.gz
| Download URL | mcp_assure-0.3.2.tar.gz |
|---|---|
| Size | 49.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1cf2f16bdf4be1195497482efa9eab9c062e9d11ea2247e18a2aecebf017154c
|
|
BLAKE2b-256 checksum How to use checksums |
6fd47debb1afcbf03f71581dc99e8a06ec81326c731bcc1c4132d522c0fe65e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.5
|
Release files / mcp_assure-0.3.2-py3-none-any.whl
| Download URL | mcp_assure-0.3.2-py3-none-any.whl |
|---|---|
| Size | 47.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fd6c760c4f3ddf7775fd2e0e8f3523e200ddb94f89f7046c4b7fcfd0ea7071cf
|
|
BLAKE2b-256 checksum How to use checksums |
17da15e779131f86b1da2c60e502f539de6a0ba761ad0fea9f25c7c4aa5b952e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.5
|