Skip to content

KTCrisis/agent-mesh

Repository files navigation

Agent Mesh

GitHub release Go License

Guardrail for AI agents. Open-source sidecar proxy between AI agents and their tools — policy, human approval, and tracing without changing agent code.

One binary. One YAML config. Fail closed by default.

Works with Claude Code, Cursor, LangChain, CrewAI, or any agent that uses HTTP, MCP, or CLI tools.

Table of contents

Architecture

flowchart LR
    subgraph Agents["Agents"]
        A1["Claude Code / Cursor"]
        A2["LangChain / CrewAI"]
        A3["Any HTTP agent"]
    end

    subgraph Mesh["agent-mesh (sidecar proxy)"]
        direction TB
        REG["Registry<br/>(tools)"]
        RL["Rate limiter<br/>+ loop detect"]
        POL["Policy engine<br/>(glob, conditions)"]
        FWD["Forward"]
        APP["Approval store"]
        GRT["Grant store<br/>(sudo for agents)"]
        TRC["Trace store<br/>(JSONL + sessions)"]
        OTEL["OTEL exporter<br/>(file / stdout / OTLP)"]

        REG --> RL --> POL --> FWD
        POL -.approval.-> APP
        POL -.bypass.-> GRT
        FWD --> TRC
        TRC --> OTEL
    end

    subgraph Upstream["Upstream tools"]
        U1["MCP servers<br/>(stdio + SSE)"]
        U2["REST APIs<br/>(OpenAPI specs)"]
        U3["CLI binaries<br/>(terraform, gh, docker)"]
    end

    subgraph Observability["Observability"]
        O1["Jaeger / Tempo /<br/>Datadog / OTLP HTTP"]
        O2["traces-otel.jsonl"]
    end

    A1 -- MCP --> Mesh
    A2 -- HTTP --> Mesh
    A3 -- HTTP --> Mesh

    FWD --> U1
    FWD --> U2
    FWD --> U3

    OTEL --> O1
    OTEL --> O2
Loading

Import: OpenAPI specs (URL or file) · MCP servers (stdio + SSE) · CLI binaries Export: MCP server (stdio) · HTTP proxy (:port) · OTLP traces

The problem

When you connect tools directly to an AI agent, the agent gets unguarded access — no policy, no trace, no control.

The solution

Put Agent Mesh between the agent and its tools:

claude mcp add agent-mesh -- ./agent-mesh --mcp --config config.yaml

The agent sees a normal tool surface. Agent Mesh enforces policy and records traces on every call.

Install

Binary (recommended)

