CertSafari
    CLAUDE-CERTIFIED-ARCHITECT-FOUNDATIONS-CCAR-F · Lessons

    Domain 2 · Lesson 11/30

    MCP Server Scopes and .mcp.json Configuration in Claude Code

    Integrate MCP servers into Claude Code and agent workflows

    10 min read
    3.6% of exam
    3 sources
    Published 28 Sep 2026
    Docs as of 26 Sep 2026

    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.

    MCP scopes: where each one is stored and who can use it
    ScopeFileAvailable to
    local (the default)~/.claude.json, under the entry for this projectOnly you, only this project
    project./.mcp.json in the project rootEveryone who clones the project
    user~/.claude.json, under the top-level mcpServers keyOnly 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.

    The same server added at user scope (only you, every project) and at project scope (written to .mcp.json for the team)bash
    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/mcp

    Nothing 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?

    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.

    A .mcp.json with one remote (http) server and one local (stdio) serverjson
    {
      "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.

    Pre-approving two named .mcp.json servers in settingsjson
    {
      "enabledMcpjsonServers": ["memory", "github"]
    }

    Sources213

    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.

    How a ${VAR} reference in an MCP server's configuration behaves
    ReferenceVariable stateWhat Claude Code does
    ${VAR}setUses the variable's value
    ${VAR:-default}unsetFalls back to the default value
    ${VAR}unset, no :-defaultWarns 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 headersunset (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.

    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?

    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:

    Passing a literal key with --env when adding a stdio serverbash
    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?

    Sources2

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 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. 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.

      Covered in Keeping credentials out of the committed file

    3. 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. 1.
      “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. 2.
      “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. 3.
      “Claude Code approves every MCP server defined in project .mcp.json files without a prompt”
      ↩︎ Writing a shared .mcp.json the team can load

    Continue to page 2 of 2

    MCP Tools and Resources in Agent Workflows