Skip to main content

mcp-assure

ci License

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.2 adds proactive campaign detection (swarm/spray/path/template smells → escalate/freeze), agent_eval_strict pack, FastMCP middleware, policy packs, 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

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) or freeze (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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_assure-0.2.2.tar.gz (43.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_assure-0.2.2-py3-none-any.whl (44.0 kB view details)

Uploaded Python 3

File details

Details for the file mcp_assure-0.2.2.tar.gz.

File metadata

  • Download URL: mcp_assure-0.2.2.tar.gz
  • Upload date:
  • Size: 43.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.5

File hashes

Hashes for mcp_assure-0.2.2.tar.gz
Algorithm Hash digest
SHA256 969f3128377c3aeee9c4bb69873220ab7cc8e8c5101ee9480ba83dcfde618c40
MD5 01132b4a45a8dfac5dbe575710e05b94
BLAKE2b-256 5c2b0edb2f83b1e0b7e72e884d9de8b6f977cdbff7a8aca03bda16386d092b79

See more details on using hashes here.

File details

Details for the file mcp_assure-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: mcp_assure-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 44.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.5

File hashes

Hashes for mcp_assure-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 69447d8ccc43f0dd3296939a91c2001bfc59a17915aa73e50b443296589d1a8e
MD5 4a00652a4508288302cb5235cdd0ce1f
BLAKE2b-256 e80339e78f140f223915b071f4533bde4cb75d4daadb0a352cd0121f22183f0f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page