estoppl 0.12.0

Compliance proxy for AI agent tool calls — intercepts, signs, and logs MCP actions
estoppl-0.12.0 is not a library.

estoppl

See what your AI agent is doing. Stop it when it goes wrong.

estoppl is a transparent proxy for MCP (Model Context Protocol) that gives you full visibility into every tool call your agent makes — and lets you set guardrails so it can't do things it shouldn't.

stdio mode:
┌──────────────┐                  ┌─────────────┐                  ┌──────────────┐
│  Agent Host  │ ── stdin ──────▶ │   estoppl   │ ── stdin ──────▶ │  MCP Server  │
│  (Claude,    │ ◀── stdout ───── │             │ ◀── stdout ───── │  (local)     │
│   LangChain) │                  │  intercept  │                  └──────────────┘
└──────────────┘                  │  guardrails │
                                  │  log + sign │
HTTP mode:                        └──────┬──────┘
┌──────────────┐                  ┌──────┴──────┐                  ┌──────────────┐
│  MCP Client  │ ── POST/SSE ──▶ │   estoppl   │ ── POST/SSE ──▶ │  MCP Server  │
│              │ ◀── JSON/SSE ── │  :4100      │ ◀── JSON/SSE ── │  (remote)    │
└──────────────┘                  └──────┬──────┘                  └──────────────┘
                                         │
                                  ┌──────▼──────┐
                                  │  audit log  │
                                  │  (signed,   │
                                  │   chained)  │
                                  └─────────────┘

Why

AI agents call tools autonomously. Without visibility, you don't know what your agent did, how many times it called an API, or whether it tried something it shouldn't have. You find out when something breaks — or when you get the bill.

Estoppl fixes this:

  • Visibility — every tool call is logged with timestamps, inputs (hashed), outputs (hashed), and latency
  • Guardrails — block specific tools, set amount thresholds, flag sensitive operations for human review
  • Tamper-evident audit trail — signed, hash-chained events that prove what happened and when

One config line. Zero code changes. Sub-millisecond overhead.

Install

Homebrew (macOS and Linux):

brew tap estoppl/tap
brew install estoppl

npm (any platform with Node.js):

npx estoppl
# or install globally
npm install -g estoppl

Cargo (Rust toolchain):

cargo install estoppl

Binary download (no dependencies):

# macOS Apple Silicon
curl -L https://github.com/estoppl/estoppl/releases/latest/download/estoppl-darwin-aarch64.tar.gz | tar xz
sudo mv estoppl-darwin-aarch64 /usr/local/bin/estoppl

# macOS Intel
curl -L https://github.com/estoppl/estoppl/releases/latest/download/estoppl-darwin-x86_64.tar.gz | tar xz
sudo mv estoppl-darwin-x86_64 /usr/local/bin/estoppl

# Linux x86_64
curl -L https://github.com/estoppl/estoppl/releases/latest/download/estoppl-linux-x86_64.tar.gz | tar xz
sudo mv estoppl-linux-x86_64 /usr/local/bin/estoppl

# Linux ARM64
curl -L https://github.com/estoppl/estoppl/releases/latest/download/estoppl-linux-aarch64.tar.gz | tar xz
sudo mv estoppl-linux-aarch64 /usr/local/bin/estoppl

Quick start

# Initialize config, keypair, and database
estoppl init --agent-id my-agent

# Start the proxy — stdio mode (wraps a local MCP server process)
estoppl start --upstream-cmd npx --upstream-args @stripe/mcp-server

# Start the proxy — HTTP mode (reverse proxy for remote MCP servers)
estoppl start-http --upstream-url http://localhost:3000/mcp

# See what your agent has been doing
estoppl audit -n 50

# Generate an HTML report
estoppl report

MCP client configuration

stdio mode (local MCP servers)

Drop the proxy into your MCP client config — one change, zero code modifications:

{
  "mcpServers": {
    "stripe": {
      "command": "estoppl",
      "args": [
        "start",
        "--upstream-cmd", "npx",
        "--upstream-args", "@stripe/mcp-server"
      ]
    }
  }
}

HTTP mode (remote MCP servers)

For MCP servers running over HTTP (Streamable HTTP transport), run the proxy as a reverse proxy:

# Start the proxy (listens on 127.0.0.1:4100 by default)
estoppl start-http --upstream-url https://mcp.stripe.com/v1

# Point your MCP client at the proxy instead of the upstream server
{
  "mcpServers": {
    "stripe": {
      "url": "http://127.0.0.1:4100"
    }
  }
}

The HTTP proxy supports the full MCP Streamable HTTP transport: POST (JSON-RPC requests, including batches), GET (SSE streams for server-initiated messages), and DELETE (session termination). Session IDs and auth headers are forwarded transparently.

Your agent doesn't know estoppl is there. Every tool call passes through transparently.

Configuration

estoppl init generates an estoppl.toml:

[agent]
id = "my-agent"
version = "0.1.0"

[rules]
# Block these tools entirely — they never reach the MCP server
block_tools = []

# Flag these for human review (call goes through, logged as HUMAN_REQUIRED)
human_review_tools = ["wire_transfer", "execute_trade"]

# Block any tool call where the amount exceeds this value
max_amount_usd = 50000.0

# Where to find the amount in tool arguments (supports dot notation)
amount_field = "amount"

# Max calls per tool per minute (0 = unlimited). Prevents runaway agents.
# rate_limit_per_minute = 30

# Per-tool rate limit overrides
# [rules.rate_limit_tools]
# "stripe.create_payment" = 5
# "execute_trade" = 10

[ledger]
db_path = ".estoppl/events.db"
# Cloud sync — stream signed events to the Estoppl cloud ledger
# cloud_endpoint = "https://api.estoppl.com/v1/events"
# cloud_api_key = "sk_your_key"

Guardrails

Block tools — tool calls matching these names are rejected before reaching the upstream server. The agent gets a JSON-RPC error. Supports wildcards: "stripe.*" blocks all Stripe tools.

Human review — tool calls go through but are flagged as HUMAN_REQUIRED in the audit log. Use this for sensitive operations you want visibility into.

Amount thresholds — tool calls with an amount field exceeding the limit are blocked automatically. Catches runaway agents before they do damage.

Rate limiting — cap how many times any tool (or a specific tool) can be called per minute. If an agent enters a loop calling Stripe 200 times, it gets cut off after the limit. Configure globally with rate_limit_per_minute or per-tool with rate_limit_tools.

Audit log

Every tool call produces a signed event:

EVENT      TOOL                           DECISION     TIMESTAMP              LATENCY
a1b2c3d4   stripe.create_payment          ALLOW        2026-03-05 14:23:01    2ms
e5f6g7h8   wire_transfer                  HUMAN_REQ    2026-03-05 14:23:03    1ms
i9j0k1l2   stripe.create_payment          BLOCK        2026-03-05 14:23:05    0ms

Each event is:

  • Signed with Ed25519 — proves the proxy produced it
  • Hash-chained — each event links to the previous one; tampering breaks the chain
  • Sequence-numbered — monotonically increasing per proxy instance; enables gap detection during cloud sync
  • Zero-retention — only SHA-256 hashes of inputs/outputs are stored, never raw data

Verify the chain hasn't been tampered with:

estoppl audit --verify
# Hash chain INTACT — 847 events verified

Generate an HTML report to share with your team:

estoppl report --output report.html

Cloud sync

Stream your signed audit events to the Estoppl cloud for centralized monitoring, alerting, and compliance evidence.

# Configure the cloud endpoint in estoppl.toml
# [ledger]
# cloud_endpoint = "https://api.estoppl.com/v1/events"
# cloud_api_key = "sk_your_key"

# Start the proxy with cloud sync enabled
estoppl start --upstream-cmd npx --upstream-args @stripe/mcp-server --sync
estoppl start-http --upstream-url http://localhost:3000/mcp --sync

