Routing
mcpr parses every JSON-RPC 2.0 message and routes it based on the protocol, not the URL path.
How routing works
Section titled “How routing works”Each proxy instance fronts one upstream MCP app.
| Request type | Behavior |
|---|---|
JSON-RPC (tools/call, resources/read, etc.) | Classified, forwarded to the upstream MCP server, response is recorded |
| Everything else | Forwarded as-is — mcpr never blocks traffic |
mcp = "http://localhost:9000"To proxy multiple MCP servers, write one mcpr.toml per upstream and launch each with mcpr proxy run <path>.
MCP method support
Section titled “MCP method support”mcpr classifies 25+ MCP methods for routing, observability, and CSP rewriting:
| Category | Methods | Proxy Behavior |
|---|---|---|
| Lifecycle | initialize, notifications/initialized, ping | Extracts client info, tracks session state |
| Tools | tools/list, tools/call, notifications/tools/list_changed | CSP rewriting, per-tool metrics, schema capture |
| Resources | resources/list, resources/templates/list, resources/read, resources/subscribe, resources/unsubscribe | CSP rewriting, resource URI logging |
| Prompts | prompts/list, prompts/get | Prompt name extraction |
| Utility | logging/setLevel, completion/complete, notifications/cancelled, notifications/progress | Forwarded, classified for logging |
Unknown methods are forwarded as-is — mcpr never blocks traffic. See Supported MCP Methods for the full reference.
Per-tool care
Section titled “Per-tool care”Each tool gets its own metrics automatically: call count, error rate, p95/max latency, bytes in/out. No configuration needed — mcpr extracts the tool name from every tools/call request.
Inspect locally with sqlite3 ~/.mcpr/store.db, or stream to mcpr Cloud for per-tool tables, slow-call detection, and latency percentiles.
CSP & widgets
Section titled “CSP & widgets”MCP Apps (ChatGPT Apps, Claude connectors) render widgets in sandboxed iframes with strict CSP. ChatGPT reads openai/widgetCSP, Claude reads ui.csp, and each interprets domain lists differently. mcpr handles this automatically:
- Rewrites CSP domain arrays in
tools/list,tools/call,resources/list,resources/templates/list, andresources/readresponses - Replaces localhost/upstream URLs with the proxy domain
- Emits the CSP shape each platform expects
- Deep-scans nested structures for CSP arrays
CSP has three independent directives — connectDomains (fetch / WebSocket), resourceDomains (scripts, styles, images), and frameDomains (iframes) — each with its own mode: extend (merge with upstream) or replace (use only declared domains). Widget entries layer glob-matched overrides on top of the global policy.
[csp.connectDomains]domains = ["api.example.com"]mode = "extend"
[csp.resourceDomains]domains = ["cdn.example.com"]mode = "extend"
[csp.frameDomains]domains = []mode = "replace"
[[csp.widget]]match = "ui://widget/payment*"connectDomains = ["api.stripe.com"]connectDomainsMode = "extend"Schema capture
Section titled “Schema capture”mcpr passively captures your server’s tools, resources, and prompts by intercepting discovery responses. No extra requests, no added latency. Captured schemas land in the local SQLite store and stream to mcpr Cloud, where added/modified/removed entries and unused tools surface in the Tools tab.
See Events for the full event and capture system.
What’s coming
Section titled “What’s coming”- Multi-server routing — route to multiple upstream MCP backends
- Multiple proxies in one process — single binary serving several upstreams (
[[proxy]]config array)