VERSION=$(curl -s https://api.github.com/repos/KTCrisis/agent-mesh/releases/latest | grep tag_name | cut -d '"' -f4)

# Linux amd64
curl -L "https://github.com/KTCrisis/agent-mesh/releases/download/${VERSION}/agent-mesh_${VERSION#v}_linux_amd64.tar.gz" | tar xz
sudo mv agent-mesh /usr/local/bin/

# macOS Apple Silicon
curl -L "https://github.com/KTCrisis/agent-mesh/releases/download/${VERSION}/agent-mesh_${VERSION#v}_darwin_arm64.tar.gz" | tar xz
sudo mv agent-mesh /usr/local/bin/

All releases: github.com/KTCrisis/agent-mesh/releases

From source

Requires Go 1.24+:

git clone https://github.com/KTCrisis/agent-mesh.git
cd agent-mesh
make install    # builds to ~/go/bin/agent-mesh with version metadata

agent-mesh --version
# agent-mesh v0.8.5 (bee2a9e) built 2026-04-16T07:11:16Z

Quick start

1. Write a config

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

policies:
  - name: claude
    agent: "claude"
    rules:
      - tools: ["filesystem.read_*", "filesystem.list_*", "filesystem.search_*"]
        action: allow
      - tools: ["filesystem.write_file", "filesystem.edit_file"]
        action: human_approval
      - tools: ["filesystem.*"]
        action: deny

  - name: default
    agent: "*"
    rules:
      - tools: ["*"]
        action: deny

Or auto-generate one:

agent-mesh discover --config config.yaml --generate-policy
agent-mesh discover --openapi https://petstore.swagger.io/v2/swagger.json --generate-policy

2. Plug into Claude Code

claude mcp add agent-mesh -- agent-mesh --mcp --config config.yaml

3. Use normally

Restart Claude Code. The agent sees the tools. Agent Mesh enforces the rules. Every call is traced.


Config reference

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>"

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"]

Agents call CLI tools like any MCP tool — terraform.plan, kubectl.get, gh.pr. 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: "params.amount"
          operator: "<"
          value: 500
      - tools: ["*"]
        action: deny
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.

Supervisor mode

supervisor:
  enabled: true          # hide approval tools from agents
  expose_content: false  # redact raw params → structural metadata

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

Other settings

port: 9090                                   # HTTP port (default 9090)
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

Features

Human approval

When a policy requires human_approval, the flow is non-blocking:

Claude calls filesystem.write_file
  → agent-mesh returns: "Approval required (id: a1b2c3d4)"
  → Claude calls approval.resolve(id: a1b2c3d4, decision: approve)
  → agent-mesh replays the original tool call
  → Result returned to Claude

Virtual MCP tools: approval.resolve, approval.pending. Also via CLI (mesh approve <id>) or HTTP API (POST /approvals/{id}/approve).

Temporal grants

Like sudo for agents — temporary override for repeated approvals:

"Grant filesystem.write_* for 30 minutes"
→ grant.create {tools: "filesystem.write_*", duration: "30m"}
→ All filesystem.write_* calls bypass approval for 30m
→ Traced as "grant:a1b2c3d4"

Virtual MCP tools: grant.create, grant.list, grant.revoke.

Grants only bypass human_approval. Tools marked deny remain blocked — policy edit required.

Rate limiting

Per-agent call limits with automatic loop detection:

Protection What it stops
max_per_minute Runaway loops
max_total Budget exhaustion
Loop detection Same tool + same params > 3x in 10s

Tracing & sessions

Every tool call is logged: agent, tool, params, policy decision, latency, approval metadata.

curl http://localhost:9090/traces?agent=claude&tool=filesystem.write_file
curl http://localhost:9090/sessions          # list sessions
curl http://localhost:9090/sessions/abc123   # session detail

Session IDs are propagated via X-Session-Id header or --mcp-session-id flag.

OpenTelemetry export

otel_endpoint: /path/to/traces-otel.jsonl   # file
otel_endpoint: stdout                        # debug
otel_endpoint: http://localhost:4318         # Jaeger, Tempo, Datadog

Each span includes agent.id, tool.name, policy.action, approval.*, and llm.token.* attributes. See docs/otel.md.

Supervisor protocol

External supervisor agents can poll GET /approvals?status=pending, evaluate with full context (recent traces, active grants, injection risk), and resolve with structured verdicts (reasoning, confidence). See docs/supervisor-protocol.md.


Commands & flags

agent-mesh (main binary)

agent-mesh [flags]                           # run proxy (HTTP or MCP mode)
agent-mesh discover [flags]                  # discover tools + generate policy
agent-mesh --version                         # print version
Flag Default Description
--config config.yaml Path to YAML config
--openapi OpenAPI spec URL (ephemeral, for quick tests)
--backend Backend base URL override
--port from config or 9090 Port override
--mcp false MCP mode (stdio JSON-RPC instead of HTTP)
--mcp-agent claude Agent ID for MCP-mode policy evaluation
--mcp-session-id auto-generated Session ID for MCP traces

discover flags: --openapi <url>, --config <path>, --generate-policy, --backend <url>.

mesh (approval CLI)

mesh pending                    # list pending approvals
mesh show <id>                  # full details
mesh approve <id>               # approve
mesh deny <id>                  # deny
mesh watch                      # interactive poll + prompt

Set MESH_URL to override the default http://localhost:9090.


API

Method Path Description
POST /tool/{name} Proxy a tool call through policy
GET /tools List all registered tools
GET /mcp-servers List connected MCP servers
GET /traces Query traces (?agent=...&tool=...)
GET /sessions List sessions (id, agent, event count, timespan)
GET /sessions/{id} Session detail
GET /otel-traces OTLP JSON spans (?agent=...&tool=...&limit=...)
GET /approvals List approvals (?status=pending&tool=filesystem.*)
GET /approvals/{id} Approval detail with context
POST /approvals/{id}/approve Approve (optional: reasoning, confidence)
POST /approvals/{id}/deny Deny (optional: reasoning, confidence)
GET /grants List active grants
POST /grants Create a grant
DELETE /grants/{id} Revoke a grant
GET /health Health check and stats
GET /version Version info

Project structure

agent-mesh/
├── cmd/
│   ├── agent-mesh/        # Main binary (entry point, wiring)
│   └── mesh/              # Approval CLI (pending/approve/deny/watch)
├── config/                # YAML config parsing + validation
├── registry/              # Tool registry (OpenAPI + MCP + CLI imports)
├── policy/                # Rule evaluation (globs, conditions, fail-closed)
├── proxy/                 # HTTP handler (auth → rate limit → policy → forward → trace)
├── mcp/                   # MCP client/server/transport (stdio + SSE)
├── approval/              # Channel-based approval store with timeout
├── grant/                 # Temporal grants (TTL-based sudo)
├── ratelimit/             # Sliding window + loop detection
├── supervisor/            # Content isolation + injection detection
├── exec/                  # Secure CLI execution (no shell, arg validation)
├── trace/                 # In-memory + JSONL + OTEL export
├── policies/              # Per-agent policy files (used with policy_dir)
├── examples/              # Example configs (filesystem, petstore, travel, langchain)
└── docs/                  # CLI tools guide, OTEL guide, supervisor protocol

Tests

go test ./...              # all tests
go test ./... -race        # with race detector

207 tests across 13 packages covering config parsing, policy evaluation, HTTP/MCP proxy flows, approval lifecycle, CLI execution security, rate limiting, tracing, OTEL export, supervisor content isolation, and injection detection.

Roadmap

  • Import OpenAPI (URL + file), MCP (stdio + SSE), CLI binaries
  • Policy engine with glob patterns + conditions
  • Human approval (non-blocking, virtual MCP tools, CLI, HTTP)
  • Temporal grants (sudo for agents)
  • Rate limiting + loop detection
  • Trace store + JSONL + OTEL export
  • Per-agent policy files + specificity sort
  • Session tracking
  • Supervisor protocol (content isolation, injection detection)
  • CLI tool governance (3 modes, secure exec)
  • OpenAPI config field (persistent import)
  • Dashboard UI (via agent7)
  • Durable state (approvals, grants, rate limits persisted across restarts)
  • agent-mesh serve daemon mode (persistent, multi-client)
  • Operator auth (separate identity from agent Bearer)
  • Session log durable + wake(sessionId) recovery
  • Policy hot-reload
  • Condition engine v2 (AND/OR/nested)

Why "Agent Mesh"

The same way Envoy sits between microservices and adds observability, auth, and rate limiting without changing service code — Agent Mesh sits between AI agents and their tools.

Agents don't know the proxy exists. They call tools, get results. The governance layer is invisible to the agent, visible to the operator.

License

Apache 2.0

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors