Skip to content

Configuration

Every feature is declared in a single YAML file. This page is the complete reference; Getting Started shows the minimal config, Writing Policies goes deep on rules.

All features are declared in a single YAML config.

MCP servers

mcp_servers:
  - name: filesystem
    transport: stdio
    command: npx
    args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/me"]

  - name: remote-service
    transport: sse
    url: "https://mcp-server.example.com/sse"
    headers:
      Authorization: "Bearer <token>"

  - name: huggingface
    transport: streamable-http
    url: "https://huggingface.co/mcp"

streamable-http is the transport hosted MCP servers now default to. It holds no long-lived stream: each JSON-RPC request is a POST whose response carries the answer, either as JSON or as an event stream. Sessions (Mcp-Session-Id) are handled transparently, and cross-origin redirects are refused so a compromised upstream cannot relay your tool calls elsewhere.

OpenAPI specs

Import REST APIs as governed tools — persisted across restarts.

openapi:
  # From URL
  - url: https://date.nager.at/swagger/v3/swagger.json

  # From local file
  - file: ./specs/internal-api.json
    backend_url: http://localhost:3001

Each endpoint becomes a tool (e.g. get_public_holidays). Same policy, same traces as MCP tools.

CLI tools

Wrap any CLI binary behind policy, approval, and tracing:

cli_tools:
  - name: gh
    bin: gh
    default_action: allow

  - name: terraform
    bin: terraform
    default_action: human_approval
    commands:
      plan:
        timeout: 120s

  - name: kubectl
    bin: kubectl
    strict: true      # only declared commands, everything else denied
    commands:
      get:
        allowed_args: ["-n", "--namespace", "-o"]

  - name: jq          # binary without subcommands
    bin: jq
    default_action: allow
    bare:
      allowed_args: ["-r", "--compact-output"]

Agents call CLI tools like any MCP tool — terraform.plan, kubectl.get, gh.pr. A bare binary registers a single <name>.run tool. Every CLI tool accepts an optional stdin param, piped to the process as data (never shell-interpreted).

default_action is the floor for the dynamic dispatcher (<name>.__dispatch), which is where undeclared subcommands land. It can only restrict, never widen, and defaults to deny — so a glob such as terraform.*: allow cannot hand over destroy along with plan. See docs/cli-tools.md.

Policies

YAML-based, first-match-wins, glob patterns for agents and tools.

policies:
  - name: support-agent
    agent: "support-*"
    rate_limit:
      max_per_minute: 30
      max_total: 1000
    rules:
      - tools: ["*.read_*", "*.list_*", "*.get_*"]
        action: allow
      - tools: ["create_refund"]
        action: allow
        condition:
          field: "amount"        # the path starts at the tool's arguments
          operator: "<"
          value: 500
      # Conditions read strings too, so a rule can look inside the call
      # rather than only at its name. A list means "any of these".
      - tools: ["Bash"]
        action: deny
        condition:
          field: "command"
          operator: "contains"
          value: ["mkfs", "> /dev/sd", "/etc/sudoers"]
      - tools: ["*"]
        action: deny

Operators: < <= > >= == != on numbers, == != contains not_contains starts_with not_starts_with on strings. String matching is literal and case-sensitive: it raises the floor against accidents, it is not a sandbox. See writing-policies.md.

Action Behavior
allow Forward to backend, return result
deny Block the call, return denial
human_approval Require human approval before forwarding

Fail closed: no matching rule = deny.

Per-agent policy files

One file per agent, drop-in/drop-out:

# config.yaml
policy_dir: ./policies   # load all *.yaml from this directory
# policies/scout7.yaml
name: scout7
agent: "scout7"
rate_limit:
  max_per_minute: 30
rules:
  - tools: ["searxng.*", "fetch.*", "ollama.*", "memory.*"]
    action: allow
  - tools: ["*"]
    action: deny

Files are loaded alphabetically after inline policies:. Duplicate names produce an error.

Policy hot-reload

Policies are reloaded automatically when files change — no restart required. The daemon watches:

  • config.yaml (inline policies: section)
  • policy_dir/ (all *.yaml files)

Changes are debounced (200ms) and validated before applying. If the new YAML is invalid, the current policies are kept and the error is logged. Rate limits defined in policies are also reloaded.

