As soon as your team hits three MCP-curious developers, "paste this config into Claude Desktop" in Slack breaks down. Here is the workflow we use — Git-versioned, secret-safe, one command for new hires.
The goal
A new hire runs one command on day one and ends up with the team’s full MCP loadout: GitHub, Postgres (dev DB), Slack, internal tools. Per-user secrets never touch the repo.
Repo layout
your-repo/
mcp/
servers.d/
00-github.json
10-postgres-dev.json
20-slack.json
30-internal-tools.json
secrets.example.env
install.sh
.gitignore # contains secrets.env
One file per server
Each JSON is a fragment — just mcpServers with one key. Example servers.d/00-github.json:
{
"mcpServers": {
"github": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-e", "GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server:v1.4.2"
]
}
}
}
Notice — no token in the file. Secrets come from the local secrets.env.
secrets.example.env
Committed, has no actual secrets:
# Each dev copies this to secrets.env and fills their own values
GITHUB_PERSONAL_ACCESS_TOKEN=
POSTGRES_DEV_URL=
SLACK_BOT_TOKEN=
Add secrets.env to .gitignore.
install.sh — the bootstrap
#!/bin/sh
set -e
# 1. Detect Claude config path
case "$(uname)" in
Darwin) CFG="$HOME/Library/Application Support/Claude/claude_desktop_config.json" ;;
Linux) CFG="$HOME/.config/Claude/claude_desktop_config.json" ;;
*) CFG="$APPDATA/Claude/claude_desktop_config.json" ;;
esac
# 2. Load secrets into env
set -a; . ./mcp/secrets.env; set +a
# 3. Merge all servers.d/*.json into one config
mkdir -p "$(dirname "$CFG")"
jq -s '{ mcpServers: ([.[].mcpServers] | add) }' \
mcp/servers.d/*.json > "$CFG"
# 4. Substitute env vars (optional — only for templated values)
envsubst < "$CFG" > "$CFG.tmp" && mv "$CFG.tmp" "$CFG"
echo "✔ MCP config written to $CFG"
echo " Restart Claude Desktop to pick up changes."
A new hire runs:
cp mcp/secrets.example.env mcp/secrets.env
# fill in tokens
./mcp/install.sh
CI check: don’t commit broken JSON
Add a workflow that runs jq '.' servers.d/*.json — fails the PR on invalid JSON. Saves a weekly "why does the config break for half the team" meeting.
Secrets via 1Password / Vault
For mature teams, don’t even let devs copy tokens into files. Use 1Password CLI or Vault:
# in install.sh
export GITHUB_PERSONAL_ACCESS_TOKEN=$(op read "op://Eng/GitHub-MCP/token")
export POSTGRES_DEV_URL=$(op read "op://Eng/Postgres-Dev/url")
Tokens never hit disk. Rotation is just a 1Password update.
Per-env configs
If you have dev / staging / prod databases, split into subfolders:
mcp/
servers.d/
common/ # GitHub, Slack — same everywhere
dev/ # dev Postgres, dev Sentry
staging/ # staging Postgres
Accept ENV=dev|staging env var in install.sh, merge common/ + $ENV/.
Read me before you ship to devs
- Never share the same MCP token across devs — per-user PATs only. Rotating one user’s token should not break others.
- Scope tokens hard: GitHub PATs with minimum-needed permissions; Postgres role read-only.
- Document what each server does in a top-level
mcp/README.md. Especially call out any server that can write.