agent governance tutorial May 10, 2026

Governing Claude agents with 10 lines of YAML

The Anthropic Agent SDK builds Claude-powered agents in Python. flux7-mesh governs them with YAML policies. MeshHooks connects the two in 3 lines of code. Full walkthrough, 5 minutes.

While building flux7-mesh (a governance proxy for agent tool calls) and learning the Anthropic Agent SDK, I wanted to make mesh7 a native part of the Claude ecosystem. The Agent SDK ships with a hooks system that intercepts tool calls before execution. I wrote a small integration layer called MeshHooks that plugs mesh7's policy engine into those hooks. This tutorial shows how to set it up.

1 Install

You need two things: the mesh7 binary (Go) and the Python SDK.

# mesh7 binary
go install github.com/KTCrisis/flux7-mesh/cmd/mesh7@latest

# Python SDK with Agent SDK support
pip install flux7-mesh[anthropic]

2 Define your tools

For this example, the agent has three tools: reading files, writing files, and deleting files.

tools.py
import os

def read_file(path: str) -> str:
    """Read a file and return its contents."""
    with open(path) as f:
        return f.read()

def write_file(path: str, content: str) -> str:
    """Write content to a file."""
    with open(path, "w") as f:
        f.write(content)
    return f"Written to {path}"

def delete_file(path: str) -> str:
    """Delete a file."""
    os.remove(path)
    return f"Deleted {path}"

Nothing special here. Plain Python functions, no framework dependency, no decorator. mesh7 doesn't care how you implement your tools. It only sees their names and parameters when they're called.

3 Write the mesh7 config

In Agent SDK mode, mesh7 doesn't proxy the tool calls. It doesn't need to know your tools or how to reach them. It acts purely as a policy engine: the hooks send a tool name and parameters, mesh7 evaluates them against the rules, and returns a decision via POST /decide.

config.yaml
listen: ":9090"

policy:
  default_action: human_approval

  rules:
    - agent: "file-agent"
      tool: "read_file"
      action: allow

    - agent: "file-agent"
      tool: "write_file"
      action: allow
      conditions:
        - param: path
          pattern: "/tmp/*"

    - agent: "file-agent"
      tool: "delete_file"
      action: deny

That's the 10 lines that matter. Three rules, each binding an agent identity to a tool with an action:

Anything not matched by a rule falls back to human_approval, which means the call is queued and waits for a human to approve or deny it. This is the safety net: if the agent discovers a new tool or calls something you didn't anticipate, it won't execute silently.

4 Wire MeshHooks into your agent

This is where MeshHooks comes in. Three lines to connect your Agent SDK agent to the policy engine.

agent.py
from anthropic.agent import Agent, ToolDefinition
from mesh7 import MeshHooks
from tools import read_file, write_file, delete_file

# Connect to mesh7
hooks = MeshHooks(agent="file-agent")

# Define tools for the Agent SDK
tools = [
    ToolDefinition(
        name="read_file",
        description="Read a file and return its contents",
        input_schema={
            "type": "object",
            "properties": {"path": {"type": "string"}},
            "required": ["path"],
        },
        function=read_file,
    ),
    ToolDefinition(
        name="write_file",
        description="Write content to a file",
        input_schema={
            "type": "object",
            "properties": {
                "path": {"type": "string"},
                "content": {"type": "string"},
            },
            "required": ["path", "content"],
        },
        function=write_file,
    ),
    ToolDefinition(
        name="delete_file",
        description="Delete a file",
        input_schema={
            "type": "object",
            "properties": {"path": {"type": "string"}},
            "required": ["path"],
        },
        function=delete_file,
    ),
]

# Create the agent with governance hooks
agent = Agent(
    model="claude-sonnet-4-6",
    tools=tools,
    hooks=hooks.agent_sdk_hooks(),
)

result = agent.run("Read /tmp/notes.txt, then write a summary to /tmp/summary.txt")

