Personal machine configuration managed by chezmoi.
1) Install gitleaks (one-time):
-
macOS:
brew install gitleaks
2) Then enable hooks:
git config core.hooksPath .githooksdotfiles
├── .chezmoiignore
├── .chezmoiscripts/ # hooks that run during `chezmoi apply`
│ ├── run_after_20-merge-claude-mcp.sh # merges ~/.claude/mcp_servers.json into ~/.claude.json
│ └── run_after_30-merge-codex-mcp.sh # merges ~/.codex/mcp_servers.toml into ~/.codex/config.toml
├── .githooks/
│ └── pre-commit # local: blocks commits containing secrets (gitleaks)
├── .github/workflows/
│ └── secrets.yml # CI: blocks PRs/pushes containing secrets (gitleaks)
├── .gitignore
├── .gitleaks.toml # gitleaks rules and allowlist
├── dot_claude/
│ └── mcp_servers.json.tmpl # → ~/.claude/mcp_servers.json (intermediate)
├── dot_codex/
│ └── mcp_servers.toml.tmpl # → ~/.codex/mcp_servers.toml (intermediate)
├── dot_config/mcp/
│ └── servers.yaml.tmpl # single source of truth for MCP servers
├── dot_cursor/
│ └── private_mcp.json.tmpl # → ~/.cursor/mcp.json
├── dot_zshenv.tmpl # → ~/.zshenv
├── scripts/
│ └── verify-mcp.sh # post-apply: validates rendered MCP configs
└── README.mdAnything under a dot_* path is a chezmoi source file: chezmoi renames dot_foo → ~/.foo and expands any .tmpl suffix using values from ~/.config/chezmoi/chezmoi.toml when running chezmoi apply.
A single canonical source drives MCP server configuration for all three AI coding tools.
dot_config/mcp/servers.yaml.tmpl defines every MCP server. Edit this file
to add, remove, or modify a server; everything else is generated.
servers:
- name: <server-name>
transport: stdio | http
# stdio:
command: <executable>
args: [<arg>, ...]
env: { KEY: VALUE, ... }
# http:
url: <https://...>
headers: { HEADER: VALUE, ... }| Tool | Config file | How it's updated |
|---|---|---|
| Cursor | ~/.cursor/mcp.json |
Fully generated from dot_cursor/private_mcp.json.tmpl. |
| Claude Code | ~/.claude.json (mcpServers key) |
Generated intermediate at ~/.claude/mcp_servers.json, then merged in by .chezmoiscripts/run_after_20-merge-claude-mcp.sh. |
| Codex | ~/.codex/config.toml ([mcp_servers.*]) |
Generated intermediate at ~/.codex/mcp_servers.toml, then merged in by .chezmoiscripts/run_after_30-merge-codex-mcp.sh. |
Claude and Codex both write dynamic state (OAuth tokens, per-project trust, session history) into their main config files, so those files cannot be overwritten wholesale. The merge hooks replace only the MCP subsection and leave everything else byte-identical.
Edit dot_config/mcp/servers.yaml.tmpl, then:
chezmoi applyDelete the entry from dot_config/mcp/servers.yaml.tmpl, then:
chezmoi apply-
Update
~/.config/chezmoi/chezmoi.tomlwith the new value. This file is local only and is never committed. -
Re-render everything:
chezmoi apply
After chezmoi apply, confirm the output files parse, agree on server names, and contain the real (non-placeholder) secret:
./scripts/verify-mcp.sh-
Clone and apply:
chezmoi init --apply git@github.com:<user>/dotfiles.git
-
Populate
~/.config/chezmoi/chezmoi.tomlwith local secrets (e.g.context7_api_key). -
Apply again:
chezmoi apply
Secrets referenced in templates, such as {{ .context7_api_key }}, live in ~/.config/chezmoi/chezmoi.toml, which is machine-local and not part of this repo. Three guards protect against accidental commits of literal secret values:
.gitignorekeeps backup and rendered artifacts out of the repo.- Pre-commit hook (
.githooks/pre-commit) runsgitleaksagainst staged changes on every commit. Enable hooks after cloning: see Setup. - CI enforcement (
.github/workflows/secrets.yml) runsgitleakson every PR tomainand on direct pushes tomain. Required to pass before merging.