Declare your MCP endpoint in anDocumentation Index
Fetch the complete documentation index at: https://docs.agentguardian.io/llms.txt
Use this file to discover all available pages before exploring further.
agentguardian.yaml contract, then run
agent-guardian scan --contract. The transport speaks JSON-RPC 2.0 over
MCP Streamable HTTP, discovers tools via tools/list, and drives each
adversarial prompt through tools/call.
What this example tests
- The 10 ASI categories against the MCP server itself — tool poisoning, schema confusion, destructive-tool gating, session bleed — rather than a specific LLM client that happens to use the server.
- Supply-chain attacks specific to MCP:
asi04.mcp-server-poisonpayloads try to register an untrusted MCP server (https://tools.attacker.test/mcp, etc.) and check whether the target acknowledged or began using it. - Rules of Engagement enforcement: declare an allowlist/blocklist in YAML and the live RoE chokepoint blocks the call before contacting the server.
--mcp-url CLI flag.
MCP servers carry session state, tool surfaces, and (often)
OAuth-protected resources; the contract is the right place to declare
all three.
Source: src/agent_guardian/transports/mcp.py,
src/agent_guardian/contract/schema.py
(McpTransport).
Prerequisites
- AgentGuardian installed —
pip install agent-guardian. - An MCP server reachable over Streamable HTTP (default) or legacy SSE.
- For OAuth-protected servers: client credentials available via env
vars (the contract dereferences
${env:NAME}references). - A model spec —
--model stubfor an offline dry-run, or a real model spec for a graded assessment.
Run target
Createagentguardian.yaml next to your project. This is a minimal MCP
target with no auth and a stateless session, plus a Rules-of-Engagement
block that allow-lists one tool and blocks a destructive one:
agentguardian.yaml
McpTransport in
src/agent_guardian/contract/schema.py:
| Field | Type | Default | Notes |
|---|---|---|---|
kind | "mcp" | required | Discriminator |
url | URL | required | The MCP JSON-RPC endpoint |
transport_type | "streamable_http" | "sse" | "streamable_http" | Streamable HTTP is the modern default |
entry_tool | string | null | null | Tool to invoke each turn. When null, the first discovered tool is used |
prompt_argument | string | "input" | Argument name the adversarial prompt is mapped onto |
init_timeout_ms | int | 30000 | Timeout for the initialize handshake (and downstream RPCs) |
roe.tools.blocklist is the live RoE chokepoint: before any
tools/call the transport asks the gate whether the tool name is
allowed; a blocked tool never contacts the server (the transport
returns a benign blocked note and a recorded ToolCall instead — see
McpTransport.send in transports/mcp.py).
Pre-flight will fail if your allow/blocklist names a tool the server
did not advertise — the discovered set from tools/list is reconciled
against the RoE.
For OAuth-protected servers, swap the auth block (every field maps
1:1 to McpOAuthAuth in the schema):
McpOAuthProvider runs the full MCP authorization flow: fetches
{resource}/.well-known/oauth-protected-resource (RFC 9728
Protected-Resource-Metadata) to discover the authorization_servers,
performs authorization-code + PKCE (S256), and applies the bearer token
in the Authorization header.
Run AgentGuardian
--contract is mutually exclusive with the positional target /
--system-prompt / --endpoint / --framework modes. The contract
supplies the transport, auth, session, and RoE; budgets in the contract
map onto the swarm config, and a provenance audit is attached to the
report.
Flag-by-flag, every option below is verified against
src/agent_guardian/cli.py:
--contract PATH— drive the scan from a target contract (agentguardian.yaml).--model stub— offline default. Swap for a real model spec for a graded run.--mode fast—fast/smart/full(default).--output sarif --output-path mcp-scan.sarif— SARIF report, ready to upload to GitHub Code Scanning. Other formats:json,md,junit,pdf.
Expected output
The scan emits a standard AgentGuardian report. The target block reflects the contract-driven path and the discovered MCP transport:Mcp-Session-Id response header, the transport
captures it and replays it as a request header on every later call so
the server can resume the same session (this is MCP’s server_session
mode, advertised in session_modes).
A finding under asi04.mcp-server-poison (the in-tree MCP probe at
src/agent_guardian/probes/asi04/mcp-server-poison.yaml) means an
attacker payload tried to register an untrusted MCP server and the
target acknowledged or began using it. That is a supply-chain failure;
the remediation is the RoE allow-list.
Common errors
tool 'X' is not advertised by the server. The discovery step (tools/list) did not return a tool named in yourentry_toolor RoE allow/blocklist. Run the MCP server’s owntools/listagainst your endpoint to see what it actually exposes.401 Unauthorizedon every call. The server is OAuth-protected and you usedauth: kind: none(or omitted the block). Switch toauth: kind: mcp_oauthwith the client_id / scopes block above.- Scan finishes with
error.category: PROTOCOL. The server returned a JSON-RPCerrormember that didn’t match a refusal hint (forbidden,denied,not allowed,blocked,unauthorized). Check the server logs — a malformedinputSchemaor a missing required argument is the usual cause. EXIT_CONTRACT_INVALIDon startup. Youragentguardian.yamlfailed Pydantic validation. Runagent-guardian contract validate agentguardian.yamlto see the exact field error.
Next step
- Author your contract with the wizard:
agent-guardian contract new. - Compare with a framework-adapter target: Scan a LangGraph agent.
- Add the SARIF report to GitHub Code Scanning: Upload SARIF to GitHub Security.
- Read the MCP-specific probe under Attack library overview (ASI04 supply-chain).