The --sync flag starts a background task that:

  • Polls local SQLite for new events every 5 seconds
  • Batches events (up to 100) and POSTs them to the cloud endpoint
  • Tracks a sync watermark so it picks up where it left off (survives restarts)
  • Retries with exponential backoff on failures (1s → 2s → 4s → ... capped at 5min)

All events stay in the local audit log regardless of sync status. The cloud is additive — if the network is down, events queue locally and sync when connectivity returns.

Chain integrity under network partition

Every event carries a monotonically increasing sequence_number that is included in the event hash (tamper-evident). Each sync batch includes chain metadata:

  • Sequence range (first_sequence / last_sequence) — lets the cloud detect gaps (e.g., "I have 1-50, you sent 53-100, where are 51-52?")
  • expected_prev_hash — the event hash the cloud should already have for the event before this batch, proving chain continuity across batch boundaries
  • batch_hash — SHA-256 of all event hashes in the batch, verifying nothing was tampered with in transit

If the cloud detects a gap, it responds with gap_from_sequence and the proxy automatically rewinds its cursor and re-sends the missing events. The local hash chain stays valid regardless of sync state — events are always chained correctly in SQLite first, then synced to cloud as a best-effort background operation.

Project structure

src/
├── main.rs          CLI entry point (clap)
├── config/          Configuration loading and defaults
├── mcp/             MCP JSON-RPC type definitions
├── identity/        Ed25519 key management and signing
├── policy/          Rules-based policy engine
├── ledger/          Local SQLite storage with hash chaining
├── proxy/           stdio + HTTP/SSE proxy core
├── sync/            Cloud sync background task
└── report/          HTML compliance report generator

Roadmap

Current (v0.4)

  • stdio proxy mode (transparent MCP interception)
  • HTTP/SSE proxy mode (MCP Streamable HTTP transport — POST, GET SSE, DELETE)
  • JSON-RPC batch support (mixed blocked + allowed in same batch)
  • Guardrails: tool block/allow lists, amount thresholds, human review flags
  • Ed25519 event signing
  • Hash-chained local SQLite audit log
  • CLI: init, start, start-http, audit, report, tail, stats
  • HTML activity report
  • estoppl tail — live-stream tool calls in your terminal as they happen
  • Rate limiting / circuit breaker — block tools called more than N times per minute
  • estoppl stats — tool call volume, latency percentiles, per-tool and per-session breakdown
  • Audit filters — --tool, --decision, --since
  • CI + prebuilt binaries (macOS, Linux) via GitHub Releases
  • --sync flag — stream signed events to cloud endpoint (bridge to cloud dashboard)

Next (v0.5)

  • Homebrew tap (brew install estoppl)
  • npm wrapper package (npx estoppl — binary distribution, no Rust required)
  • estoppl wrap — auto-detect and wrap existing MCP client configs (Claude Desktop, Cursor) in one command
  • Blocking human review — HUMAN_REQUIRED tools pause and wait for explicit approval before forwarding
  • estoppl dashboard — local web UI for browsing audit events, guardrail hits, and agent activity
  • OPA (Open Policy Agent) integration for enterprise policy management

Future

  • Cloud dashboard with real-time event feed and alerting
  • Cloud ledger with immutable WORM storage for regulated industries
  • OpenAI function calling interception (beyond MCP)
  • A2A (Agent-to-Agent) protocol interception for multi-agent delegation chains
  • Framework-agnostic compliance report templates (EU AI Act, SEC, SOC 2)
  • Agent Skill provenance logging
  • Kubernetes sidecar deployment
  • Cross-org agent trust verification

For regulated teams

If you're in financial services and need legally defensible audit records — immutable WORM storage (SEC 17a-4), regulatory evidence packs, cross-org trust verification — see estoppl.ai for the enterprise platform. This proxy is the open-source foundation; the cloud ledger is where compliance certification lives.

License

Apache 2.0