PrepGenAICerts

MCP Configuration Scope (Four Levels) and the Secrets-in-Config Anti-Pattern

Core

Understand MCP servers, their primitives, and transports · Difficulty 2/5

0%
mcpconfiguration-scopesecrets-managementmcp-jsonanti-pattern

Explanation

Four Scope Levels, Four Config Locations

An MCP Server's configuration lives at one of four scope levels, and each level answers a different question about who sees it and where it's stored:

ScopeConfig locationShared?Fits
Local~/.claude.json, per-project entryNot shared, not committedA server tied to one project's context you're not ready to commit to the repo
UserPersonal settings, applied across all your projectsPersonal -- not shared with teammatesA personal utility you use regardless of which codebase you're in
Project.mcp.json at the repository rootCommitted to VCS, shared with every cloneA server the whole team needs, traveling with the code
EnterpriseAdmin-managed, org-widePushed to everyone by an administratorShared internal services or security tooling that must be present org-wide, not left to individual configuration

A subtlety on Project scope: committing .mcp.json shares the *configuration*, not a running server. For a stdio server configured this way, each teammate's clone still spawns its own local subprocess on their own machine -- so every teammate needs the same local runtime available (e.g., Node.js, if the server launches via npx). Committing the config doesn't eliminate the need for each machine to be able to run the server.

The Anti-Pattern: Secrets Placed Inline in Config

Here's a worked scenario that shows exactly how this goes wrong. A developer connects to a data-warehouse MCP Server and, to get it working quickly, places the server's auth token inline in .mcp.json:

{
  "type": "http",
  "url": "https://warehouse.internal/mcp",
  "headers": {
    "Authorization": "Bearer sk-abc123..."
  }
}

Swapping it for an environment variable was the intended next step -- except the file gets pushed to the repo before that happens, "just so the team can keep moving." Two days later, that same token is sitting in four or more separate places: the original machine, the repo's commit history (a later commit that removes the value doesn't erase it from history), every teammate's local clone the moment they pull, and whatever CI system checked the repo out. Rotating the now-compromised key is the correct remediation, but it's expensive: every other service still configured with the old key breaks the moment the key rotates, and each of those has to be found and updated separately.

The Fix: Reference an Environment Variable

{
  "type": "http",
  "url": "https://warehouse.internal/mcp",
  "headers": {
    "Authorization": "Bearer ${WAREHOUSE_MCP_TOKEN}"
  }
}

The committed configuration file now holds only the *reference* to a variable, never the secret value itself. Each teammate (and each CI runner) supplies WAREHOUSE_MCP_TOKEN through their own environment, and the actual credential never enters version control at all -- so there's nothing in history to rotate away from in the first place.

Common exam traps

  • Assuming that removing a secret from .mcp.json in a later commit removes it from exposure. It does not -- prior commits still carry the plaintext value in history, and the credential must be treated as compromised and rotated regardless of the follow-up commit.
  • Assuming Project scope means the server itself runs centrally. A committed .mcp.json for a stdio server still launches a fresh local subprocess on every teammate's own machine -- each of them needs the runtime installed.
  • Picking Local or User scope for a capability the whole team needs, or Project scope for a purely personal utility -- scope should match who needs to see and use the configuration, not habit.

Key Takeaways

  • MCP configuration has four scope levels: Local (~/.claude.json, per-project, not shared), User (personal, across all projects, not shared), Project (.mcp.json at repo root, committed, shared with clones), and Enterprise (admin-managed, org-wide)
  • A Project-scoped stdio server still runs per-teammate-machine after cloning -- each teammate needs the local runtime (e.g. Node for an npx-launched server) installed
  • Placing a secret inline in .mcp.json and committing it puts that credential in 4+ places within days: local machine, full repo history, every clone, and any CI runner -- and a later commit removing it does not remove it from history
  • Rotating a compromised key found this way is expensive: every other service still configured with the old value breaks and each has to be found and fixed separately
  • The fix is to reference an environment variable in the config (e.g. Bearer ${WAREHOUSE_MCP_TOKEN}) instead of an inline value, so the committed file never carries the secret at all

Glossary Terms

Related Concepts

PrepGenAICerts.com is an independent third-party exam-prep platform for the Claude Certified Architect (CCA-F) certification. We are not affiliated with, endorsed by, or acting on behalf of Anthropic PBC.

Note: New premium upgrades are temporarily paused while we resolve an issue with our payment provider. Existing premium members retain full access.