MCP Servers
A single canonical registry defines every MCP server once. At chezmoi apply time, generators render per-tool configs for Claude Code, Pi, OMP, Codex, and OpenCode, avoiding five hand-maintained copies of the same server list.
Use this page when adding, removing, or debugging an MCP server, or when tracing how a server reaches a given assistant.
Mental modelโ
| Piece | Role |
|---|---|
home/.chezmoidata/mcp_servers.yaml | Source of truth for server declarations |
scripts/mcp_registry.py | Normalizes registry entries and resolves $(command) strings through a login shell |
scripts/generate_mcp_configs.py | Emits tool-specific { "mcpServers": { ... } } documents |
| Tool injectors | Preserve live runtime-owned config while replacing only the MCP section |
The registry mechanics are generic. The registry currently declares no servers.
Registry: mcp_servers.yamlโ
Each entry is one of two shapes:
| Shape | Required fields | Optional fields |
|---|---|---|
| Command server | name, command, args (list) | exclude_tools (a list of tool names) to omit the server for specific tools that cannot express per-tool membership via oauth_by_tool |
| HTTP server | name, type: http, url | oauth_by_tool for per-tool OAuth client metadata, since tools expect different OAuth field shapes |
The registry currently declares no servers, so every generated MCP config is empty.
An HTTP server reaches a harness only when oauth_by_tool names that harness; every other harness omits it. OpenCode and Codex emit command servers only and skip every HTTP entry.
Hosted OAuth limitsโ
Codex supports streamable HTTP MCP natively, but its OAuth callback settings are global (mcp_oauth_callback_port / mcp_oauth_callback_url), and its bearer_token_env_var support reads the env var once at launch, dying with that token. Codex therefore gets no hosted server.
Pi 1.0.0 connects HTTP servers through its built-in MCP support, which accepts a registered OAuth client (oauth.clientId, callbackPort or callbackUrl, scope).
Using itโ
Add or change a serverโ
-
Preview the rendered per-tool configs:
chezmoi diff -
Regenerate them:
chezmoi apply
Verification:
chezmoi apply
python3 -m json.tool < ~/.pi/agent/mcp.json
python3 -c "import json; print(list(json.load(open('$HOME/.claude.json')).get('mcpServers', {})))"
codex mcp list # command servers only
Generation pipelineโ
The common pipeline has two stages:
| Stage | Script | Purpose |
|---|---|---|
| Normalize registry | scripts/mcp_registry.py | Resolve $(command) strings through a login shell |
| Generate per-tool shape | scripts/generate_mcp_configs.py | Emit a tool-specific { "mcpServers": { ... } } document |
Per-tool transforms handle schema differences between tools.
Pi emits { url, oauth } for HTTP servers, with scope normalised to space-separated tokens, as its built-in MCP expects. Other tools have their own config schemas.
Pi's built-in MCP defaults a server's exposure to codemode: its tools are not declared to the model and are called from codemode scripts as tools.mcp__<server>__<tool>. See Pi coding agent settings.
Tools whose config is not plain JSON get dedicated injectors with explicit ownership rules:
| Injector | Ownership rule |
|---|---|
scripts/inject_mcp_into_codex_toml.py | Replaces a # __MCP_SERVERS__ marker in the authoritative base config, then reattaches only valid runtime MCP approvals, hook trust hashes, project trust levels, and unsigned-32-bit TUI model-availability counters. Other live tables are discarded. |
scripts/inject_mcp_into_opencode_jsonc.py | Replaces a "mcp": "__MCP_SERVERS__" placeholder with local command servers; HTTP entries are intentionally skipped. |
scripts/merge_claude_mcp.py | Surgically updates only the mcpServers key in ~/.claude.json, which Claude Code also writes runtime state into. |
Per-tool targetsโ
| Tool | Target file (mcpServers) | Rendered by hook |
|---|---|---|
| Claude Code | ~/.claude.json (mcpServers key) | run_onchange_after_07-generate-mcp-configs.sh.tmpl |
| Pi | ~/.pi/agent/mcp.json | run_onchange_after_07-generate-mcp-configs.sh.tmpl |
| OMP | ~/.omp/agent/mcp.json | run_onchange_after_07-generate-mcp-configs.sh.tmpl |
| OpenCode | ~/.config/opencode/opencode.jsonc | run_onchange_after_07-merge-opencode-config.sh.tmpl |
| Codex | ~/.codex/config.toml | run_onchange_after_07-merge-codex-config.sh.tmpl |
LetsFG is intentionally not exposed through the shared MCP registry because its tools are irrelevant to most sessions. Agents load its skill on demand instead. See Tool configs for details.
Relatedโ
- Tool configs โ per-assistant settings and config merging
- AI assistants โ shared instructions and configuration