Sharing a Claude Code config across a team
Versioned CLAUDE.md, settings.json vs settings.local.json, shared MCP servers, hooks: what gets shared, what stays local, and the traps in between.
Two developers, same repo, same prompt. On one machine, Claude Code runs the tests before every commit and refuses to touch migration files. On the other, it commits without testing and rewrites a production migration. Same project, opposite behavior: half a day lost figuring out why.
The reason is always the same: the team’s Claude Code config lives on machines, not in the repo. Here is how to structure what gets shared, what stays personal, and the traps waiting in between.
The three configuration layers
Claude Code reads its configuration as a cascade, from most general to most specific:
- User:
~/.claude/(orCLAUDE_CONFIG_DIR). Your global preferences, your authentication, your memory. Never in git. - Shared project:
.claude/settings.jsonandCLAUDE.mdat the repo root. Versioned, identical for the whole team. - Local project:
.claude/settings.local.json. Your personal overrides on this project. Physically in the repo, but ignored by git.
The most specific layer wins. A setting in settings.local.json overrides the same setting in settings.json, which overrides the one in ~/.claude/settings.json. This is the single most important fact in this article: when a teammate’s behavior differs from yours, the answer is almost always in their local layer.
What goes in git
CLAUDE.md: the project rules
CLAUDE.md at the repo root is read by Claude Code at the start of every session. It is the right place for everything a new developer should know:
# Project rules
## Conventions
- Tests use Vitest, not Jest. `npm run test:unit` before any commit.
- Migrations in db/migrations/ are never edited, create a new one.
- Commit messages in English, conventional commits format.
## Architecture
- The payments/ module never depends on notifications/.
- Every new route goes through the auth middleware, no exceptions.
Write it like an onboarding doc: concrete, checkable rules, not generalities. “Write clean code” does nothing; “migrations are never edited” prevents an incident.
.claude/settings.json: shared permissions and hooks
{
"permissions": {
"allow": [
"Bash(npm run test:*)",
"Bash(npm run lint)",
"Read(**)"
],
"deny": [
"Bash(git push --force*)",
"Read(.env*)",
"Read(secrets/**)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash(git commit*)",
"hooks": [
{
"type": "command",
"command": "npm run lint --silent"
}
]
}
]
}
}
Two things to take from this example:
- Shared
denyrules are your team safety net. Blocking.envreads and force pushes for everyone is one line of JSON. The same rule communicated over Slack is one incident per quarter. - The lint-before-commit hook runs on everyone’s machine, identically. It is the executable version of “don’t forget to lint”.
.mcp.json: the project’s MCP servers
MCP servers declared in .mcp.json at the repo root are offered to the whole team:
{
"mcpServers": {
"postgres-dev": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "${DEV_DATABASE_URL}"
}
}
}
}
The crucial point: ${DEV_DATABASE_URL} is an environment reference, not a value. Each developer defines it in their shell or local .env. The versioned file describes the server, never the secret.
On first launch, every team member sees an approval prompt for the servers in .mcp.json. That is normal and desirable: nobody should run an MCP server they have not approved. As for which servers deserve a spot in that file, I sorted through them in the MCP servers that actually earn their keep.
The rest of the .claude/ directory
Since 2026, the project directory can also hold versionable subfolders: rules/ to split up a large CLAUDE.md, agents/ for subagent definitions, skills/ for project skills. Same logic for all of them: if the whole team should get the same behavior, it goes in git.
What stays local
.claude/settings.local.json
Your personal overrides on the project: a different default model, wider permissions because you are the lead, a notification hook nobody else cares about. Claude Code adds this file to .gitignore automatically when creating it, but check:
.claude/settings.local.json
Secrets, always
API keys, tokens, URLs with credentials: never in settings.json, never in .mcp.json, never in CLAUDE.md. Environment references exist for exactly this. A secret committed in a Claude Code config is a committed secret, with everything that implies about rotation and history scrubbing.
Authentication and memory
Each developer has their own account, subscription, and memory. None of it is shared through the repo. If you juggle several accounts on one machine (personal and client, say), I covered that setup in multiple Claude Code accounts on a single machine.
The traps
The local-overrides-shared trap
The classic: the team adds a deny rule to settings.json, one developer has an older, wider allow in their settings.local.json, and the team rule silently does not apply on their machine. Nobody sees it, because the local file is not in git.
The debugging reflex: when behavior diverges between two machines, compare the local layers first.
# what git knows about
cat .claude/settings.json
# what git does not know about
cat .claude/settings.local.json 2>/dev/null
The drifting CLAUDE.md trap
Three repos, three copy-pasted CLAUDE.md files, each edited independently. Six months later the conventions have diverged and nobody knows which one is right. Claude Code has no cross-repo config inheritance yet: if you have a shared baseline, manage it as a synced template (a script that propagates the common sections), not as free-floating copies.
The ignored-approval trap
A new MCP server lands in .mcp.json through a pull request. Your teammates see an approval prompt on their next launch and click yes without reading. The approval mechanism only protects a team that actually reads what it approves: call out any new MCP server in the description of the PR that introduces it.
Where to start
If your team has none of this today, the order that pays off fastest:
- A
CLAUDE.mdwith your five most-violated rules. Half an hour, immediate payoff. - The security
denyrules in.claude/settings.json: secrets unreadable, force push blocked. - The
.mcp.jsonwith your shared servers and environment references. - Shared hooks last: the most powerful piece but also the most intrusive, so introduce it once the team already trusts the rest.
Team config for Claude Code is like CI: the day it is in place, you wonder how you worked without it. And everyone’s local file stays right there for experimentation, exactly where it belongs.