What you will be able to do
- Choose between local, project and user scope for an MCP server based on who should get it and where it should load
- Write a project-scoped .mcp.json entry that loads, including the type rule for remote servers
- Use ${VAR} and ${VAR:-default} references to keep credentials out of a committed .mcp.json, and predict what happens when a variable is unset
Key concept
MCP installation scope — Every MCP server in Claude Code belongs to exactly one scope. The scope decides which file holds its definition and who gets the server: only you in one project (local), only you in all projects (user), or everyone who clones the repository (project).
1.Three scopes, two files
Every MCP server you add to Claude Code lives in exactly one scope. The scope settles two things at once: which file holds the definition, and who ends up with the server. There are three scopes but only two files. Local scope and user scope both write to ~/.claude.json in your home directory. Project scope writes to .mcp.json at the repository root, and that is the file you commit.
| Scope | File | Available to |
|---|---|---|
| local (the default) | ~/.claude.json, under the entry for this project | Only you, only this project |
| project | ./.mcp.json in the project root | Everyone who clones the project |
| user | ~/.claude.json, under the top-level mcpServers key | Only you, all projects |
The default is the one to remember. If you run claude mcp add without a --scope flag, the server goes into local scope: it stays private to you and loads only in the project where you ran the command. That makes local scope the right place for a half-built server you are prototyping in one repository. Your teammates never see it, and your other projects don't load it. User scope is also private, but it follows you into every project, so it suits a personal utility you already trust. Project scope is the only one that travels with the code. Anyone who clones the repository gets the server.
claude mcp add --scope user --transport http claude-code-docs https://code.claude.com/docs/mcp
claude mcp add --scope project --transport http claude-code-docs https://code.claude.com/docs/mcpNothing is broken. Local scope is doing what it is designed to do. The quickstart's fix is to re-add the server from the project you are in now, or to add it with --scope user so it isn't tied to a project. The quickstart gives a second cause of an empty /mcp: editing a configuration file at a path Claude Code never reads. Claude Code reads only ~/.claude.json and <project>/.mcp.json. Similar-looking paths such as ~/.claude/mcp.json are ignored.
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.
Sources1
2.Writing a shared .mcp.json the team can load
Project scope is how a team shares tooling. A server defined once in .mcp.json is available to everyone who clones the repository, and cloud sessions load a committed .mcp.json too. The file holds one mcpServers object, keyed by server name. Each entry is either a remote endpoint or a program that runs on the local machine.
{
"mcpServers": {
"claude-code-docs": {
"type": "http",
"url": "https://code.claude.com/docs/mcp"
},
"playwright": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
}
}
}For an HTTP server, url is the endpoint Claude Code connects to. For a stdio server, command and args are the program it launches. The type field matters. Claude Code treats an entry with no type as a stdio server, so a remote entry that has a url but no type fails to load. Server names have rules as well. Use only letters, numbers, hyphens and underscores, and avoid the names Claude Code reserves for its built-in servers. An entry that uses a reserved name is skipped at load time with a warning.
An error in one entry doesn't break the whole file. Claude Code skips a malformed entry, loads the rest, and claude mcp list prints a parse warning that names the offending field.
Committing .mcp.json doesn't mean every teammate automatically runs every server in it. A project-scoped server that a user hasn't approved yet shows as Pending approval until they run claude interactively and accept it. Settings can decide this in advance. enableAllProjectMcpServers approves every server in project .mcp.json files, enabledMcpjsonServers approves the servers it names, and a disabledMcpjsonServers entry rejects a server. If none of these is set, Claude Code asks about each server.
{
"enabledMcpjsonServers": ["memory", "github"]
}3.Keeping credentials out of the committed file
Everyone with access to the repository can read a committed .mcp.json, so a token pasted into it is a leaked token. The fix is environment variable expansion. Where the secret belongs, you write a ${VAR} reference such as ${GITHUB_TOKEN}. You commit the reference, and each person's environment supplies the actual value on the machine that runs Claude Code. Two forms cover the cases you will meet: ${VAR} and ${VAR:-default}. The part worth memorizing is what happens when the variable is missing.
| Reference | Variable state | What Claude Code does |
|---|---|---|
| ${VAR} | set | Uses the variable's value |
| ${VAR:-default} | unset | Falls back to the default value |
| ${VAR} | unset, no :-default | Warns in claude mcp list and /mcp, naming the variable, and still loads the server with the ${VAR} text unexpanded |
| ${VAR} in a remote server's url or headers | unset (some credential variables) | Reads it as empty, with no warning |
Look at what the unset, no-default case doesn't do. It doesn't skip the server, and it doesn't substitute an empty string. The server still loads, the literal ${VAR} text is passed through, and a warning names the missing variable. The fix is to set the variable or give the reference a :-default. Remote servers are the exception to watch: in their url and headers, some credential variables come through empty without any warning.
The server receives us-east-1. The missing-variable warning applies only to a reference that is unset and has no :-default. Adding a fallback is one of the two fixes the docs recommend.
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.
The diagnostics are designed so they don't reveal secrets. If the same server name is defined in more than one scope with different endpoints, Claude Code quotes each endpoint as written, with ${VAR} references left unexpanded, so a resolved API key never shows up in the warning. Sign-ins are tracked separately: OAuth sign-ins are stored per endpoint, so each conflicting definition needs its own sign-in. Another check flags hidden leading or trailing whitespace in values such as headers.Authorization, usually a token pasted with a trailing newline, and it names the field without printing its value. Compare all of this with passing a credential when you add the server:
claude mcp add --env AIRTABLE_API_KEY=YOUR_KEY --transport stdio airtable \
-- npx -y airtable-mcp-server--env gives the server a literal value. That is acceptable for a private local-scope server. In a file the whole team shares, use a ${VAR} reference instead.
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.Local scope means the project's .mcp.json file.Why is that wrong?
Local scope is stored in ~/.claude.json under the entry for the current project. Only you get it, only in that project, and it is the default when you don't pass --scope. The shared file .mcp.json is project scope.
Covered in Three scopes, two files
2.If a ${VAR} reference points to an unset variable with no default, the server fails to load.Why is that wrong?
Claude Code warns and names the variable, but it still loads the server and passes the ${VAR} text through unexpanded. The fix is to set the variable or add a :-default fallback.
3.A remote server entry in .mcp.json only needs a url.Why is that wrong?
An entry without a type is read as stdio, so a url entry must also say http, sse or ws.
Covered in Writing a shared .mcp.json the team can load
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
“Only you, only this project. The default”
↩︎ Three scopes, two files“Local-scoped servers are tied to the project where you added them”
↩︎ Three scopes, two files“The correct files are ~/.claude.json and <project>/.mcp.json.”
↩︎ Three scopes, two files“Claude Code skips that entry and still loads the others.”
↩︎ Writing a shared .mcp.json the team can load“Cloud sessions: commit a .mcp.json to your repository; a session with one repository loads it.”
↩︎ Writing a shared .mcp.json the team can load“Local-scoped servers are tied to the project where you added them”
↩︎ Key concept“Only you, only this project. The default”
↩︎ Exam trap 1 - 2.https://code.claude.com/docs/en/mcpOfficial docs
“Claude Code reads an entry with no type as a stdio server, so a url entry without a type fails.”
↩︎ Writing a shared .mcp.json the team can load“If your configuration defines a server with a reserved name, Claude Code skips it at load time”
↩︎ Writing a shared .mcp.json the team can load“still loads the server with the ${VAR} text unexpanded. Set the variable or add a ${VAR:-default} fallback.”
↩︎ Keeping credentials out of the committed file“some credential variables read as empty instead, with no warning.”
↩︎ Keeping credentials out of the committed file“with ${VAR} references unexpanded, so it never shows a resolved value such as an API key.”
↩︎ Keeping credentials out of the committed file“which often comes from pasting a token with a trailing newline”
↩︎ Keeping credentials out of the committed file“still loads the server with the ${VAR} text unexpanded. Set the variable or add a ${VAR:-default} fallback.”
↩︎ Exam trap 2“Claude Code reads an entry with no type as a stdio server, so a url entry without a type fails.”
↩︎ Exam trap 3 - 3.https://code.claude.com/docs/en/settings-referenceOfficial docs
“Claude Code approves every MCP server defined in project .mcp.json files without a prompt”
↩︎ Writing a shared .mcp.json the team can load