Skip to main content

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โ€‹

PieceRole
home/.chezmoidata/mcp_servers.yamlSource of truth for server declarations
scripts/mcp_registry.pyNormalizes registry entries and resolves $(command) strings through a login shell
scripts/generate_mcp_configs.pyEmits tool-specific { "mcpServers": { ... } } documents
Tool injectorsPreserve 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:

ShapeRequired fieldsOptional fields
Command servername, 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 servername, type: http, urloauth_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โ€‹

  1. Edit home/.chezmoidata/mcp_servers.yaml.

  2. Preview the rendered per-tool configs:

    chezmoi diff
  3. 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:

StageScriptPurpose
Normalize registryscripts/mcp_registry.pyResolve $(command) strings through a login shell
Generate per-tool shapescripts/generate_mcp_configs.pyEmit 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:

InjectorOwnership rule
scripts/inject_mcp_into_codex_toml.pyReplaces 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.pyReplaces a "mcp": "__MCP_SERVERS__" placeholder with local command servers; HTTP entries are intentionally skipped.
scripts/merge_claude_mcp.pySurgically updates only the mcpServers key in ~/.claude.json, which Claude Code also writes runtime state into.

Per-tool targetsโ€‹

ToolTarget file (mcpServers)Rendered by hook
Claude Code~/.claude.json (mcpServers key)run_onchange_after_07-generate-mcp-configs.sh.tmpl
Pi~/.pi/agent/mcp.jsonrun_onchange_after_07-generate-mcp-configs.sh.tmpl
OMP~/.omp/agent/mcp.jsonrun_onchange_after_07-generate-mcp-configs.sh.tmpl
OpenCode~/.config/opencode/opencode.jsoncrun_onchange_after_07-merge-opencode-config.sh.tmpl
Codex~/.codex/config.tomlrun_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.