A self-hosted code execution MCP gateway that turns standard upstream MCP servers into sandboxed, dynamically discoverable execution backends β with governed routing and namespace-aware access control.
Some say MCP is dead, hopefully we can give it CPR.. π₯
| Demo |
| Category | Feature | Status |
|---|---|---|
| ποΈ Database | SQLite Support | β |
| PostgreSQL Support | β Planned | |
| π₯οΈ Interface & Admin | WebUI and Admin API | β |
| Active sessions management | β | |
| Bootstrap file support | β | |
| Config versioning & rollback | β | |
| Config export as JSON | β | |
| βοΈ Operating Modes | All Tools Loaded Mode | β Default |
| Two-tool Low Schema Mode | β Compat | |
| BM25 / lexical ranking | β Compat | |
| Sandbox Execution Tool discovery | β Code | |
| π‘ Transport | HTTP-Streamable Support | β |
| Stdio Support | β Planned | |
| Stdio Interactive Auth | β | |
| Protocol version negotiation | β | |
| π Downstream Servers | Tool editing | β |
| Tool token usage counter | β | |
| Namespaces for isolation | β | |
| Token ENV Support | β | |
| Encrypted Token SQL Storage | β | |
| OAuth Support | β | |
| Bearer Token Support | β | |
| Command allowlist (stdio) | β | |
| Health-aware tool ranking | β | |
| Tool quarantining | β | |
| Server import preview | β | |
| π‘οΈ Security | External idP + DCR OAuth | β |
| Bearer token per user/service | β | |
| SSRF protection | β | |
| Shell injection prevention | β | |
| OAuth URL allowlist (wildcards) | β | |
| Env var sanitization | β | |
| Admin session (HttpOnly cookie) | β | |
| HTTP security headers | β | |
| CORS restricted to loopback | β | |
| π― Focus Mode | Adaptive tool window | β |
| Tool capability inference | β | |
| Write/admin/unhealthy penalties | β | |
| Successful pattern tracking | β | |
| β‘ Resilience | Per-session rate limiting | β |
| Per-user rate limiting | β | |
| Per-downstream concurrency | β | |
| Circuit breaker | β | |
| Connect/response/total timeouts | β | |
| π Triggers | refreshOnSuccess | β |
| refreshOnTimeout | β | |
| refreshOnError | β | |
| FirstSuccessInDomain | β | |
| ErrorThreshold | β | |
| IdleTimeout | β | |
| replaceOrAppend mode | β | |
| cooldownSeconds | β | |
| π¦ Starter Packs | preferredTags filtering | β |
| Risk level filtering | β | |
| Mode filtering | β | |
| maxTools cap | β | |
| πΎ Code Runtime | Artifact store (save/list) | β |
| Result APIs (pick/limit/grep) | β | |
| Handle registry (TTL-based) | β | |
| Memory/execution limits | β | |
| π RBAC | Roles (allow/deny namespaces) | β |
| Permission Management | β | |
| Bearer Token Management | β | |
| Allowed OAuth providers | β | |
| π·οΈ Tool Trust | Risk levels (Low/Med/High) | β |
| Source trust (Untrusted/Verified) | β | |
| Schema compression | β | |
| π Observability | Auto refresh tools | β |
| Audit & Observability | β | |
| Pino structured logging | β | |
| Audit log pruning | β | |
| Debug endpoints (loopback) | β | |
| π Client Support | Claude Code | β |
| OpenAI Codex | β | |
| OpenCode | β | |
| Claude Web Client | β | |
| ChatGPT Web Client | β |
| Mode | Tool Window | Best For |
|---|---|---|
| Code | 2 tools only | Auto orchestration in a JS sandbox |
| Compat | 4 meta-tools | Large tool sets, minimal context usage |
| Default | All enabled downstream tools, filtered by namespace | Full transparency, small tool sets |
Go to Benchmarking for current token-usage comparison details.
Modes are configured per namespace and can be mixed across different access paths. For instance, you can create a mcp/dev with complex tools to be used in code mode or /mcp/personal with a small set of tools to be used in default mode for example.
flowchart LR
subgraph clients["MCP Clients"]
claude["Claude / Claude Code"]
codex["OpenAI Codex"]
inspector["MCP Inspector"]
end
subgraph gateway["MCPR Gateway (Fastify + TypeScript)"]
direction TB
auth["π Auth & RBAC\nBearer token β role β namespace"]
modes["βοΈ Operating Modes\nCode Β· Compat Β· Default"]
registry["π‘ Server Registry\n& Health Monitor"]
sessions["πΎ Session Store"]
end
subgraph downstream["Downstream MCP Servers"]
s1["Server A\n(stdio)"]
s2["Server B\n(HTTP)"]
s3["Server C\n(SSE)"]
end
adminui["π₯οΈ Admin WebUI\n/ui/"]
subgraph webui["Admin Panels"]
direction TB
wp1["π Dashboard Β· π Servers Β· π οΈ Tools"]
wp2["π¬ Sessions Β· π Access Control"]
wp3["π Audit Β· βοΈ Config & History Β· π Namespaces"]
end
sqlite[("ποΈ SQLite\nSessions Β· Audit Β· Config")]
clients -->|"Bearer token\nPOST /mcp/:namespace"| auth
auth --> modes
modes --> registry
registry --> s1 & s2 & s3
sessions <--> sqlite
gateway --- sessions
adminui -->|"admin_session cookie\n/admin/*"| gateway
adminui --- webui
node --version # must be 24.x LTS
git clone https://github.com/TempoNaoTenho/mcpr-gateway.git && cd mcpr-gateway
cp .env.example .env
npm ci
npm run build
npm start # built UI + MCP gateway on PORTBefore running npm start, replace change-me-* in .env with your own secure values.
Use Node 24 LTS. Run: npm ci, npm run build, then npm start (serves UI + gateway on same port). The build step auto-rebuilds isolated-vm/better-sqlite3 if needed.
.env is optional; environment variables take priority. The app exits if required security settings are missing or default. Native module fixes and test preflights are automatic for all scripts. Use npm run setup if you want help editing .env or generating bootstrap.json.
App runs at http://127.0.0.1:3000 (UI at /ui/). For development, use npm run dev (UI on PORT, API on PORT+1).
| Variable | Purpose | Required |
|---|---|---|
ADMIN_TOKEN |
Enables authentication on all /admin/* routes |
Yes |
GATEWAY_ADMIN_USER |
Username typed at the admin login | Yes for production |
GATEWAY_ADMIN_PASSWORD |
Password typed at the admin login | Yes |
DOWNSTREAM_AUTH_ENCRYPTION_KEY |
AES-256 key for downstream credentials at rest | Required for managed downstream secrets |
Without ADMIN_TOKEN, the admin panel is unprotected β anyone with network access can reach it. The default npm start path now fails fast instead of silently accepting missing or placeholder values, whether they come from .env or platform-injected environment variables.
# Export variables in your shell (CI/CD), or pass an env file explicitly:
docker compose --env-file .env -f docker/docker-compose.yml up --buildThe compose file reads ADMIN_TOKEN, GATEWAY_ADMIN_PASSWORD, and DOWNSTREAM_AUTH_ENCRYPTION_KEY for interpolation from the shell/CI environment or an explicit env file such as --env-file .env. Runtime HOST inside the container is always 0.0.0.0 in this file β your dev .env value HOST=127.0.0.1 does not apply there. If ADMIN_TOKEN or GATEWAY_ADMIN_PASSWORD are missing, docker compose up fails immediately with a clear error before the container starts. If DOWNSTREAM_AUTH_ENCRYPTION_KEY is malformed, the container exits on startup.
If the UI or /health fails from the browser, try http://127.0.0.1:3000 instead of http://localhost:3000 (some systems resolve localhost to IPv6 first).
Issue a client Bearer token from the Access Control panel at /ui/access (or add it to auth.staticKeys in bootstrap.json), then configure your client.
π‘ Without
bootstrap.json, the built-in namespace isdefault. Replace it only when you configure custom namespaces.
{
"mcpServers": {
"mcpr-gateway": {
"type": "http",
"url": "http://localhost:3000/mcp/<namespace_name>",
"headers": { "Authorization": "Bearer <your-token>" }
}
}
}[mcp_servers.mcpr-gateway]
type = "http"
url = "http://localhost:3000/mcp/<namespace_name>"
bearer_token_env_var = "MCPR_GATEWAY_TOKEN"export MCPR_GATEWAY_TOKEN=<your-token># Add via CLI or config file
opencode mcp add mcpr-gateway \
--url "http://localhost:3000/mcp/<namespace_name>" \
--token "<your-token>"- Go to Settings β Integrations β MCP Servers
- Click Add Integration
- Fill the form:
- Name:
MCPR Gateway - URL:
http://localhost:3000/mcp/<namespace_name>
- Name:
- Save and enable the integration
- Go to Settings β Plugins β MCP Servers (or search "MCP" in plugin store)
- Add a new MCP server
- Configure:
- Server URL:
http://localhost:3000/mcp/<namespace_name>
- Server URL:
- Save and activate
Both supports OAuth.
Send Authorization: Bearer <token> on every request. After initialize, include the Mcp-Session-Id header returned by the gateway.
| Concern | Implementation |
|---|---|
| Client auth | Bearer token per user/service, issued via Admin UI or auth.staticKeys in bootstrap |
| Admin protection | ADMIN_TOKEN enables login; GATEWAY_ADMIN_USER / GATEWAY_ADMIN_PASSWORD are the credentials; in NODE_ENV=production with no ADMIN_TOKEN, admin routes are not mounted |
| Downstream credentials | AES-encrypted in SQLite when DOWNSTREAM_AUTH_ENCRYPTION_KEY is set |
| HTTP security headers | @fastify/helmet applied to all responses |
| CORS | Restricted to loopback origins (localhost, 127.0.0.1, ::1) for MCP endpoints |
| Topic | Detail |
|---|---|
| Persistence | SQLite (default) or in-memory (SESSION_BACKEND=memory) |
| TTL | 30 min default (session.ttlSeconds = 1800), automatic cleanup |
| Transport | HTTP-Streamable: GET /mcp/:namespace (SSE) + POST /mcp/:namespace (JSON-RPC) |
| Session header | Mcp-Session-Id required on all requests after initialize |
| Admin ops | Query, inspect, and revoke sessions via /ui/sessions or GET /admin/sessions |
| Topic | Detail |
|---|---|
| Transports | stdio and http / streamable-HTTP |
| Auth options | none, bearer (env var or inline), oauth |
| Credentials | Encrypted at rest; UI-managed via /ui/servers |
| Health monitoring | Continuous checks; degraded servers penalized in tool selection ranking |
| Namespacing | Servers assigned per namespace; tool pool isolated per access path |
| Concept | Description |
|---|---|
| Namespace | Isolated access path β e.g. /mcp/dev, /mcp/prod, /mcp/personal |
| Role | Maps a bearer token to one or more namespaces with allowed operating modes |
| Token | Per-client Bearer token, issued via Admin UI and stored in SQLite |
| Auth mode | static_key β token resolved to role; role checked against namespace policy |
Served at /ui/ β SvelteKit 2 + TailwindCSS v4. Requires admin login when ADMIN_TOKEN is set.
| Panel | Path | What you can do |
|---|---|---|
| Dashboard | /ui/ |
Session counts, server health overview |
| Servers | /ui/servers |
Add, edit, delete downstream servers; view health status |
| Sessions | /ui/sessions |
Inspect active sessions; revoke individual sessions |
| Access Control | /ui/access |
Issue and revoke client bearer tokens |
| Audit | /ui/audit |
Browse events filtered by user, tool, event type, date range |
| Config | /ui/config |
Edit runtime config; view full version history; one-click rollback |
| Namespaces | /ui/namespaces |
Token estimates, catalog sizes, mode metrics per namespace |
| Tools | /ui/tools |
Browse full downstream tool catalog |
| Topic | Detail |
|---|---|
| Logging | Pino structured logs to stdout; level set via LOG_LEVEL env var |
| Audit trail | SQLite-persisted per-event records; prunable by retention window (AUDIT_RETENTION_DAYS) |
| Audit events | SessionCreated, ToolExecuted, ExecutionDenied, DownstreamMarkedUnhealthy |
| Query API | GET /admin/audit β filters: session_id, user_id, event_type, tool_name, from, to |
| Feature | Config key | Default |
|---|---|---|
| Rate limiting | resilience.rateLimit.* |
Per-session and per-user windows |
| Downstream concurrency | resilience.concurrency.* |
Per-server cap |
| Response timeout | resilience.timeoutMs |
Configurable |
| Health-aware ranking | Automatic | Unhealthy servers penalized in tool selection |
- Current benchmark suite is a work in progress and is not yet ready for production use. Ideally we should compare token usage for discovery tools and complete tool execution cases.
Scenario: native benchmark
export BENCH_AUTH_HEADER="Bearer key"
npm run benchmark -- real --namespaces name_space1, name_space_2, ...~10 common dev mcp servers with ~30 tools total (context7, tavily, etc)
| Mode | Retrieval recall@3 | MRR | E2E Success | Avg Tokens Loaded in Context | Reduction % |
|---|---|---|---|---|---|
| code | 1 | 1 | 1 | 703.2 | ~92% |
| default | 1 | 1 | 1 | 9138.2 | 0% |
Scenario: real-usage on MCP Client
| Mode | Tool invocations | Ξ£ totalTokensEstimate | Ξ£ HTTP responseTime (ms) |
|---|---|---|---|
| code | 3 | 2309 | 11051.31 |
| compat | 7 | 4470 | 6707.00 |
| default | 4 | 3338 | 9369.15 |
- Create a realistic benchmark suite to compare different modes and downstream servers
- Implement Gateway stdio transport
- Implement PostgreSQL support
| Guide | Audience | Contents |
|---|---|---|
| Getting Started | Operators, integrators | Dependencies, setup, MCP client flow, auth basics |
| Configuration | Operators | bootstrap.json, selector publication, CONFIG_PATH, two-tier config model |
| Architecture | Contributors | Sessions, registry, selector, triggers, high-level flow |
| HTTP API | Integrators | Health, MCP JSON-RPC, admin, debug, static UI β full endpoint reference |
| Deployment | Operators | Docker Compose, persistence, production hardening, TLS |
| Development | Contributors | npm scripts, project layout, tests, Web UI workflow |
| Changelog | Operators, adopters | Release notes, runtime requirements, known caveats |
Config schema source of truth:
src/config/schemas.ts. For bootstrap examples and copy-paste snippets, seeconfig/README.md.
MIT β see LICENSE.
