mcpmu (μ)
One secure, context-efficient MCP gateway for all your agents.
mcpmu lets you configure MCP servers once and expose them through a single
stdio or Streamable HTTP endpoint. Claude Code, Codex, Cursor, Windsurf, and
other MCP clients can share the same servers without duplicating configuration. Lets you compress large tool lists behind discovery calls to prevent excess context usage in your agent harness.
- **Control tools at the gateway** — Allowlist tools by namespace and globally
block dangerous operations, regardless of which agent connects.
- **Reduce tool-schema context costs** — Replace a large `tools/list` response
with three discovery and invocation tools, configurable per namespace.
- **Create focused namespaces** — Give work, personal, and project agents only
the servers and tools they need.
- **Connect any upstream** — Manage local stdio processes and remote Streamable
HTTP/SSE servers.
- **Serve any client** — Use local stdio shims or one persistent Streamable HTTP
endpoint with a URL for each namespace.
- **Manage everything visually** — Use the terminal UI or browser UI to add,
test, monitor, and configure servers.
- **Keep MCP metadata intact** — Tool annotations, output schemas, icons, and
structured results survive the proxy hop.
### Terminal UI
### Web UI
[Website](https://mcpmu.bigsy.uk) · Free and open source under the MIT license.
## Installation
### Homebrew (macOS/Linux)
```bash
brew tap Bigsy/tap
brew install mcpmu
```
### From source
```bash
go install github.com/Bigsy/mcpmu/cmd/mcpmu@latest
```
## Quick start
Add an MCP server:
```bash
# Local stdio server
mcpmu add context7 -- npx -y @upstash/context7-mcp
# Remote HTTP server with OAuth
mcpmu add atlassian https://mcp.atlassian.com/mcp --scopes read,write
```
Then register mcpmu with a local agent:
```bash
claude mcp add mcpmu -- mcpmu serve --stdio
# or
codex mcp add mcpmu -- mcpmu serve --stdio
```
The equivalent MCP configuration is:
```json
{
"mcpmu": {
"command": "mcpmu",
"args": ["serve", "--stdio"]
}
}
```
Run `mcpmu` for the terminal UI or `mcpmu web` for the browser UI.
Alternatively, let your coding agent perform the setup. Install the bundled
agent skill, then ask it to import your existing MCP configuration:
```bash
mcpmu skill install
```
## Give each agent the right tools
Namespaces create separate tool profiles from the same server configuration:
```bash
mcpmu namespace add work --description "Work tools"
mcpmu namespace assign work atlassian
mcpmu namespace assign work context7
claude mcp add work -- mcpmu serve --stdio --namespace work
```
Permissions are enforced inside mcpmu, not delegated to the connecting client.
You can deny dangerous tools everywhere, create an allowlist for a namespace,
or choose defaults for one particularly broad server:
```bash
# This cannot be overridden by a namespace
mcpmu server deny-tool filesystem delete_file
# Expose only selected tools in this namespace
mcpmu namespace set-deny-default work true
mcpmu permission set work context7 resolve allow
```
The precedence is: **server global deny > explicit tool rule > server default >
namespace default > allow**. See [Tool permissions](docs/permissions.md) for
recipes, the full resolution model, and the interaction with compression.
Permission edits submitted together in the TUI save as one batch. If a referenced
server or namespace was removed while the editor was open, the save fails and
reports the error without saving any of the batch.
## Reduce tool-schema context costs
Large MCP setups can send thousands of tokens of tool schemas when a session
starts. Compression replaces that surface with `list_tools`,
`get_tool_schema`, and `invoke_tool`, allowing an agent to fetch full schemas
only when it needs them:
```bash
# Recommended starting point for a large namespace
mcpmu namespace set-compression work medium
```
Compression is opt-in and works with stdio and HTTP. Denied tools stay out of
the compact listing, and calls through `invoke_tool` still use the real target
tool for mcpmu permissions and metrics.
See [Tool-surface compression](docs/compression.md) for level selection,
flag overrides, client-side permission caveats, and operational details.
## Choose a client transport
| Transport | Best fit | Namespace | Process model |
|---|---|---|---|
| stdio | Local agents and editor integrations | `--namespace work` | Per-client shim; shared daemon by default on Unix |
| Streamable HTTP | Persistent or networked endpoints | `/mcp/work` | One long-running HTTP process |
For HTTP clients, start the endpoint and point the client at the namespace URL:
```bash
mcpmu serve --http
```
```json
{
"mcpmu": {
"url": "http://127.0.0.1:8081/mcp/work"
}
}
```
`/mcp` selects the default namespace; `/mcp/{namespace}` selects a named one.
A non-loopback bind requires a bearer token:
```bash
mcpmu serve --http --addr 0.0.0.0:8081 --token "$MCPMU_TOKEN"
```
Servers are shared by default. For stateful servers such as browser automation
or REPLs, turn off **Share between agent connections** in the TUI or web server
form, add with `mcpmu add browser --shared=false -- browser-command`,
or set `"shared": false` in the config to give each client session a private instance.
Changing sharing takes effect when serving processes reload the config. The [CLI reference](docs/CLI.md) covers
daemon controls, HTTP security, OAuth, custom headers, and all serve flags.
## Runtime status and diagnostics
TUI and web statuses describe **this management session**. Their start/stop
controls use a separate supervisor from the daemon serving agent connections.
A stopped server in a manager may still be running for an agent.
```bash
mcpmu status # resolved config, daemon availability and running instances
mcpmu status --json
mcpmu doctor # config, executable, working-directory and env checks
mcpmu doctor --json
```
Both commands are read-only and support `--config`. They never start upstreams,
perform OAuth login, or start a daemon. Doctor checks the current CLI environment
plus server overrides; a running daemon may have inherited a different environment.
Missing configs use empty defaults. Status succeeds for a determined absent daemon;
doctor exits 1 for invalid config or failed local prerequisite checks. Environment
checks show variable names and outcomes, never values.
Serve reloads keep unaffected upstreams running. Permission/compression edits
retain instances; runtime edits restart affected servers. Invalid external edits
retain the previous valid config, and the web UI shows a warning until recovery.
Global settings such as metrics and OAuth still use a full reload.
## More features
- Search and install servers from the official MCP registry.
- Authenticate remote servers with OAuth 2.1, PKCE, dynamic client
registration, bearer tokens, or custom headers.
- Hot-reload configuration without restarting serve mode.
- Start servers lazily or pre-start them with `--eager`.
- Pass through upstream resources and prompts.
- Track per-tool call counts, error rates, latency, and unused tools. Click a
tool’s error count to inspect failed responses, including structured MCP
errors and transport diagnostics, filtered by namespace and time window.
Error responses are stored locally in `metrics.json` for 60 days, independently
of counter retention, and flushed every ~30 seconds while metrics are enabled.
Successful calls do not store inputs or responses. To retain inputs for failed
calls, enable **Record inputs for failed tool calls** in the server edit form
(or set `recordErrorInputs: true` on that server in the config). This defaults
to off per server. Inputs share the error history’s 60-day retention and redact
common secret fields recursively; free text and other data remain unchanged.
Error responses retain whatever diagnostic content the upstream returned. Older counts have
no response history. Expired responses are hidden on read and pruned while
serve mode is running.
- Share upstream processes across concurrent stdio clients while allowing
private instances per server.
- Negotiate MCP protocol revisions through 2025-11-25 and relay cancellation
and progress in both directions. Upstream pings are answered, and the
`instructions` of running upstreams are surfaced in the initialize result.
- Relay an upstream's elicitation requests (forms and sign-in URLs) to the
client that caused them, for servers that opt in (`--elicitation`). Routing
is certain for `"shared": false` servers and strong for HTTP upstreams that
send the request on the call's own response stream; an opt-in single-caller
heuristic covers the rest. Anything that cannot be routed is answered
`cancel`, and a tool's timeout pauses while it waits on the user.
Upstream JSON-RPC errors such as `URLElicitationRequiredError` pass through
intact. See [Client features](docs/CLI.md#client-features-elicitation).
- Relay sampling requests for servers that opt in (`--sampling`), only where
the request is certainly or strongly tied to one client. See
[Sampling](docs/CLI.md#sampling).
- Tell servers their roots (`--root`, `mcpmu server set-roots`): mcpmu answers
`roots/list` itself and announces edits with `roots/list_changed`. See
[Roots](docs/CLI.md#roots).
## Documentation
- [Tool permissions](docs/permissions.md) — access rules, precedence, and
practical allowlist and denylist patterns
- [Tool-surface compression](docs/compression.md) — reduce tool-schema context
while preserving gateway enforcement
- [CLI reference](docs/CLI.md) — commands, flags, configuration fields, HTTP,
and shared-daemon operation
- [Shell completions](docs/completions.md) — zsh, bash, fish, and PowerShell
- [Architecture](ARCHITECTURE.md) — implementation and protocol details
## Building and testing
```bash
git clone https://github.com/Bigsy/mcpmu.git
cd mcpmu
go build -o mcpmu ./cmd/mcpmu
./mcpmu
```
```bash
go test ./...
make check
make test-integration
```
Local linting and CI use [golangci-lint v2.12.2](https://github.com/golangci/golangci-lint/releases/tag/v2.12.2),
built with Go 1.26. Run `make check` and `make test-integration` sequentially:
a legacy OAuth callback fixture uses a fixed port. Self-contained reload and
diagnostic smoke coverage: `./scripts/smoke.sh smoke_selective_reload`.
Performance fixtures and measured baselines are in [docs/performance.md](docs/performance.md).