{
  "markdown": "# mcp-broker\n<!-- mcp-name: io.github.doricstack/mcp-broker -->\n\n![mcp-broker routing backbone brand header](brand/assets/readme-header.svg)\n\n`mcp-broker` is a local Model Context Protocol process broker for MCP clients.\n\nThink PgBouncer for MCP: one stable local endpoint in front of many upstream MCP servers. The broker owns upstream startup, reuse, cleanup, profile exposure, status, and safe tool routing.\n\nThe core idea is simple: do not make every agent session load every upstream tool definition before the user asks a task.\n\n## Why this exists\n\nAI coding sessions with many MCP servers tend to accumulate the same problems:\n\n- every client config repeats the same MCP server list\n- every new session can start duplicate upstream processes\n- OAuth, browser state, local files, and database handles spread across tools\n- raw tool lists consume context before the task begins\n- hosted connector caches can duplicate local MCP tools\n- orphaned MCP processes survive after client sessions exit\n\n`mcp-broker` puts a small broker facade in front of those upstreams. It is not a hosted workflow builder; it is local infrastructure for keeping MCP clients small, predictable, and under one config contract.\n\n```text\nClient profile\n        |\n        | one local MCP entry\n        v\n  mcp-broker-client\n        |\n        | Unix socket\n        v\n  mcp-broker-daemon\n        |\n        | profile gates, namespace routing, status, cleanup\n        v\n  upstream MCP servers\n```\n\nThe client sees a small set of broker tools:\n\n```text\nbroker_search_tools\nbroker_describe_tool\nbroker_call_tool\nbroker_status\nbroker_close_session\n```\n\nThe upstream MCPs still exist. They are discovered and called through the broker when a task needs them.\n\n## Measured context reduction\n\nOn 2026-05-24, the measured Codex setup went from many raw MCP and hosted app tool definitions to one broker facade plus a pruned `codex_apps` cache.\n\n| Surface | Before | After | Reduction |\n|---|---:|---:|---:|\n| Direct Codex MCP server entries | 11 | 1 | 90.91% |\n| MCP tool definitions | 414 | 4 | 99.03% |\n| Hosted `codex_apps` tool definitions | 195 | 39 | 80.00% |\n| Combined always-loaded tool definitions | 609 | 43 | 92.94% |\n| Combined serialized tool payload bytes | 1,026,171 | 185,877 | 81.89% |\n| Combined `o200k_base` tool tokens | 276,989 | 45,281 | 83.65% |\n\nThe 92.94% number is a tool-definition count reduction. The 83.65% number is a token reduction for canonical serialized tool payloads measured with `tiktoken` `o200k_base`.\n\nSee [docs/context-reduction-measurement.md](docs/context-reduction-measurement.md) for evidence and caveats.\n\n## What it does\n\n- Runs one local broker daemon over a Unix socket.\n- Exposes one lightweight stdio client shim to MCP clients.\n- Starts upstream MCP servers on demand.\n- Reuses shared upstreams across sessions when configured.\n- Isolates per-session upstreams when state must not be shared.\n- Maps upstream tools into stable namespaces.\n- Exposes compact search, describe, call, status, and caller-bound session-close tools.\n- Enforces profile-level tool budgets and exposure gates.\n- Blocks mutating upstream exposure unless a profile allowlist grants it.\n- Stores runtime state under `$HOME/mcp/mcp-broker`, outside the repo.\n- Renders MCP client config entries with dry-run, backup, and rollback.\n- Provides LaunchAgent install and uninstall flows for macOS.\n- Provides Linux systemd user-service render, install, unload, and removal flows.\n- Provides Windows PowerShell Scheduled Task render, install, and removal flows.\n- Includes unit, journey, live, and e2e tests through Makefile targets.\n\nCore differentiators:\n\n- Profile-scoped exposure: each MCP client gets a configured view of upstreams instead of every tool by default.\n- Mutating-tool gates: mutating upstreams stay hidden until a profile allowlist grants access.\n- Lifecycle ownership: shared and per-session upstreams are started, watched, stopped, and reaped by the broker.\n- Client parity checks: rendered profiles can be validated through the same compact broker facade before config is applied.\n\n## Who this is for\n\nUse `mcp-broker` if you:\n\n- use Codex, Claude Code, AGY CLI, or other MCP clients\n- have more MCP tools than you want in every session\n- need shared local MCP servers without duplicate process startup\n- want one place for OAuth state, browser state, sockets, logs, and cleanup\n- need per-client profiles instead of the same tool list everywhere\n- want a small broker facade instead of raw upstream tool dumps\n\nThis repo is not an enterprise MCP control plane. It is local desktop infrastructure for developer-agent workflows.\n\n## Current status\n\nImplemented:\n\n- YAML config loading from `config/broker.private.yaml`, created from `config/broker.example.yaml`.\n- Strict YAML contract validation for runtime, clients, profiles, upstreams, and policy blocks.\n- Public JSON Schema validation through `config/broker.schema.json`.\n- Runtime path derivation from `runtime.root`.\n- Tool namespace mapping from configured upstream prefixes.\n- Local upstream subprocess lifecycle management and process-group cleanup.\n- Broker daemon over Unix socket.\n- MCP client shim and renderers for configured MCP client profiles, including Codex, Claude, and AGY.\n- AGY profile rendering to `.gemini/config/mcp_config.json`, including its MCP\n  allowed-server policy.\n- Dry-run client config rendering, apply-time backups, and rollback.\n- LaunchAgent render and install scripts with dry-run defaults.\n- Compact broker facade for search, describe, call, status, and caller-bound session close.\n- Profile validation from YAML smoke probes.\n- Discovery parity checks between compact client profiles.\n- Public and maintainer quality gates through Makefile targets.\n- Shared-runtime guardrail contracts through P3.8 E2E proof while hosted\n  execution remains disabled by default.\n\nWiring status:\n\n- Codex is wired through the broker.\n- Claude is wired through the broker after profile validation and manual `/mcp` acceptance.\n- AGY is wired through the broker by rendering `.gemini/config/mcp_config.json`.\n\nPublic release status:\n\n- The repo is designed to stay public-safe.\n- Private upstream inventory, account paths, OAuth state, secrets, sockets, logs, and generated client configs stay outside git.\n- Stable release metadata is validated by `make release-version-check`; publication proof is tracked in `docs/distribution.md`.\n- Docker image support is available for container-friendly configs. Docker MCP Catalog submission still requires Docker review.\n- MCPB metadata is present at `mcpb/manifest.json` for local directory review.\n\nSee [ROADMAP.md](ROADMAP.md) for public-facing release work.\n\n## Architecture\n\n`mcp-broker` has three runtime layers:\n\n| Layer | Responsibility |\n|---|---|\n| Client shim | Presents one stdio MCP server entry to each MCP client and forwards JSON-RPC over the broker socket. |\n| Broker daemon | Owns profile gates, namespace routing, upstream lifecycle, status, logging, and cleanup. |\n| Upstream MCP servers | Run as configured `stdio`, HTTP, streamable HTTP, or SSE connectors with shared or per-session process policy. |\n\nThe config file is the contract. Profiles decide exposure, upstreams define transport and lifecycle behavior, and smoke probes define safe read calls for validation.\n\n## Comparison\n\n| Approach | Best fit | Tradeoff |\n|---|---|---|\n| Raw MCP client config | Small setups with a few tools. | Every session loads the full tool list and each client repeats config. |\n| Simple MCP proxy | Forwarding one server to one client. | Does not own upstream lifecycle, profile budgets, or cross-client cleanup. |\n| Hosted app connectors | SaaS tools managed by the client provider. | Local MCP state and cross-client parity remain outside user control. |\n| `mcp-broker` | Local developers with many upstream MCPs across MCP clients. | Adds a local daemon and config contract that must be installed and monitored. |\n\n## Screenshots Or GIF\n\nThe quickstart flow should look like this:\n\n![mcp-broker quickstart terminal](docs/assets/quickstart-terminal.svg)\n\n```text\nmake config-init\nmake config-validate\nmake broker-status\nmake codex-facade-smoke\n```\n\nIn an MCP client, `/mcp` should show one `mcp-broker` entry. Use `broker_status`\nto inspect profile-visible upstream state.\n\n## Quickstart\n\nPrerequisites:\n\n- macOS with `launchctl` for LaunchAgent use.\n- Python 3.10 or newer available as `python3`.\n- `make`.\n- Node.js and `npx` for npm-based upstream MCP servers.\n- A clone of this repo.\n\nPackage installs:\n\n```bash\npipx install mcp-broker\nuv tool install mcp-broker\nbrew tap ${HOMEBREW_TAP_REF}\nbrew install mcp-broker\n```\n\nHomebrew installs the same console scripts as the Python package. Package\ninstalls do not write MCP client config; client wiring stays an explicit\nMakefile action.\n\nDocker is for container-friendly configs:\n\n```bash\ndocker build -t mcp-broker:local .\ndocker run --rm -i mcp-broker:local\n```\n\nLocal stdio clients and MCPB-style installs use the package-owned lifecycle:\n\n```bash\nmcp-broker stdio --init-if-missing\n```\n\nCreate the local venv, install dependencies, and verify runtime layout:\n\n```bash\nmake setup\n```\n\nCreate private config from the public template:\n\n```bash\nmake config-init\n```\n\n`config-init` creates the destination directory when needed and copies the public\ntemplate as the starting point. It does not import local MCP inventory, user\npaths, or secrets.\n\nEdit `config/broker.private.yaml` for local upstreams. Keep secret values out of config. Use environment variable names or files under:\n\n```text\n$HOME/mcp/mcp-broker/secrets/\n```\n\nRun the quality gate:\n\n```bash\nmake quality-gate\n```\n\nValidate the configured YAML contract:\n\n```bash\nmake config-validate\n```\n\nStart the broker:\n\n```bash\nmake broker-start\n```\n\nCheck status:\n\n```bash\nmake broker-status\n```\n\nFor the full install flow, see [docs/install.md](docs/install.md).\nFor a clone-to-running adoption flow, see\n[docs/adoption-guide.md#clone-to-running-path](docs/adoption-guide.md#clone-to-running-path).\nFor shared-runtime boundaries, see\n[docs/shared-runtime-guardrails.md](docs/shared-runtime-guardrails.md).\nP3.8 E2E proof covers tenant isolation, authz denial, quota denial, session\naffinity, audit events, rollback, degraded mode, local-only routing, and\nshared-eligible routing. Hosted execution remains unsupported in the public\nlocal broker.\n\n## Runtime layout\n\nDefault runtime root:\n\n```text\n$HOME/mcp/mcp-broker/\n|- backups/\n|- logs/\n|- renders/\n|- run/\n|- secrets/\n|- sockets/\n`- state/\n   `- upstreams/\n```\n\nRuntime files are not repo files. Upstream OAuth state, browser state, secret files, sockets, logs, rendered client configs, backups, and daemon state belong under the runtime root.\n\nSee [docs/runtime-layout.md](docs/runtime-layout.md).\n\n## Client wiring\n\nBack up a client config:\n\n```bash\nmake config-backup CLIENT=codex\n```\n\nDry-run render:\n\n```bash\nmake config-render CLIENT=codex CONFIG_RENDER_APPLY=0\n```\n\nApply after reviewing the rendered file under `$HOME/mcp/mcp-broker/renders/`:\n\n```bash\nmake config-render CLIENT=codex CONFIG_RENDER_APPLY=1\n```\n\nRollback:\n\n```bash\nmake config-rollback CLIENT=codex\n```\n\nUse `CLIENT=claude` or `CLIENT=agy` after that profile smoke passes and that\nclient is intended to use the broker. For new JSON-based MCP clients, generate a\nstarter block:\n\n```bash\nmake profile-snippet NEW_PROFILE=local-client NEW_CLIENT_FORMAT=mcp-settings-json\n```\n\nSee [docs/add-profile.md](docs/add-profile.md) for the full new-profile flow.\n\n## Compact broker facade\n\nThe compact facade keeps chat-facing profiles small:\n\n| Tool | Purpose |\n|---|---|\n| `broker_search_tools` | Search configured upstream tools by query. Results carry name, description, upstream, purpose, tags, and mutating flag; the heavy `inputSchema` is omitted and fetched on demand from `broker_describe_tool`. |\n| `broker_describe_tool` | Return schema and metadata for one upstream tool. |\n| `broker_call_tool` | Call one upstream tool through broker routing. Accepts an optional `projection` (`{\"paths\": [...], \"max_array_items\": N}`) that trims the response server-side before it reaches the client. |\n| `broker_status` | Show profile-visible upstream state, passive auth probes, and last errors without starting tools. |\n| `broker_close_session` | Release caller-owned per-session upstream processes without stopping shared upstreams or another client session. |\n\nCodex `/mcp` shows the single `mcp-broker` entry by design. Per-upstream visibility, status, and socket path come from `broker_status`.\n\n## Profiles and safety\n\nProfiles decide which upstreams a client can see and call.\n\nSupported concepts:\n\n- `max_tools` protects clients from huge tool lists.\n- `compact_tools_enabled` exposes broker facade tools instead of raw upstream tools.\n- `broker_tool_name_style` adapts broker facade names for clients that cannot surface dotted tool names.\n- `mcp_allowed_servers` renders client settings for MCP clients that require an explicit server allowlist.\n- `allow_mutating_upstreams` is required before a mutating upstream can be exposed.\n- `shared` mode reuses one upstream process where shared account state is acceptable.\n- `per_session` mode isolates upstream state per client session.\n- `per_call` mode starts a fresh stdio process for each tool call or tool-list operation, then stops that process before returning.\n- `disabled` mode keeps compatibility records without exposing the upstream.\n\nStatus reports `active_call_count` for in-flight `per_call` operations, including\ntool discovery. It returns to zero after success, failure, or timeout cleanup.\nThis count is separate from `session_count` and is not a count of saved threads.\nReading status does not start an upstream; profile-hidden upstreams stay hidden.\n\nProtected surfaces such as OAuth, browser state, filesystem roots, and databases require explicit config and validation. Public examples stay disabled or placeholder-based.\n\nSee [docs/security-review.md](docs/security-review.md) and [docs/upstream-compatibility-matrix.md](docs/upstream-compatibility-matrix.md).\nFor a deeper safety checklist, see [docs/safety.md](docs/safety.md).\n\n## Config contract\n\nThe public template is [config/broker.example.yaml](config/broker.example.yaml).\nThe matching JSON Schema is [config/broker.schema.json](config/broker.schema.json).\n\nSupported top-level sections:\n\n```yaml\nschema_version: 1\nruntime: {}\nbroker: {}\nprofiles: {}\nclients: {}\nupstreams: {}\n```\n\nThe loader rejects unknown keys. Runtime placeholders such as `{runtime.root}`, `{runtime.state_dir}`, and `{runtime.secrets_dir}` can be used in upstream command, args, working directory, and env file paths.\n\n`make config-validate` checks the selected `CONFIG_PATH` against the public JSON Schema first, then runs the runtime loader so semantic rules are enforced from the same code path the broker uses.\n\nEach enabled upstream exposed to a profile should define a safe smoke probe:\n\n```yaml\nsmoke:\n  query: read example graph\n  tool: example-store.read_graph\n  arguments: {}\n  call: true\n```\n\n`make profile-validation PROFILE=<profile>` validates every enabled upstream visible to that profile through `broker_status`, `broker_search_tools`, `broker_describe_tool`, and the configured safe `broker_call_tool`.\n\n## Codex operator acceptance\n\nRepo-owned tests validate broker behavior through the local client shim. The last\nCodex-specific check has to run inside an active Codex session because that is\nwhere the deferred MCP wrapper tools exist.\n\nGenerate the current acceptance steps from YAML:\n\n```bash\nmake codex-deferred-acceptance\n```\n\nThe target reads the configured `smoke` probes and prints the exact\n`mcp__mcp_broker__` wrapper calls for search, describe, and safe call. It does\nnot invoke Codex, does not call an external LLM session, and is not part of\n`make quality-gate`.\n\nSee [docs/codex-deferred-tool-acceptance.md](docs/codex-deferred-tool-acceptance.md).\n\n## LaunchAgent\n\nRender without writing:\n\n```bash\nmake launchagent-install\n```\n\nApply and load:\n\n```bash\nmake launchagent-install LAUNCHAGENT_APPLY=1\nmake launchagent-load\nmake broker-status\n```\n\nUnload or remove:\n\n```bash\nmake launchagent-unload\nmake launchagent-uninstall LAUNCHAGENT_APPLY=1\n```\n\n## systemd\n\nLinux user-service install uses the same runtime root and config path contract:\n\n```bash\nmake systemd-install\nmake systemd-install SYSTEMD_APPLY=1\nmake systemd-load\n```\n\nFor package installs, set `MCP_BROKER_DAEMON_COMMAND` to the installed daemon\npath before applying the service.\n\n## Windows\n\nWindows startup uses PowerShell Scheduled Task commands with the same runtime\nroot and config path contract:\n\n```bash\nmake windows-install\nmake windows-install WINDOWS_APPLY=1\nmake windows-load\n```\n\nRemove it with:\n\n```bash\nmake windows-unload\nmake windows-uninstall WINDOWS_APPLY=1\n```\n\n## Test and release gates\n\nRun all test tiers:\n\n```bash\nmake test\n```\n\nRun the public quality gate:\n\n```bash\nmake quality-gate\n```\n\nThe coverage gate uses line and branch coverage for Python source.\n\nRun the release gate when preparing a tag:\n\n```bash\nmake release-gate\n```\n\n`release-gate` runs package, smoke, and mutation checks. Mutation receives a\nrelease-scoped child count derived from `LOCAL_CPU_BUDGET` and\n`RELEASE_GATE_JOBS`, so it does not take the full CPU budget while other\nrelease children run. Mutation runs public unit and journey tests last and writes\n`var/quality/mutation_stats.json` with total counts, score, and ranked\n`blocked_by_file` entries. On macOS, the release gate runs mutation inside a\nLinux container to avoid local mutmut fork failures. E2E tests remain in `make\nquality-gate`.\n\nRun smoke and runtime cleanup checks:\n\n```bash\nmake config-validate\nmake broker-smoke\nmake broker-stop\nmake broker-reap\nmake doctor\nmake release-smoke\n```\n\nRelease or client config apply should wait for:\n\n- `make quality-gate`\n- `make config-validate`\n- `make broker-smoke`\n- dry-run config render for each intended client\n- rollback test\n- `make release-smoke`\n- `make release-gate` before tagging\n- `make doctor` with no stale broker-owned resources\n\nSee [docs/release-checklist.md](docs/release-checklist.md).\n\n## Public commands\n\nThese targets use this repo plus declared Python and Node prerequisites:\n\nFor source contributions, install `gitleaks` on PATH and run `make hooks-install`\nin each checkout. The tracked pre-commit hook requires a redacted staged secret\nscan, then runs commit-tier affected tests through Make. Missing scanner tools,\nempty staged scope, and scan or test failures block the commit. `GITLEAKS` and\n`PYTHON` select the scanner and maintainer interpreter. Installation uses Git's\nworktree-specific config and refuses unknown existing hooks or common\n`core.worktree`/bare settings that need migration. No hosted workflow is started.\n\n```bash\nmake setup\nmake config-init\nmake test\nmake test-unit\nmake test-journey\nmake test-live\nmake test-e2e\nmake test-cov\nmake precommit\nmake quality-gate\nmake release-gate\nmake config-validate\nmake broker-smoke\nmake broker-start\nmake broker-status\nmake broker-stop\nmake broker-reap\nmake doctor\nmake config-backup\nmake codex-app-policy\nmake config-render\nmake config-rollback\nmake tools-count\nmake facade-smoke\nmake codex-facade-smoke\nmake claude-facade-smoke\nmake agy-facade-smoke\nmake profile-validation\nmake codex-profile-validation\nmake claude-profile-validation\nmake agy-profile-validation\nmake discovery-parity\nmake codex-claude-discovery-parity\nmake codex-deferred-acceptance\nmake launchagent-install\nmake launchagent-load\nmake launchagent-unload\nmake launchagent-uninstall\nmake systemd-install\nmake systemd-load\nmake systemd-unload\nmake systemd-uninstall\nmake windows-install\nmake windows-load\nmake windows-unload\nmake windows-uninstall\nmake linux-container-smoke\nmake windows-powershell-smoke\nmake release-smoke\nmake mutation\nmake mutation-linux\n```\n\n`make quality-gate` is repo-local. It does not call personal scripts outside this repo.\n\n`make codex-deferred-acceptance` is maintainer-only. It does not invoke Codex\nor an external LLM session. It reads the same YAML `smoke` probes and prints the\nexact `mcp__mcp_broker__` deferred wrapper calls to run inside an active Codex\nsession. See [docs/codex-deferred-tool-acceptance.md](docs/codex-deferred-tool-acceptance.md).\n\n## Project tree\n\n```text\nmcp-broker/\n|- .gitignore\n|- Makefile\n|- README.md\n|- pyproject.toml\n|- requirements.txt\n|- config/\n|  |- broker.example.yaml\n|  |- broker.private.yaml        # local, ignored by git\n|  `- broker.schema.json\n|- docs/\n|- registry/\n|- scripts/\n|  `- check_mutation_stats.py\n|- src/\n|  `- mcp_broker/\n|- tests/\n|  |- unit/\n|  |- journey/\n|  |- live/\n|  |- e2e/\n|  `- support/\n`- var/                         # tracked skeleton; generated contents ignored\n```\n\nGenerated reports stay under `var/`, especially `var/coverage/`, `var/test-logs/`, and `var/quality/`.\n\n## Docs\n\n- [SECURITY.md](SECURITY.md)\n- [CONTRIBUTING.md](CONTRIBUTING.md)\n- [ROADMAP.md](ROADMAP.md)\n- [docs/install.md](docs/install.md)\n- [docs/add-profile.md](docs/add-profile.md)\n- [docs/migration.md](docs/migration.md)\n- [docs/adoption-guide.md](docs/adoption-guide.md)\n- [docs/comparison.md](docs/comparison.md)\n- [docs/distribution.md](docs/distribution.md)\n- [docs/github-publication.md](docs/github-publication.md)\n- [docs/community-launch.md](docs/community-launch.md)\n- [docs/auth-recipes.md](docs/auth-recipes.md)\n- [docs/architecture.md](docs/architecture.md)\n- [docs/protocol.md](docs/protocol.md)\n- [docs/runtime-layout.md](docs/runtime-layout.md)\n- [docs/safety.md](docs/safety.md)\n- [docs/security-review.md](docs/security-review.md)\n- [docs/upstream-compatibility-matrix.md](docs/upstream-compatibility-matrix.md)\n- [docs/codex-deferred-tool-acceptance.md](docs/codex-deferred-tool-acceptance.md)\n- [docs/context-reduction-measurement.md](docs/context-reduction-measurement.md)\n- [docs/mutation-testing.md](docs/mutation-testing.md)\n- [docs/release-checklist.md](docs/release-checklist.md)\n- [docs/troubleshooting.md](docs/troubleshooting.md)\n\n## Design rules\n\n- Keep upstream definitions in central config.\n- Keep runtime state under `$HOME/mcp/mcp-broker`.\n- Keep private upstream inventory in `config/broker.private.yaml`, which is ignored by git.\n- Keep secret values out of config and source.\n- Do not hardcode personal paths in source, tests, docs, or public config.\n- Run build, test, runtime, and config operations through the Makefile.\n",
  "bytes": 22451,
  "sha": "9b7c91b309290666ed488ec55d59d02bb302af63cd7829f6ed6b453396802f11",
  "repo_slug": "doricstack/mcp-broker",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_doricstack_mcp_broker_29297700/readme"
}