An open API service indexing awesome lists of open source software.

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

Awesome Lists containing this project

README

          

mcpr logo

# mcpr

[![CI](https://github.com/pragmalabs-tech/mcpr/actions/workflows/check.yml/badge.svg)](https://github.com/pragmalabs-tech/mcpr/actions/workflows/check.yml)
[![codecov](https://codecov.io/gh/pragmalabs-tech/mcpr/branch/main/graph/badge.svg)](https://codecov.io/gh/pragmalabs-tech/mcpr)
[![License](https://img.shields.io/badge/license-Apache--2.0-blue)](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