Context
A single source of truth — home/.chezmoidata/mcps/{00-base,10-chipwolf}.yaml — is rendered into MCP config for six+ tools. Each renderer independently re-implements the same filtering and resolution logic:
conditions matching (e.g. private: true/false)
- per-target gating via
targets.<tool>.enabled
server.enabled gating
$data.* placeholder resolution (hasPrefix "$data." | trimPrefix | index $root)
That's ~150 lines of near-identical conditional logic spread across the renderers. Each renderer is shallow: its interface (one tool's MCP config) is nearly as complex as its implementation because the filtering dominates and only the output format differs per tool.
Files
Source:
home/.chezmoidata/mcps/00-base.yaml
home/.chezmoidata/mcps/10-chipwolf.yaml
Renderers (each re-implements the filtering):
home/dot_cursor/mcp.json.tmpl
home/modify_dot_claude.json
home/private_dot_mcpproxy/modify_mcp_config.json (also translates streamableHttp → streamable-http)
home/dot_pi/agent/mcp.json.tmpl
home/AppData/Roaming/Claude/modify_claude_desktop_config.json
home/dot_config/opencode/* (partially centralised already)
home/dot_config/zed/modify_settings.json + .chezmoitemplates/zed-context-servers.tmpl
What to do
- Load the
update-mcp-servers skill (.agents/skills/update-mcp-servers/SKILL.md) first — these files are governed by it.
- Add
home/.chezmoitemplates/mcp-servers-resolved.tmpl that takes the target name + root and yields the filtered, $data-resolved server set (one place for conditions / targets.<tool>.enabled / server.enabled / $data.*).
- Rewrite each renderer to call the partial once and handle only its own output format (Cursor
transport, Claude Code type, mcpproxy transport translation, etc.).
- Verify byte-identical output before/after for every target with
chezmoi cat / chezmoi execute-template and diff. No behaviour change is intended.
Out of scope
- Changing which servers are enabled or any server's config.
- Touching the YAML schema.
Context
A single source of truth —
home/.chezmoidata/mcps/{00-base,10-chipwolf}.yaml— is rendered into MCP config for six+ tools. Each renderer independently re-implements the same filtering and resolution logic:conditionsmatching (e.g.private: true/false)targets.<tool>.enabledserver.enabledgating$data.*placeholder resolution (hasPrefix "$data." | trimPrefix | index $root)That's ~150 lines of near-identical conditional logic spread across the renderers. Each renderer is shallow: its interface (one tool's MCP config) is nearly as complex as its implementation because the filtering dominates and only the output format differs per tool.
Files
Source:
home/.chezmoidata/mcps/00-base.yamlhome/.chezmoidata/mcps/10-chipwolf.yamlRenderers (each re-implements the filtering):
home/dot_cursor/mcp.json.tmplhome/modify_dot_claude.jsonhome/private_dot_mcpproxy/modify_mcp_config.json(also translatesstreamableHttp→streamable-http)home/dot_pi/agent/mcp.json.tmplhome/AppData/Roaming/Claude/modify_claude_desktop_config.jsonhome/dot_config/opencode/*(partially centralised already)home/dot_config/zed/modify_settings.json+.chezmoitemplates/zed-context-servers.tmplWhat to do
update-mcp-serversskill (.agents/skills/update-mcp-servers/SKILL.md) first — these files are governed by it.home/.chezmoitemplates/mcp-servers-resolved.tmplthat takes the target name + root and yields the filtered,$data-resolved server set (one place for conditions /targets.<tool>.enabled/server.enabled/$data.*).transport, Claude Codetype, mcpproxy transport translation, etc.).chezmoi cat/chezmoi execute-templateanddiff. No behaviour change is intended.Out of scope