The MeshHooks(agent="file-agent") constructor creates a set of hooks that the Agent SDK will call before every tool execution. Under the hood, MeshHooks sends the tool name and its parameters to mesh7's POST /decide endpoint, which evaluates the YAML policy and returns allow, deny, or ask. The agent ID "file-agent" is what links this agent instance to the rules you wrote in the config.

The hooks.agent_sdk_hooks() call returns a dict that the Agent SDK understands natively. It registers a PreToolUse hook on all tools (you can narrow the scope with a regex via tool_matcher if needed). The tool definitions and the agent creation are standard Agent SDK patterns, nothing mesh7-specific.

5 Run it

Two terminals:

# Terminal 1 — start mesh7
mesh7 --config config.yaml
# Terminal 2 — run the agent
python agent.py

Watch the mesh7 logs. You'll see every tool call evaluated against your policy:

INFO  tool_call agent=file-agent tool=read_file decision=allow     latency=0ms
INFO  tool_call agent=file-agent tool=write_file decision=allow    latency=0ms  path=/tmp/summary.txt
INFO  tool_call agent=file-agent tool=delete_file decision=deny    latency=0ms

If Claude tries to delete a file, the hook returns deny before the function is ever called. The agent receives a denial reason in the hook response, understands the action was blocked, and adapts its plan accordingly.

What happens under the hood

PYTHON PROCESS Claude MeshHooks PreToolUse hook Tool fn() tool call allow mesh7 POST /decide policy engine trace store config.yaml HTTP /decide allow / deny / ask
MeshHooks delegates policy evaluation to mesh7. The hook is a thin HTTP call.

The flow, step by step:

  1. Claude decides to call write_file with {"path": "/tmp/summary.txt", ...}
  2. The Agent SDK fires the PreToolUse hook before execution
  3. MeshHooks sends POST /decide to mesh7 with the tool name, params, and agent identity
  4. mesh7 evaluates the YAML policy: write_file is allowed for file-agent when path matches /tmp/*
  5. mesh7 returns allow. MeshHooks translates to the Agent SDK format
  6. The tool function executes. mesh7 logs a structured trace

One design choice worth noting: if mesh7 is unreachable, MeshHooks defaults to deny. Fail closed, not open. The idea is that if the governance layer is down, the agent shouldn't keep running ungoverned. You can change this with fail_action="allow" if you prefer leniency in development, but for production agents, closed is the safer default.

What about human_approval?

We've used allow and deny so far, which are immediate decisions. But human_approval is the more interesting case: the agent wants to do something, and mesh7 decides a human should validate it first.

When a tool call hits a human_approval rule, mesh7 queues the request. The hook blocks, and the agent waits. On the other side, a human can resolve the pending approval through several channels:

Once resolved, mesh7 unblocks the hook and returns the decision to the agent. If the human approved, the tool executes normally. If denied, the agent gets a denial reason and adapts.

The interesting part is what happens over time: if you connect flux7-memory, mesh7 records every approval as a queryable fact. After 3 consistent approvals for the same agent+tool pattern with 0 rejections, mesh7 starts auto-approving that pattern. The governance becomes less intrusive over time without becoming less safe.

Going further

This tutorial covers the simplest case. Everything below can be added without touching agent code. Only YAML and config:

FeatureConfig changeEffect
Human approvalaction: human_approvalTool call queued, agent waits, human approves/denies in console
Rate limitingrate_limit: {max: 10, window: 60s}Max 10 calls per minute per tool
Temporal grantsmesh7 grant create ...Temporary override: allow delete_file for the next 30 minutes
Auto-approveConnect flux7-memoryAfter 3 human approvals for the same pattern, auto-approve future calls
Multi-agentDifferent agent: per ruleEach agent gets its own permissions, same mesh7 instance
JWT authauth.jwt in configAgent identity from IdP tokens instead of strings

All of this is policy and configuration. Your Python agent code stays exactly the same. That's the separation that matters: the governance layer evolves independently from the agent code. Different repo, different team, different deploy cycle. A platform engineer can tighten a policy or add rate limiting without redeploying the agent, and a developer can add new tools without waiting for a policy review.