mcp-assure
Deny-by-default assurance runtime for MCP-style tool calls.
The model proposes. The gate decides. Receipts remember.
mcp-assure is a plug-in control plane you put in front of tool execution: policy catalog, argument constraints, optional resource/audience binding checks, velocity and blast limits, freeze mode, and hash-chained decision receipts. It is not a full SOC, not a scanner, and not a claim that all agent misuse is impossible.
Built by Alex Price / StellarRequiem. Apache-2.0. Zero runtime dependencies (core).
Version 0.2.1 adds policy packs, host/decorator integration, FastMCP on_call_tool middleware, purple stress fixtures, and an optional verity hook.
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 (stateless core, auth expectations, extensions). Scanners help at build time; runtime authorization is still required at the moment of tools/call.
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]"
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.
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
Purple stress suite
Synthetic adversarial sequences (no network):
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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_assure-0.2.1.tar.gz.
File metadata
- Download URL: mcp_assure-0.2.1.tar.gz
- Upload date:
- Size: 32.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8cf00009f2d4eff9e962cf720800591fc7c7e636350543dfb738aae452f0e0d6
|
|
| MD5 |
da979061b44947e8d0a97768985de9d9
|
|
| BLAKE2b-256 |
af31cbe535eaef2bdb99038f6cc87cb7dbdf4e29352a12f90097d79e0f69cd3c
|
File details
Details for the file mcp_assure-0.2.1-py3-none-any.whl.
File metadata
- Download URL: mcp_assure-0.2.1-py3-none-any.whl
- Upload date:
- Size: 32.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
10b86cce55b337a23656269bde0ec3f8c76592c9a60cd72ddd45b9c36d074be6
|
|
| MD5 |
9163d94f28599c5430f88cd4d2abb7d4
|
|
| BLAKE2b-256 |
a102fa9a7298d767f50c0dcb5476cedc542630aeb244afc6aca1d32af93943e1
|