Skip to content

Tutorial: Govern an MCP tool

This tutorial shows how to add governance to tool calls in an MCP-connected pipeline — scanning arguments before the call and results after it.

How tool governance works

%%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#3f51b5', 'primaryTextColor': '#ffffff', 'primaryBorderColor': '#283593', 'lineColor': '#7986cb', 'secondaryColor': '#3949ab', 'tertiaryColor': '#5c6bc0', 'clusterBkg': '#e8eaf6', 'clusterBorder': '#7986cb', 'edgeLabelBackground': '#e8eaf6', 'titleColor': '#1a237e', 'nodeTextColor': '#ffffff'}}}%%
flowchart LR
    EX[Execute node] -->|tool call request| TCG[Tool-call guard]
    TCG -->|approved args| MCP[MCP tool]
    MCP -->|tool result| TRG[Tool-result guard]
    TRG -->|clean result| EX
    TCG -->|block| REF([Refused + audit])
    TRG -->|block| REF

The execute stage splices the guardrail contract into both directions:

  • Tool-call guard — scans model output (arguments) before the tool runs. Catches masked-data exfiltration attempts and requires approval for high-risk tools.
  • Tool-result guard — scans tool output before it re-enters the model context. The primary prompt-injection defence.

Configure tool governance in aegis.yaml

providers:
  main:
    type: anthropic
    api_key: secret://env/ANTHROPIC_API_KEY

guardrails:
  injection:
    pack: aegis.regex_guard
    mode: block

pipeline:
  tool_result: [injection]

routes:
  default:
    provider: main

The tool_result pipeline stage runs after every MCP tool result and after every RAG retrieval — both are untrusted inputs.

Per-tool require_approval

To pause and require human approval before a specific tool runs, add the tool to a per-tool policy:

pipeline:
  tool_call: [approval_gate]

When the model calls a tool governed by require_approval, the run pauses via a LangGraph interrupt. A human approves or denies via the Approvals UI (/approvals) or the CLI (aegis runs approve <run-id>).

Testing tool governance

Use FakeProvider with tool_calls_sequence to inject synthetic tool calls in unit tests — no real model or MCP server required:

from aegis_core.testing import FakeProvider

provider = FakeProvider(
    tool_calls_sequence=[
        [
            {"name": "search", "arguments": {"query": "hello"}},
        ]
    ],
)

The tool-result guard will scan the result of each call. A blocked result causes the run to end with status="blocked".

Next steps