MCP Tool Risk Contract

What this adds. SecurityRecipes now treats MCP tool metadata as risk vocabulary, not enforcement. The contract lets an agent host or MCP gateway use annotations safely while still relying on deterministic scope, authorization, sandbox, network, approval, and output controls.

MCP tools can now declare behavior with annotations such as readOnlyHint, destructiveHint, idempotentHint, and openWorldHint. That is valuable, but the MCP specification is clear: clients must treat annotations as untrusted unless they come from a trusted server. The MCP Tool Risk Contract turns that reality into a reviewer-ready control surface. Rechecked August 23, 2026: MCP 2026-07-28 is still current and stateless. There is no negotiation handshake. Each request carries protocol version and capabilities. Servers MUST implement server/discover. --session-id and kill_session here are local run identifiers and host-session kill switches, not Mcp-Session-Id. Streamable HTTP revisions through 2025-11-25 could assign that header; 2026-07-28 ignores it and does not mint session IDs.

The core policy is simple: before a tool call runs, decide whether the session has private data, untrusted content, and an external or state-changing capability in the same execution path. If it does, the call is denied unless there is an explicit approval/control path. This makes tool risk easy for enterprise teams to reason about without pretending the model can reliably separate user instructions from attacker-controlled content.

Generated artifact

  • Profile: data/assurance/mcp-tool-risk-contract-profile.json
  • Generator: scripts/generate_mcp_tool_risk_contract.py
  • Runtime evaluator: scripts/evaluate_mcp_tool_risk_decision.py
  • Evidence pack: data/evidence/mcp-tool-risk-contract.json
  • MCP tools: recipes_mcp_tool_risk_contract, paired with recipes_playbook_plan using playbook id mcp-tool-risk-contract.

Regenerate and validate:

python3 scripts/generate_mcp_tool_risk_contract.py
python3 scripts/generate_mcp_tool_risk_contract.py --check

Evaluate one proposed tool call:

python3 scripts/evaluate_mcp_tool_risk_decision.py \
  --workflow-id vulnerable-dependency-remediation \
  --namespace repo.contents \
  --tool-name repo.contents.patch \
  --requested-access-mode write_branch \
  --agent-id sr-agent::vulnerable-dependency-remediation::codex \
  --run-id run-ci \
  --session-id session-ci \
  --correlation-id corr-ci \
  --server-trusted \
  --read-only-hint false \
  --destructive-hint false \
  --idempotent-hint false \
  --open-world-hint true \
  --human-approval-id approval-ci \
  --expect-decision allow_with_confirmation

Workflow at a glance

MCP Tool Risk Contract workflow

Classify an MCP tool's data access, side effects, reversibility, authority, argument controls, and approval needs before exposure to agents.

mcp-governance
  1. Signal

    Describe the tool

    Capture server, tool name, schema, purpose, owner, data, targets, credentials, environments, side effects, and dependencies.

  2. Scope

    Analyze capability risk

    Rate read/write/admin authority, sensitive data, destructive or irreversible effects, scope, concurrency, network, and confused-deputy risk.

  3. Decision

    Define required controls

    Specify argument/path validation, tenant isolation, auth, rate limits, dry-run, idempotency, approvals, telemetry, and rollback.

  4. Action

    Assign an operating mode

    Approve read-only, allow bounded writes, require per-call approval, restrict to pilot, hold, or deny exposure.

  5. Proof

    Publish the contract

    Record schema hash, risk tier, controls, allowed workflows/identities, examples, owner, expiry, and drift triggers.

Decision gate

Are tool capabilities fully declared and controlled for their maximum data and side-effect impact?

Proceed

Expose the tool only in its contracted operating mode and scope.

Hold or stop

Hold or deny unknown, overbroad, unvalidated, destructive, or unowned capabilities.

Evidence to retain

  • tool schema/capability inventory
  • risk/control assessment
  • operating-mode decision

Expected outputs

  • MCP tool risk contract
  • approval policy
  • tool remediation backlog

Decision model

Decision Meaning
allow_tool_call The call fits workflow scope, trusted annotations, and session-combination policy.
allow_with_confirmation The call can proceed only with a durable human approval or confirmation record.
hold_for_tool_risk_review Evidence is missing, annotations are untrusted for the risk level, or the tool is sensitive.
deny_annotation_contradiction Runtime request contradicts the tool annotations, such as read-only metadata on a write call.
deny_session_exfiltration_path The session combines private data, untrusted content, and external or state-changing capability without approval.
deny_scope_drift Namespace, connector, access mode, or workflow is outside the generated contract.
kill_session_on_tool_risk_signal A kill signal appeared: secret-bearing arguments/results, tool-list drift after approval, private-network destination, or approval bypass.

What gets scored

The generator reads the MCP connector trust pack, authorization conformance pack, workflow manifest, and gateway policy. It produces a profile for every MCP namespace with:

  • trusted vs untrusted annotation source
  • suggested standard annotations
  • risk tier
  • private-data, untrusted-content, exfiltration, state-change, and approval-required factors
  • authorization conformance state
  • workflow-level combination risk

The pack is intentionally conservative. Open-world tools taint the session; untrusted annotations never reduce friction for sensitive tools; write and non-idempotent calls need approval; tool-list changes after approval are kill signals.

Source anchors

See also