# Add a new agent policy at runtime — takes effect in <1s
echo 'name: temp-agent
agent: "temp"
rules:
  - tools: ["weather.*"]
    action: allow' > policies/temp.yaml

# Remove it — reverts immediately
rm policies/temp.yaml

Hot-reload covers policies and rate limits only. Changes to MCP servers, CLI tools, or OpenAPI specs require a restart.

Supervisor mode

supervisor:
  enabled: true          # hide approval tools from agents
  expose_content: false  # redact raw params → structural metadata
  supervisor_agents:     # agent IDs (glob) allowed to see approval tools
    - "supervisor-*"

When enabled, approval.resolve and approval.pending are hidden from agents — only an external supervisor can resolve approvals. See docs/supervisor-protocol.md.

Agents matching supervisor_agents globs are whitelisted: they see and can call approval tools even in supervisor mode. This enables a Managed Agent (e.g. Claude via MCP Streamable HTTP) to act as a cloud supervisor — connecting to POST /mcp with Authorization: Bearer agent:supervisor-claude and resolving approvals with Claude's judgment.

Memory integration

Persist approval decisions as queryable facts in mem7. Fire-and-forget — a failing mem7 never blocks approvals.

memory:
  url: http://localhost:9070    # mem7 daemon URL
  token: ""                     # optional Bearer token

When configured, every approval resolve (approve, deny, timeout) is written to mem7 as a fact with tags [decision, approved|denied, <tool>, agent:<id>].

Auto-approve from past decisions — when memory.url is set, mesh7 queries mem7 before submitting to the approval queue. If a tool+agent pattern has 3+ consistent approvals with 0 rejections, it is auto-approved (traced as supervisor:mem7). Governance gets less intrusive over time without getting less safe.

supervisor:
  auto_approve: true     # default true when memory.url is set
  min_approvals: 3       # threshold for auto-approve (default 3)

The auto-approve is a pre-filter (Level 1). If it can't resolve, the request proceeds to the external supervisor (if running) or human. If mem7 is down, the request is escalated — never blocked. See docs/mem7-auto-approve.md for a step-by-step example.

Authentication

Two planes, two guards:

auth:
  # Control plane (traces, grants, approvals, policies, sessions, metrics).
  # When set, these endpoints require `Authorization: Bearer <token>`.
  # When empty, they are restricted to loopback callers only.
  # MESH_ADMIN_TOKEN env overrides this value.
  admin_token: "a-long-random-secret"

  # Data plane: validate agent identity via JWT against an external IdP.
  jwt:
    jwks_url: https://idp.example.com/.well-known/jwks.json
    issuer: https://idp.example.com      # optional
    audience: mesh7                      # optional
    agent_claim: sub                     # claim used as agent id (default: sub)
    user_claim: ""                       # claim naming the human the agent acts for (default: off)
    allow_legacy: false                  # keep plaintext "agent:<id>" off when JWT is on

  # Reject data-plane requests with no credentials (401) instead of letting
  # them resolve to "anonymous" and fail closed at the policy engine.
  require_authentication: false
Setting Guards Default behavior when unset
admin_token Control plane — a caller here can mint grants and resolve approvals, overriding what policies enforce Loopback-only
jwt Data-plane identity — cryptographic agent id instead of the spoofable agent:<id> header. With user_claim set, a delegation-shaped token also carries the human the agent acts for, recorded on every trace (user_id) and OTel span (enduser.id) Plaintext identity accepted
require_authentication Anonymous access to /tools and /mcp-servers enumeration Anonymous allowed, governed by policy

The data plane (tool calls, /decide, /mcp, /health) is never gated by admin_token. Details: control-plane auth and JWT authentication.

Other settings

port: 9090                                   # HTTP port (default 9090)
storage_path: state.db                       # SQLite durable state (approvals, grants survive restarts)
trace_file: traces.jsonl                     # JSONL persistence
otel_endpoint: /path/to/traces-otel.jsonl    # or "stdout" or "http://localhost:4318"
approval:
  timeout_seconds: 300                       # approval TTL (default 5 min)
  notify_url: https://hooks.slack.com/...    # webhook on new pending approval