Skip to main content

MCP observability and control plane — audit logging, rate limiting, circuit breaker for any MCP server

Project description

👁️ mcp-warden

MCP observability and control plane.

pip install mcp-warden

mcp-warden audit -- your-mcp-server [args...]
mcp-warden guard --max-calls-per-minute 30 --block-tool write_query -- any-mcp-server
mcp-warden both  --max-calls-per-minute 60 --max-response-kb 512 -- any-mcp-server
mcp-warden log   --tail 20

PyPI version Python 3.9+ License: MIT CI Zero dependencies


The problem

MCP clients like Cline and Claude Desktop can call tools on any connected server with no visibility and no throttle.

An agent in a loop can:

  • Call sql_query hundreds of times a minute, burning your API budget
  • Pull back 50 MB of data in a single tool call
  • Hit a tool you meant to leave off
  • Fail repeatedly and nobody notices until the damage is done

mcp-warden fixes all of this — for any MCP server, without touching it.


How it works

MCP client (Cline / Claude Desktop)
    │  stdin/stdout
    ▼
mcp-warden  ◄──── audit + guard
    │  subprocess stdin/stdout
    ▼
Upstream MCP server (any MCP server)

mcp-warden is a transparent JSON-RPC relay. The client never knows it's there. The upstream server never knows it's there. Every tools/call passes through the audit and guard layer in the middle.

Latency impact: ~0.1–0.5 ms per message — negligible against typical LLM tool call round trips of 100 ms to 10 s.


Install

pip install mcp-warden

Zero runtime dependencies. Pure Python 3.9+ stdlib.


Usage

Audit — log everything

mcp-warden audit \
  --audit-log ~/.mcp_warden/audit.jsonl \
  --session-id cline \
  -- your-mcp-server [args...]

Each line of the JSONL log is a self-contained event:

{"ts":"2025-03-04T10:22:01.234Z","session_id":"cline","direction":"request","msg_id":"1","method":"tools/call","tool":"sql_query","args":{"sql":"SELECT TOP 10 * FROM Product"}}
{"ts":"2025-03-04T10:22:01.376Z","session_id":"cline","direction":"response","msg_id":"1","method":"tools/call","tool":"sql_query","duration_ms":142.3,"content_len":4821,"is_error":false,"error_msg":null}

Guard — rate limits + blocking

mcp-warden guard \
  --max-calls-per-minute 30 \
  --per-tool-limit sql_query:10 \
  --block-tool write_query \
  --max-response-kb 512 \
  --max-errors 5 \
  -- any-mcp-server

When a limit is hit, the agent gets a clear error:

Tool 'write_query' is in the blocked list.
Global rate limit reached (30 calls/min). Retry in ~42s.
Response too large (1024.0 KB > 512 KB limit). Consider narrowing your query.
Circuit breaker open — too many consecutive errors. Auto-reset in 58s.

Both — recommended

mcp-warden both \
  --audit-log ~/.mcp_warden/audit.jsonl \
  --max-calls-per-minute 60 \
  --per-tool-limit sql_query:20 \
  --block-tool write_query \
  --max-response-kb 1024 \
  --max-errors 3 \
  -- your-mcp-server [args...]

View the log

mcp-warden log --tail 20
mcp-warden log --tail 50 --filter-tool sql_query
mcp-warden log --errors-only
mcp-warden log --json | jq 'select(.is_error == true)'

Claude Desktop / Cline setup

Create a wrapper script:

#!/bin/bash
# ~/.config/mcp_warden/mydb.sh
exec mcp-warden both \
  --audit-log ~/.mcp_warden/mydb.jsonl \
  --max-calls-per-minute 60 \
  --max-response-kb 1024 \
  --max-errors 3 \
  -- /path/to/your-mcp-server.sh

Reference it in your MCP config:

{
  "mcpServers": {
    "mydb": { "command": "/Users/you/.config/mcp_warden/mydb.sh" }
  }
}

Guard options

Option Default Description
--max-calls-per-minute N 0 (off) Global cap across all tools per 60 s window
--per-tool-limit TOOL:N Per-tool cap e.g. sql_query:20 (repeatable)
--block-tool TOOL Block a tool entirely (repeatable)
--max-response-kb KB 0 (off) Reject responses larger than this
--max-errors N 0 (off) Trip circuit breaker after N consecutive errors
--circuit-cooldown SECS 60 Seconds before circuit auto-resets

Audit log schema

Field Direction Description
ts all ISO-8601 UTC timestamp
session_id all Set via --session-id
direction all request, response, or blocked
msg_id all JSON-RPC message id
method all e.g. tools/call, tools/list
tool all Tool name (tools/call only)
args request Tool arguments (truncated at --max-arg-bytes)
duration_ms response Round-trip milliseconds
content_len response Response size in bytes
is_error response True if upstream returned an error
error_msg response Error message text
reason blocked Why the request was blocked

Python API

from mcp_warden import MCPWarden, AuditLogger
from mcp_warden.guard import GuardConfig

proxy = MCPWarden(
    cmd=["your-mcp-server", "--arg", "value"],
    audit_logger=AuditLogger(path="./audit.jsonl", session_id="my-agent"),
    guard_config=GuardConfig(
        max_calls_per_minute=30,
        per_tool_limits={"sql_query": 10},
        blocked_tools=["write_query"],
        max_response_kb=512,
        max_errors_before_circuit_break=3,
    ),
)
proxy.run()


Roadmap

  • Async audit write queue (decouple disk I/O from relay path)
  • HTTP/SSE transport support
  • Prometheus metrics endpoint
  • Per-session budget and cost tracking
  • Webhook alerts on circuit break / blocked calls
  • Web dashboard for log exploration

Contributing

git clone https://github.com/raj8github/mcp-warden
cd mcp-warden
pip install -e ".[dev]"
pytest tests/ -v

See CONTRIBUTING.md.


License

MIT — see LICENSE

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_warden-0.1.3.tar.gz (22.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_warden-0.1.3-py3-none-any.whl (16.3 kB view details)

Uploaded Python 3

File details

Details for the file mcp_warden-0.1.3.tar.gz.

File metadata

  • Download URL: mcp_warden-0.1.3.tar.gz
  • Upload date:
  • Size: 22.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for mcp_warden-0.1.3.tar.gz
Algorithm Hash digest
SHA256 9c9f10a26e7849b27d3dd81a4ed29bfe897684df79ed69aaa5b8bd0f1d5d6c13
MD5 d6eba103497477ad16a43f14f18258d5
BLAKE2b-256 dac97047ba124f3a4ce231f9711b7811bfd2e93c5a51bea8fba44a12d19f4172

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_warden-0.1.3.tar.gz:

Publisher: release.yml on raj8github/mcp-warden

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mcp_warden-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: mcp_warden-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 16.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for mcp_warden-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 3171c4c0680588431aa6a98892088cd31f7bf3c155b5131b128100f08ff58b43
MD5 8eae2c6d418b1eba7ea6d602a321377c
BLAKE2b-256 dbd9d2be48ab16dc313a4d9d46fc72b8162645ba14040e8025feb4c802372f19

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_warden-0.1.3-py3-none-any.whl:

Publisher: release.yml on raj8github/mcp-warden

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page