MCP Proxy
Wrap any MCP server with Fuze protection, budget, loop detection, audit logging, without touching the server's code.
Usage
The MCP server doesn't know Fuze exists. Fuze sits between the MCP client and the real server.
What gets intercepted
Every tools/call JSON-RPC request passes through Fuze:
- Budget check, is there budget remaining for this call?
- Loop detection, have we seen this tool+args combination before?
- Side-effect check, is this tool known to have side-effects?
- Audit logging, record the call in the trace
If all checks pass, the request is forwarded to the real MCP server unchanged.
CLI options
| Option | Description |
|---|---|
--max-cost <n> | Override max_cost_per_run from config |
--max-iterations <n> | Override max_iterations from config |
--trace | Write all call traces to ./fuze-proxy-traces.jsonl |
--verbose | Print intercepted calls and decisions to stderr |
--daemon | Connect to the Fuze daemon for cross-run enforcement |
Configuration
Configure the proxy in your fuze.toml. Per-tool settings use the [proxy.tools.TOOLNAME] table format:
Each [proxy.tools.<name>] section supports:
| Key | Type | Description |
|---|---|---|
estimated_cost | float | Cost attributed to each invocation of this tool |
side_effect | bool | Whether this tool mutates external state |
max_calls_per_run | int | Maximum times this tool can be called in one run |
timeout | string | Per-call timeout (e.g. "30s", "2m") |
Graceful shutdown
When the proxy receives a shutdown signal (SIGINT, SIGTERM, or the MCP client disconnects), it performs a clean teardown:
- Calls
await router.stop()to flush pending traces and finalize the run - Writes any remaining trace entries to
./fuze-proxy-traces.jsonl(if--traceis enabled) - Closes the connection to the daemon (if running in daemon mode)
- Exits the process
This ensures no trace data is lost, even if the client disconnects abruptly.
JSON-RPC request/response ID tracking
The proxy matches JSON-RPC responses to their originating requests by ID, not by payload shape. This is important because:
- MCP servers may return responses out of order
- Multiple
tools/callrequests can be in flight simultaneously - Matching by ID ensures the correct budget and trace entries are updated for each response
Each intercepted request's JSON-RPC id is stored in a pending map. When a response arrives, the proxy looks up the original request by id, attributes the cost, and records the result in the trace.
Trace output
When --trace is enabled, every tool call is logged to ./fuze-proxy-traces.jsonl as one JSON object per line. Traces are written only at result time (not at intercept time), so each line contains both the request and the response:
This avoids duplicate trace entries and ensures every logged call has a known outcome.
Transports
The proxy supports all MCP transport types:
- Stdio, pipes stdin/stdout between client and server
- SSE, proxies HTTP Server-Sent Events streams
- Streamable HTTP, proxies HTTP request/response
Combined with the daemon
With the daemon running, all MCP calls participate in cross-run pattern detection and org-wide budget enforcement.
Windows support
On Windows, the daemon socket uses a named pipe instead of a Unix domain socket:
The proxy detects the platform automatically -- no configuration needed. Path traversal checks also use path.relative() for correct behavior on Windows paths.