What you will be able to do
- Choose between local, project and user scope for an MCP server based on where it should load and who should get it
- Write a project-scoped .mcp.json entry that takes credentials from environment variables instead of committing them
- Predict how Claude Code behaves when a referenced environment variable is unset, has a default, or when a teammate hasn't approved a project server yet
Key concept
Shared config, personal secrets — A project-scoped .mcp.json is committed so the whole team gets the same servers. Every credential in it is written as a ${VAR} reference, which each developer's own environment fills in at load time, so the file in version control never holds a secret.
1.Three scopes, two questions
Every MCP server you add to Claude Code goes into an installation scope. The scope settles two things together: which projects the server loads in, and whether your teammates get it. The exam guide frames this as project-level versus user-level, but Claude Code has a third scope, local, which is the one you get by default. You need all three to answer scoping questions correctly.
| Scope | Stored in | Loads in | Shared with team |
|---|---|---|---|
| local (default) | ~/.claude.json, under the entry for this project | Current project only | No |
| project | Project root .mcp.json | Current project only | Yes, via version control |
| user | ~/.claude.json, under the top-level mcpServers key | All your projects | No |
Project scope is for shared team tooling. The entry goes into .mcp.json at the repository root, gets committed, and loads for everyone who clones the repo. Pass --scope project and Claude Code writes the file for you:
{
"mcpServers": {
"shared-server": {
"type": "http",
"url": "https://example.com/mcp"
}
}
}User scope is for personal servers you want in every project, such as a tool you like that the team hasn't adopted. Local scope is for a server that is yours alone and belongs to one repository, such as an unstable prototype you don't want loading anywhere else. Local and user servers are both stored in ~/.claude.json, so knowing which file a server is in doesn't tell you its scope. What matters is the key it sits under: a project entry or the top-level mcpServers.
# Add a user server
claude mcp add --transport http hubspot --scope user https://mcp.hubspot.com/anthropicTwo mistakes come up often. Claude Code reads only ~/.claude.json and <project>/.mcp.json. A file at a path like ~/.claude/mcp.json is silently ignored, and the symptom is that /mcp reports no servers. Also, if you define the same server name with different endpoints in more than one scope, Claude Code warns about the conflict. Keep the definition you want and remove the others with claude mcp remove <name> --scope <scope>.
Checkpoint 1 of 6· Check yourself
You want a personal, experimental MCP server available in every repository you open, but your teammates should never get it. Which scope fits?
User scope loads in all your projects and is never shared. Local scope would limit the server to one project, and project scope would share it with the team.
“Only you, all projects”Source: code.claude.com
Checkpoint 2 of 6· Exam question
An architect is rolling out a GitHub MCP server for the whole engineering team. Every teammate has their own GitHub personal access token, and the config must be checked into the repo without ever committing a real secret. How should the architect configure this?
Correct answer: A — Add the server with project scope in .mcp.json, and set the header to `Authorization: Bearer ${GITHUB_TOKEN}` so each teammate's environment supplies the value at connection
- A. Correct. Project scope stores the server in .mcp.json for team-wide sharing via version control, and ${GITHUB_TOKEN} expansion pulls the value from each user's own environment at connection time, so no secret is ever committed.
- B. User scope stores the entry in ~/.claude.json, which is private to one machine and not shared with the team, and pasting literal tokens defeats the goal of never committing secrets.
- C. Local scope is private to the current project on one machine and is not checked into version control at all, so it cannot serve as the shared team configuration the scenario requires.
- D. Ignoring .mcp.json removes the shared configuration entirely, meaning no teammate gets the server automatically and the stated goal of a checked-in, team-wide config is not met.
2.Keeping tokens out of a committed .mcp.json
Committing .mcp.json creates a problem: shared servers usually need credentials, and a token written into a committed file is a leaked token. Claude Code handles this with environment variable expansion inside .mcp.json. It supports two forms: ${VAR}, which takes the variable's value, and ${VAR:-default}, which falls back to the default when the variable isn't set. The docs list the fields where expansion applies: command, args, env, url, and headers. That covers both stdio servers (which take credentials through env or args) and remote servers (which take them through headers).
{
"mcpServers": {
"api-server": {
"type": "http",
"url": "${API_BASE_URL:-https://api.example.com}/mcp",
"headers": {
"Authorization": "Bearer ${API_KEY}"
}
}
}
}The committed file describes the shape of the connection: the endpoint, the header name, and where the token comes from. The token itself never appears. Each developer exports API_KEY in their own shell. If API_BASE_URL is unset, the URL falls back to https://api.example.com. A shared GitHub server works the same way, with "Authorization": "Bearer ${GITHUB_TOKEN}" in place of a literal personal access token.
Compare this with the docs' command-line example, claude mcp add --transport http github https://api.githubcopilot.com/mcp/ --header "Authorization: Bearer YOUR_GITHUB_PAT". That command writes a literal token into the configuration. At local scope only your own ~/.claude.json holds it. Copy the same literal into a project-scoped entry and you have committed it.
Checkpoint 3 of 6· Fill the gap
Which key carries the bearer token for this HTTP server?
{
"mcpServers": {
"api-server": {
"type": "http",
"url": "${API_BASE_URL:-https://api.example.com}/mcp",
" ? ": {
"Authorization": "Bearer ${API_KEY}"
}
}
}
}Remote HTTP servers authenticate through headers. env and args configure a locally launched stdio process.
Checkpoint 4 of 6· Exam question
A developer wants to try out an experimental local MCP server that queries their personal Notion workspace. They do not want it to appear for any other teammate, and they want it available whenever they open any project on their own machine. Which configuration achieves this?
Correct answer: A — Add the server with user scope so the entry is written to ~/.claude.json and loads across every project on that machine without being shared
- A. Correct. User scope stores the server in ~/.claude.json and makes it available across all of that user's projects while remaining private to their account, matching cross-project personal use.
- B. Project scope writes to .mcp.json specifically so the server is shared with the whole team via version control; there is no per-server flag to keep a project-scoped entry personal.
- C. Local-scoped servers are stored in ~/.claude.json under that specific project's path, not in .mcp.json, and they only load in the project where they were added rather than across all projects.
- D. MCP servers are configured through mcpServers entries in .mcp.json or ~/.claude.json, not through the general settings.json permission/settings file.
Sources2
3.What teammates see when they load your .mcp.json
A committed .mcp.json doesn't connect automatically on a teammate's machine. Until they run claude interactively and approve it, claude mcp list reports the server as ⏸ Pending approval. This is deliberate, because a repository you cloned shouldn't be able to start processes on your machine without your consent.
The next issue is environment variables that aren't set. If a ${VAR} reference names a variable that is unset and has no :-default, Claude Code doesn't refuse the server. It warns in claude mcp list and /mcp, naming the variable, and then loads the server with the literal ${VAR} text in place. The usual result is a server that receives a nonsense argument or header. The fix is to set the variable or add a ${VAR:-default} fallback. Remote servers have one exception: in a url or headers value, some credential variables are read as empty, and no warning is shown.
Claude Code also avoids leaking expanded values in its own output. Warnings and connection errors quote the configuration as written, with ${VAR} references left unexpanded, so a resolved API key never appears in the message. It also warns about leading or trailing whitespace in values such as headers.Authorization, which usually comes from pasting a token with a trailing newline. It names the field without echoing the value, and it doesn't trim the whitespace for you.
For remote HTTP servers that support it, OAuth avoids handling a token yourself. Add the server, then sign in from /mcp in the browser. Claude Code stores and refreshes the tokens. If you configured the Authorization header yourself and the server returns 401 or 403, Claude Code reports a failed connection instead of offering a sign-in, because the credential that needs fixing is the one you supplied.
Checkpoint 5 of 6· Check yourself
A teammate pulls a repo whose newly committed .mcp.json adds a server. Before they have run claude interactively, what does claude mcp list show for it?
Project-scoped servers need each user's approval before they connect. Until then, both claude mcp list and claude mcp get show them as pending.
“a project-scoped server from .mcp.json that you haven’t approved yet”Source: code.claude.com
Checkpoint 6 of 6· Exam question
A repository's .mcp.json defines a server named `analytics`, and a developer also has a server named `analytics` added with local scope on their machine pointing at a different endpoint entirely. When that developer runs Claude Code in the project, which server definition is used?
Correct answer: A — The local-scoped definition, because local scope takes precedence over project scope when names collide, and the two entries are not merged
- A. Correct. The scope hierarchy is local, then project, then user, then plugin-provided, then claude.ai connectors, and Claude Code uses the entire entry from the highest-precedence source rather than merging fields.
- B. Version control tracking doesn't grant precedence; the documented hierarchy places local scope above project scope regardless of where each is stored.
- C. Claude Code does not merge fields across scopes for a duplicate name; it uses the complete entry from whichever scope wins precedence.
- D. A duplicate name across scopes is resolved by precedence order, not treated as an error that disables both servers.
Sources2
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.A personal prototype server that should load in only one repository belongs at user scope, because user scope means 'not shared'.Why is that wrong?
User scope is unshared, but it loads in all your projects. A server for one repository that only you use belongs at local scope, which is the default.
Covered in Three scopes, two questions
2.If a ${VAR} in .mcp.json is unset and has no default, Claude Code refuses to load that server.Why is that wrong?
Claude Code warns, naming the variable, and still loads the server with the ${VAR} text unexpanded. The server then gets the literal placeholder.
3.Once .mcp.json is committed, the server connects automatically for every teammate.Why is that wrong?
Each user has to approve a project-scoped server interactively. Until then it shows as Pending approval.
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.https://code.claude.com/docs/en/mcp-quickstartOfficial docs
“Everyone who clones the project”
↩︎ Three scopes, two questions“The correct files are ~/.claude.json and <project>/.mcp.json.”
↩︎ Three scopes, two questions“Only you, only this project. The default”
↩︎ Exam trap 1“Only you, only this project. The default”
↩︎ Prediction“Only you, all projects”
↩︎ Checkpoint - 2.https://code.claude.com/docs/en/mcpOfficial docs
“if you define the same server name in more than one scope with different endpoints, Claude Code warns about the conflict”
↩︎ Three scopes, two questions“${VAR:-default}: expands to VAR if set, otherwise uses default”
↩︎ Keeping tokens out of a committed .mcp.json“headers: for HTTP server authentication”
↩︎ Keeping tokens out of a committed .mcp.json“In a remote server’s url and headers, some credential variables read as empty instead, with no warning.”
↩︎ What teammates see when they load your .mcp.json“with ${VAR} references unexpanded, so it never shows a resolved value such as an API key”
↩︎ What teammates see when they load your .mcp.json“which often comes from pasting a token with a trailing newline”
↩︎ What teammates see when they load your .mcp.json“Authentication tokens are stored securely and refreshed automatically”
↩︎ What teammates see when they load your .mcp.json“${VAR}: expands to the value of environment variable VAR”
↩︎ Key concept“still loads the server with the ${VAR} text unexpanded”
↩︎ Exam trap 2“Run claude interactively to review and approve it.”
↩︎ Exam trap 3“a project-scoped server from .mcp.json that you haven’t approved yet”
↩︎ Checkpoint