https://github.com/pragmalabs-tech/mcpr
Observability-first proxy for MCP servers
https://github.com/pragmalabs-tech/mcpr
mcp mcp-gateway mcp-proxy rust
Last synced: about 1 month ago
JSON representation
Observability-first proxy for MCP servers
- Host: GitHub
- URL: https://github.com/pragmalabs-tech/mcpr
- Owner: pragmalabs-tech
- License: apache-2.0
- Created: 2026-03-22T14:26:07.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-05-02T06:05:12.000Z (about 1 month ago)
- Last Synced: 2026-05-02T07:29:35.411Z (about 1 month ago)
- Topics: mcp, mcp-gateway, mcp-proxy, rust
- Language: Rust
- Homepage: https://mcpr.app
- Size: 28 MB
- Stars: 28
- Watchers: 0
- Forks: 1
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README

# mcpr
[](https://github.com/pragmalabs-tech/mcpr/actions/workflows/check.yml)
[](https://codecov.io/gh/pragmalabs-tech/mcpr)
[](LICENSE)
**Observability-first proxy for MCP servers.** A single Rust binary that sits in front of your MCP app and records every JSON-RPC call to a local SQLite store: per-tool latency, session traces, schema diffs, client breakdowns.
## Quickstart
### Install
```bash
curl -fsSL https://mcpr.app/install.sh | sh
```
Detects your OS and architecture, downloads the latest release from GitHub, installs to `$HOME/.local/bin/mcpr`, and adds that directory to your shell PATH. Override the destination with `MCPR_INSTALL_DIR=/usr/local/bin` if you prefer. Linux (x86_64, aarch64) and macOS (Intel, Apple Silicon) are supported. Manual tarballs are attached to every [GitHub release](https://github.com/pragmalabs-tech/mcpr/releases).
### Run
```bash
cat > mcpr.toml <<'EOF'
mcp = "http://localhost:9000"
port = 3000
EOF
mcpr proxy run mcpr.toml
```
Traffic flows through `http://localhost:3000`. `mcpr proxy run` is a foreground process: your supervisor (systemd, Docker, `child_process.spawn`) owns the lifecycle. SIGTERM drains gracefully.
To stream events into the cloud dashboard, run `mcpr proxy setup` once.
### Docker
```bash
cat > mcpr.toml <<'EOF'
mcp = "http://host.docker.internal:9000"
port = 3000
EOF
docker run -d --name mcpr \
-v "$(pwd)/mcpr.toml:/etc/mcpr/mcpr.toml:ro" \
-v mcpr-state:/var/lib/mcpr \
-p 3000:3000 \
ghcr.io/pragmalabs-tech/mcpr:latest
```
See [docs/DOCKER.md](docs/DOCKER.md) for volumes, environment variables, and compose / Kubernetes examples, and [`docker-compose.yml`](docker-compose.yml) for the sidecar pattern.
---
## What mcpr does
mcpr sits in front of your MCP app and does three things, in order of how much work each one saves you:
1. **Observe**: records every `tools/call`, `resources/*`, and `prompts/*` request to a local SQLite store. Per-tool p50/p95/max latency, error rates, session traces, client breakdowns, and schema diffs over time. No instrumentation in your app.
2. **Route**: one upstream per proxy today. JSON-RPC classification, and CSP rewriting that emits the shape each AI client (ChatGPT, Claude, Copilot) expects.
3. **Authenticate** *(in progress)*: OAuth 2.1 and API key handling at the proxy layer. Your app receives a verified `x-user-id` header instead of implementing auth flows itself.
Running in front of [mcp.usestudykit.com/mcp](https://mcp.usestudykit.com/mcp) today.
---
## Observe
Every JSON-RPC request that flows through mcpr lands in `~/.mcpr/store.db`: tool name, latency, status, error code, request/response size, session ID, server/client info captured on `initialize`, and full `tools/list` / `resources/list` / `prompts/list` responses for schema tracking.
### Local SQLite
The store at `~/.mcpr/store.db` is the source of truth. Inspect it directly with the `sqlite3` CLI for ad-hoc analysis, or use:
```bash
mcpr store stats # row counts, oldest/newest events, db size
mcpr store vacuum # delete old records and reclaim disk
```
Schema is documented in [docs/CLI.md](docs/CLI.md).
---
## Route
Each proxy instance fronts one upstream MCP app. mcpr classifies requests by JSON-RPC shape: MCP methods go to the backend; anything else is forwarded upstream as-is.
```toml
mcp = "http://localhost:9000"
```
To proxy multiple MCP servers, write one `mcpr.toml` per upstream and launch each with `mcpr proxy run --config `.
### Widget CSP
mcpr applies widget CSP in both shapes (the legacy OpenAI per-widget format and the current MCP standard), so a single config block works for both Claude and ChatGPT.
```toml
[csp]
# Public host the proxy is reachable on. Written into the OpenAI
# `widgetDomain` field. `_meta.ui.domain` is left to Claude, which
# derives that field from the proxy URL itself and rejects values
# supplied by anything in front of it.
domain = "widgets.example.com"
# Lands in `connect-src` (fetch / WebSocket / EventSource targets).
# `extend` merges with whatever the upstream MCP server declared;
# `replace` ignores upstream.
[csp.connectDomains]
domains = ["api.example.com"]
mode = "extend"
# Lands in `script-src`, `style-src`, `img-src`, `font-src`, `media-src`:
# one bucket for everything the widget loads. Same merge semantics.
[csp.resourceDomains]
domains = ["cdn.example.com"]
mode = "extend"
# Lands in `frame-src` (nested iframes). Defaults to `replace` so
# upstream cannot silently widen this directive.
[csp.frameDomains]
domains = []
mode = "replace"
# Per-widget override, matched by URI pattern (glob). Only the
# payment widget gets `connect-src` to Stripe.
[[csp.widget]]
match = "ui://widget/payment*"
connectDomains = ["api.stripe.com"]
connectDomainsMode = "extend"
```
---
## Authenticate
*In progress.* mcpr will handle MCP OAuth 2.1 and API key auth at the proxy layer, so your MCP app receives a verified `x-user-id` header instead of implementing auth flows itself. Planned config:
```toml
[auth]
mode = "oauth2.1" # or "api_key"
provider = "google" # google, github, bring-your-own
```
Track progress in the [Roadmap](#roadmap) below. Open an issue if your provider isn't covered.
---
## Reference
- Configuration: [docs/proxy/PROXY_CONFIGURATION.md](docs/proxy/PROXY_CONFIGURATION.md) (upstream URL, port, CSP, cloud sync, logging, limits)
- CLI: [docs/CLI.md](docs/CLI.md) (`proxy run/stop/list/delete/setup`, `store stats/vacuum`)
- Docker: [docs/DOCKER.md](docs/DOCKER.md) (volumes, health probes, compose/Kubernetes)
- Bench harness: [benches/README.md](benches/README.md) (initialize + tools/call latency, direct vs proxied)
---
## Roadmap
**Observability**
- [x] Per-tool metrics (calls, error%, p50, p95, max, request/response size)
- [x] Request logs, session tracking, AI client tracking
- [x] Schema capture with change tracking
- [x] Cloud dashboard sync ([cloud.mcpr.app](https://cloud.mcpr.app))
**Routing & Network**
- [x] JSON-RPC routing (single upstream per proxy)
- [x] CSP rewriting
- [ ] Multi-upstream routing from one port
**Auth**
- [ ] OAuth 2.1 for standard providers
- [ ] OAuth 2.1 for legacy (non-standard) auth
- [ ] API token auth
- [ ] Multiple auth modes per server
**Security**
- [ ] Per-tool access control
- [ ] Rate limiting and circuit breaker
- [ ] IP whitelist
## License
Apache 2.0