Claude Code MCP Configuration File: Scope, JSON, and Safety
Put Claude Code MCP servers in the right configuration scope, write a reviewable .mcp.json file, validate it before a session starts, and keep credentials out of the repository.
The short answer
Use a project-root .mcp.json file when a team can safely share an MCP server definition. Use ~/.claude.json for private local or user-scoped servers. In both places, treat configuration as code: validate the JSON, state the transport explicitly, and pass secrets by environment variable rather than committing them.
In this guide
Choose the scope before you write JSON
The most common Claude Code MCP configuration mistake is choosing a file because it is convenient, then discovering that a colleague cannot see the server or that a token has been committed. Claude Code separates local, project, and user scopes. The official MCP documentation describes project scope as a version-controlled .mcp.json file, while local and user scopes live in your home-level configuration.
| Scope | Who loads it | Storage | Good use |
|---|---|---|---|
| Local | One project, private to you | ~/.claude.json | Experiments and personal servers |
| Project | One repository, shared in Git | .mcp.json at the repository root | Safe team-wide server definitions |
| User | All your projects, private to you | ~/.claude.json | Personal utilities used everywhere |
A project file is a contract with the team. It should answer which server is connected, which transport it uses, and how someone supplies their own credential. It should not contain a credential, a private filesystem path, or a one-off command that other developers cannot run. Keep those in a user or local configuration and document the required environment variable instead.
Write a team-safe .mcp.json file
Start with the smallest useful server entry. This example is intentionally incomplete as a production integration: it shows a shared remote endpoint and an environment-variable placeholder, but it does not put a real token in source control. Replace the URL with a server your team has reviewed.
{
"mcpServers": {
"team-catalog": {
"type": "http",
"url": "${CATALOG_MCP_URL:-https://mcp.example.com}",
"headers": {
"Authorization": "Bearer ${CATALOG_MCP_TOKEN}"
}
}
}
}The top-level key is mcpServers. Each server name becomes the identifier that teammates see in Claude Code. Prefer names that describe the system and access boundary, such as team-catalog-readonly, not vague names such as tools or prod.
Do not copy secrets into JSON examples
A committed bearer token is not a configuration shortcut. Rotate it, remove it from Git history, and move it to your team's approved secret-delivery path. A checked-in file should refer to an environment variable, then fail visibly when that variable is missing.
Remote MCP servers need an explicit transport
For a remote server, type and url work together. Claude Code's reference notes that an entry with a URL but no type is interpreted as a stdio entry and skipped. Use http for the current remote HTTP transport; use sse only for an existing server that still requires it, and use ws only when the server genuinely needs a persistent WebSocket connection.
{
"mcpServers": {
"issue-tracker": {
"type": "http",
"url": "https://mcp.example.com/issues",
"headers": {
"X-Workspace": "${WORKSPACE_ID}",
"Authorization": "Bearer ${ISSUES_MCP_TOKEN}"
}
}
}
}Before adding a remote server, decide what data it can read and which actions it can take. A server that sees issue text, database rows, or deployment controls can introduce prompt-injection and authorization risk. The correct configuration scope does not make an untrusted server safe; it only controls who loads it.
Local stdio servers are processes, not just JSON
A stdio MCP server runs as a local process. The configuration must make the process auditable: name the executable, show arguments separately, and keep any environment variables narrow. The double dash in the equivalent claude mcp add command separates Claude Code options from the server command. In JSON, that separation appears as distinct command and args fields.
{
"mcpServers": {
"local-notes": {
"command": "node",
"args": ["./scripts/mcp-notes-server.mjs"],
"env": {
"NOTES_ROOT": "${NOTES_ROOT:-./notes}"
}
}
}
}Avoid a project configuration that starts a general shell wrapper, points to an arbitrary absolute path, or gives a tool unbounded access to files. If the server writes files or calls an API, apply the same validate-before-execute discipline used for an MCP tool JSON Schema.
Validate the file before Claude Code loads it
First, check syntax with the JSON Validator. That catches trailing commas, unquoted keys, and malformed nesting. Then validate the shape your team expects. A JSON file can be valid and still be a broken MCP configuration, for example when a remote entry has a URL without a transport type.
import { z } from "zod";
const RemoteServer = z.object({
type: z.enum(["http", "sse", "ws"]),
url: z.string().url(),
headers: z.record(z.string()).optional(),
});
const StdioServer = z.object({
command: z.string().min(1),
args: z.array(z.string()).default([]),
env: z.record(z.string()).optional(),
});
const McpConfig = z.object({
mcpServers: z.record(z.union([RemoteServer, StdioServer])),
});
export function validateMcpConfig(raw: unknown) {
return McpConfig.safeParse(raw);
}This is a starting point, not a universal schema. Add an allowlist for server names, limit which domains are acceptable, and require HTTPS for remote endpoints in a production team. Use the JSON Schema Generator or JSON to Zod to accelerate a first draft, then review every generated constraint.
Validation does not grant trust
A schema tells you that a file has the expected shape. It does not prove the endpoint is safe, the server is authorized, or its tools should be approved. Review the server owner, scopes, and side effects separately, then confirm its state with claude mcp list or /mcp.
Review checklist and common failures
The official settings guide distinguishes shared project settings from a local settings file. Treat that distinction as a code-review question: would every developer need this exact server definition, and can it run without a secret baked into the repository? If either answer is no, it belongs outside the committed project configuration.
| Configuration kind | Minimum fields | Review question |
|---|---|---|
| Remote server | type plus url | A URL without a type is skipped as an invalid stdio entry. |
| Local stdio server | command and optional args | Keep executable paths and arguments reviewable. |
| Authentication | environment variable reference | Do not commit bearer tokens or API keys. |
| Project file | mcpServers object | Every shared server should have a clear owner and purpose. |
- Server is not found: check the chosen scope and run the session from the expected repository root.
- A remote entry is skipped: add an explicit
typethat matches the endpoint transport. - A project server is pending: review it interactively; project scope does not silently approve a server for every clone.
- A variable is literal text: check the environment variable name and provide a safe default only where one makes sense.
- A config change seems ignored: inspect
/statusfor loaded sources, then confirm the JSON is valid.
A safe handoff sequence
- 1. Decide whether the server is local, project, or user scoped.
- 2. Write the smallest server entry with an explicit transport or command.
- 3. Keep secrets in environment variables, never in a committed JSON file.
- 4. Validate JSON syntax and the expected server shape before opening a session.
- 5. Review and approve the server in Claude Code, then verify its status.
Frequently asked questions
Where should a team put a shared Claude Code MCP server?
Put a shareable, secret-free definition in .mcp.json at the repository root. Use project scope only when every collaborator should see the server and the server configuration is safe to review in Git.
Does .mcp.json replace Claude Code settings.json?
No. Settings files configure Claude Code behavior such as permissions and hooks. .mcp.json is for project-scoped MCP server definitions. Both can exist in the same repository, but they solve different configuration problems.
Can an MCP configuration include an API token?
It can technically include a header, but a shared configuration should reference an environment variable rather than contain the token. This limits accidental exposure in Git history, review tools, logs, and copied examples.
Why use a JSON validator when Claude Code parses the file?
A validator gives fast, line-level feedback before a Claude Code session starts. It also separates JSON syntax failures from configuration-shape failures, which makes a broken file faster to diagnose in a team review.
What is the difference between HTTP and stdio MCP servers?
An HTTP server is a remote endpoint Claude Code connects to over the network. A stdio server is a local process Claude Code starts and communicates with through standard input and output. The security and deployment review for each is different.
Try These Tools
JSON Validator
Catch invalid JSON before Claude Code tries to read the configuration.
Open toolJSON Schema Generator
Draft a schema from a representative MCP server entry, then tighten it by hand.
Open toolJSON to Zod Schema
Turn a known-good configuration sample into a TypeScript validation starting point.
Open toolJSON Formatter
Format a configuration file before review so changed fields and nesting are easy to inspect.
Open tool