MCP configuration is the difference between a Claude Code setup that quietly stalls on a broken server and one that runs dozens of tools across local processes, remote HTTPS endpoints, and enterprise allowlists without friction. This guide goes past the install step and into the transport, scope, authentication, and tool-search decisions that make a fleet of servers actually behave. You will learn how each config key is evaluated, how OAuth flows resolve, and how to lock servers down for a whole team from a single managed file.

MCP configuration: What You’ll Learn
This is the advanced companion to the Claude Code MCP Servers setup guide. Where that post covers first-time install, this one assumes you already have servers running and now need to reason about MCP configuration at scale. You will leave knowing how transports interact with scopes, how the tool-search deferral layer protects your context window, and how a managed policy file overrides everything else on the machine.
If you have read the Claude Code Extensibility Guide, treat this as the deep cut on the MCP layer of that stack. Every flag, key, and behavior below was checked against the official Model Context Protocol reference at code.claude.com.
The Four Transports: Picking the Right Channel
Every MCP server entry is bound to a transport, and the transport dictates latency model, auth story, and reconnection behavior. There are four, not three, and conflating them is the single most common misconfiguration in the wild.
stdio: the local default
When you omit the type key entirely, Claude Code spawns the server as a local child process and speaks JSON-RPC over its standard streams. This is the fastest path for anything that ships as an npm package or a binary on disk. Claude injects the CLAUDE_PROJECT_DIR environment variable so the server can locate the workspace it was launched from, which is how tools like the filesystem server scope themselves.
stdio servers are not auto-reconnected. If the child process exits, the server shows as failed in the /mcp panel and stays that way until you reconnect manually or restart the session. Plan your long-running workflows around that fact.
HTTP: the recommended remote transport
Remote servers should use "type": "http", internally the Streamable HTTP transport. It supports OAuth, custom headers, and a robust reconnection strategy with exponential backoff. The docs are explicit that HTTP is preferred over the legacy SSE variant, so treat HTTP as the default for anything that lives behind a URL.
SSE: deprecated, avoid for new servers
Server-Sent Events ("type": "sse") is marked deprecated in the current reference. The official guidance reads, in plain terms, use HTTP servers instead where available. You may still encounter SSE endpoints on older infrastructure, and Claude Code will connect to them, but do not author a new .mcp.json entry against SSE when an HTTP equivalent exists.
WebSocket: new, config-only
The "type": "ws" transport is new in 2026 and opens a persistent bidirectional socket. Two hard limits to remember: it has no OAuth support, and there is no --transport ws flag on claude mcp add. WebSocket servers are config-file only today. Use them for low-latency push channels where you control auth out of band.
The silent type error
The most insidious config bug is a remote entry written with a url field but no type. Claude Code reads the absence as stdio, tries to spawn a process, and fails with a message like MCP server "<name>" has a "url" but no "type". Always pair url with "type": "http" in the same object.
The Three Scopes: Where Configuration Lives
Scope controls who sees a server. The names changed since older blog posts, so verify you are reading current docs before copying examples. There are exactly three scopes, evaluated in a strict precedence order.
Local scope (the default)
local is the default when you run claude mcp add without --scope. The server is registered for the current project only and is not shared. Claude stores it inside ~/.claude.json under a path-keyed block for that specific project. This is the right scope for personal scratch servers and experiments.
Project scope (shared via VCS)
project writes a .mcp.json file at the repository root, which you commit and your teammates inherit on clone. Because a committed file can ship arbitrary commands, Claude Code requires interactive approval before a project-scoped server goes live. The approval is per-user and is not itself version controlled.
As of v2.1.196, workspace trust blocks a freshly cloned repo from self-approving its own enableAllProjectMcpServers setting. A server declared in a cloned .mcp.json lands in Pending approval state until a human confirms it in the /mcp panel. To clear all prior approvals for a project and start over, run claude mcp reset-project-choices.
User scope (all projects, not shared)
user makes a server available in every project on the machine, but unlike project it is not shared through version control. It lives in ~/.claude.json under a top-level mcpServers key. Reach for user scope for cross-cutting utilities such as a personal search or notebook server.
Precedence and the winner-takes-all rule
Precedence is local, then project, then user, then plugin, then a claude.ai connector. The rule that surprises people is that the entire entry from the highest-precedence source wins. Claude does not merge fields across scopes. If you define github at user scope and again at project scope, the project entry replaces the user one wholesale for that project, with no field-level fallback.
This matters most for MCP configuration teams share across environments. Imagine a user-scoped Sentry server configured with one set of OAuth scopes, and a project-scoped Sentry entry that omits the scopes key entirely. A developer might expect the project entry to inherit the scopes from user scope and merely override the URL. It does not. The project entry wins whole, the scopes vanish, and the OAuth flow prompts from scratch. The fix is to make each scope-level entry self-contained, carrying every key it needs to function on its own.
The --scope flag, aliased as -s, accepts local, project, or user and defaults to local. Verify where a server actually landed with claude mcp get <name>, which prints the resolved scope alongside the config. This is the single fastest way to debug why a server is visible in one project but not another.
The .mcp.json File in Depth
The project-scoped config file is a plain JSON object keyed by server name. Each value is one server definition. Getting the schema right is the whole game for shared, reviewable MCP configuration.
A complete project file
A representative .mcp.json mixing all four transports and several auth styles:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
},
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer ghp_YOUR_PAT"
}
},
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
},
"legacy-sse": {
"type": "sse",
"url": "https://internal.example.com/events"
},
"realtime-ws": {
"type": "ws",
"url": "wss://push.example.com/mcp"
}
}
}
Notice that filesystem and playwright omit type and are therefore stdio. The github entry pins type to http and passes a bearer token inline. The SSE and WebSocket entries are shown for completeness; prefer HTTP for new remote work.
Never hard-code secrets
Putting a PAT directly in a committed .mcp.json leaks it into version history the moment you push. Move secrets into environment variables and reference them indirectly, or restrict the sensitive server to local or user scope so the file never leaves the machine. The headersHelper pattern covered below is the robust answer for short-lived tokens.
CLI Mastery: Beyond claude mcp add
The claude mcp command group is the command-line interface to the same config the /mcp panel exposes interactively. Knowing the full subcommand set saves round-trips into the UI.
The subcommand reference
claude mcp add # interactive add (stdio default)
claude mcp add-json # add from a raw JSON string
claude mcp add-from-claude-desktop # import from Claude Desktop (macOS/WSL only)
claude mcp list # list all configured servers + status
claude mcp get <name> # show one server's resolved config
claude mcp remove <name> # delete a server entry
claude mcp serve # run Claude Code itself as an MCP server
claude mcp login <name> # start OAuth flow for a server (v2.1.186+)
claude mcp logout <name> # revoke stored OAuth tokens
claude mcp reset-project-choices # clear all project-scope approvals
The login and logout subcommands arrived in v2.1.186. Over SSH where no browser can open, pair login with --no-browser to get a URL you can paste into a local browser and bring the code back.
add-json for non-interactive setups
Scripts and provisioning tools should prefer add-json because it takes a complete server definition as one string and skips prompts entirely. This is how you bootstrap a fleet of servers from a manifest without touching the TUI:
claude mcp add-json --scope user github
'{"type":"http","url":"https://api.githubcopilot.com/mcp/","headers":{"Authorization":"Bearer $GH_PAT"}}'
Verified real-world servers
These one-liners are confirmed against the vendors’ published endpoints and make useful smoke tests as you read the rest of this guide:
# GitHub (remote, HTTP, bearer header)
claude mcp add --transport http github https://api.githubcopilot.com/mcp/
--header "Authorization: Bearer ghp_YOUR_PAT"
# Sentry (remote, HTTP, OAuth on first call)
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp
# Notion (remote, HTTP, OAuth on first call)
claude mcp add --transport http notion https://mcp.notion.com/mcp
# dbhub for PostgreSQL (local stdio)
claude mcp add --transport stdio db -- npx -y @bytebase/dbhub --dsn "postgresql://user:pass@host/db"
Stripe, PayPal, and HubSpot publish analogous endpoints at https://mcp.stripe.com, https://mcp.paypal.com/mcp, and https://mcp.hubspot.com/anthropic. All are HTTP and trigger OAuth on first use.
The /mcp Command and Live Management
Once servers are declared, the /mcp slash command is your live cockpit. Run it bare for an interactive panel, or pass a subcommand for non-interactive control.
Bare /mcp
The no-argument form opens a server list with per-server status, tool count, and actions: connect, disconnect, authenticate, and clear-auth. Status icons are Connected, Needs authentication, Failed to connect, and Pending approval (the last is what an unapproved cloned project server shows).
Subcommands for scripting
/mcp reconnect <server> # reconnect one server
/mcp enable <server> # enable one server
/mcp enable all # enable every configured server
/mcp disable <server> # disable one server
/mcp disable all # disable every server
In headless mode with -p, /mcp prints a plain-text summary instead of opening the panel. That is the right shape for CI checks, and it pairs naturally with the patterns in the Claude Code headless mode guide.
OAuth, Headers, and Enterprise Authentication
Remote servers usually need credentials. Claude Code supports a layered auth model that scales from a single static header all the way to Kerberos-style short-lived tokens refreshed by an external helper.
Automatic OAuth flow
OAuth is triggered automatically when a server returns 401 or 403 with a WWW-Authenticate header. Discovery follows RFC 9728 Protected Resource Metadata first, falling back to RFC 8414. If you omit --client-id, Claude performs Dynamic Client Registration, minting a client on the fly. Provide --client-secret on the command line or through the MCP_CLIENT_SECRET environment variable when the server requires a pre-registered secret.
Two convenience flags matter for headless and remote-box use: --callback-port <port> pins the local callback port, and --no-browser prints the authorization URL instead of opening one, which is essential over SSH.
OAuth keys inside .mcp.json
For declarative configuration, the OAuth flow is shaped by two keys under the server entry. oauth.authServerMetadataUrl points at a metadata document and must use the https:// scheme. oauth.scopes is a space-separated list per RFC 6749 section 3.3; Claude appends offline_access when the auth server advertises it in scopes_supported, so refresh tokens are issued where supported.
{
"mcpServers": {
"linear": {
"type": "http",
"url": "https://mcp.linear.app/mcp",
"oauth": {
"authServerMetadataUrl": "https://mcp.linear.app/.well-known/oauth-protected-resource",
"scopes": "read write"
}
}
}
}
The headersHelper pattern for SSO
Static headers expire. For Kerberos, SSO, or any scheme that mints short-lived bearer tokens, set headersHelper to a command. Claude runs it at connect time, parses stdout as JSON, and merges the keys into the request headers. The helper gets a ten-second budget.
Inside the helper, two environment variables are available: CLAUDE_CODE_MCP_SERVER_NAME and CLAUDE_CODE_MCP_SERVER_URL, so one script can serve many servers. From v2.1.193 onward, if the server later returns 401 or 403, Claude re-runs the helper, reconnects, and retries the call exactly once before surfacing an error.
#!/usr/bin/env bash
# ~/bin/k8s-token.sh — mint a fresh Kubernetes Service Account token
TOKEN=$(kubectl create token default --duration=5m)
printf '{"Authorization":"Bearer %s"}' "$TOKEN"
Tool Search and Context Budgeting
Tool definitions are expensive. A busy MCP fleet can ship hundreds of tools, and loading every schema at startup burns context before you have typed a single prompt. Tool search is the 2026 feature that solves this, and it is on by default.
How deferral works
With tool search active, Claude loads only tool names plus server instructions at startup. Full schemas are pulled on demand as the model decides it may need them. Tool descriptions and server instructions are each truncated at 2 KB to bound even the eager portion. The net effect is that adding a tenth server no longer costs you a tenth of your context budget up front.
The ENABLE_TOOL_SEARCH dial
# always defer (default behavior)
export ENABLE_TOOL_SEARCH=true
# load eagerly if deferral would save less than 10% of context
export ENABLE_TOOL_SEARCH=auto
# custom threshold: load if it would save less than 5%
export ENABLE_TOOL_SEARCH=auto:5
# turn deferral off entirely (load all tools up front)
export ENABLE_TOOL_SEARCH=false
The auto modes are the sweet spot for most users. They defer when it helps and skip the round-trip when the fleet is small enough that eager loading is cheaper.
Pinning always-loaded tools
Some tools must always be present, deferral or not. Pin them at the server level with "alwaysLoad": true, or per tool via the _meta["anthropic/alwaysLoad"]: true annotation inside the tool’s own definition. Use this for critical low-latency utilities such as a search tool the model should never have to discover.
The tradeoff is real. Every always-loaded tool claims a slice of context at startup, so a generous pinning policy erodes the very savings tool search was designed to deliver. A practical rule for MCP configuration at scale: pin only tools the model calls in the first turn of a typical session, such as a workspace-wide search or a status-check utility. Leave everything else deferred and let the model pull schemas on demand.
When a deferred tool is needed, the model issues a lightweight search request that returns matching tool schemas. This round-trip adds latency to the first call that touches a given server, but subsequent calls within the same session hit the already-loaded definitions. For interactive sessions this cost is invisible; for MCP configuration in headless pipelines where every millisecond matters, consider setting ENABLE_TOOL_SEARCH=false and loading everything eagerly if the fleet is small enough to fit comfortably in context.
Roots, Resources, Sampling, and Elicitation
Beyond tool calls, MCP defines four advanced capabilities that Claude Code implements on the client side. Understanding which are automatic and which need opt-in keeps your configuration honest.
Roots
A server that implements roots/list learns about the directories Claude Code is working in. Claude answers with the launch directory plus anything added with --add-dir, and since v2.1.203 it pushes notifications/roots/list_changed when that set changes. The CLAUDE_PROJECT_DIR value is always a stable root a server can anchor to.
Resources
Resources are server-provided data the model can cite by URI. Reference one inline as @server:protocol://resource/path and Claude offers autocomplete when you type the leading @. If a server exposes resources but no explicit list or read tools, Claude auto-provides generic list_resources and read_resource tools so the data is reachable.
Sampling
Sampling lets a server ask Claude to generate text on its behalf. Claude Code advertises sampling: {} in the initialize handshake, and the server invokes sampling/createMessage when it needs model output. There is no user-facing configuration flag for sampling; it is always available to any server that asks.
Elicitation
New in 2026, elicitation lets a server request structured input mid-task through a dialog. The server describes the fields it needs, Claude surfaces a prompt, and the user’s answer is returned as typed data. For automated pipelines you can short-circuit the dialog with an Elicitation hook, which pairs with the techniques in the Claude Code Hooks guide.
Channels
A server can declare the claude/channel capability and push messages directly into the session, outside the normal tool-call cycle. Channels are opt-in: pass --channels to allow them for a session. This is the substrate for live status feeds and long-running build notifications.
Server-Author _meta Annotations
Server authors can embed hints in the _meta object that change how Claude Code treats their output. Three annotations matter in practice.
anthropic/maxResultSizeChars: raises the per-result output ceiling up to a hard cap of 500,000 characters. The default is measured in tokens, roughly 25 K, which is small for log-dumping tools.anthropic/requiresUserInteraction: forces a permission prompt on every call, even when the session runs inbypassPermissionsmode. Useful for destructive financial or infrastructure tools (v2.1.199+).anthropic/alwaysLoad: per-tool equivalent of the server-levelalwaysLoadflag, exempting one specific tool from search deferral.
As a consumer, you do not set these; you choose servers whose authors have set them sensibly. As a server author, they are the levers that keep your tool both capable and safe.
Timeouts, Limits, and Reconnection
MCP configuration includes a family of timeouts and limits that protect the session from runaway servers. Most are environment variables so they can be tuned per-machine without touching the config file.
Connection and call timeouts
MCP_TIMEOUT bounds server startup. MCP_TOOL_TIMEOUT is the default per-call limit applied to every tool. A per-server "timeout" field in the config overrides the per-call default with a hard ceiling expressed in milliseconds; values below 1000 are silently ignored to prevent pathological misconfiguration. MAX_MCP_OUTPUT_TOKENS raises the 25 K default output ceiling and moves the 10 K warning threshold accordingly.
{
"mcpServers": {
"slow-db": {
"type": "http",
"url": "https://db.internal/mcp",
"timeout": 30000
}
}
}
Idle timeouts by transport
CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT sets how long a tool connection can sit idle before Claude tears it down. Defaults are transport-specific: 5 minutes for HTTP, SSE, and WebSocket; 30 minutes for stdio. Set it to 0 to disable idle teardown entirely, which suits long-lived channels.
Reconnection behavior
HTTP and SSE servers disconnect with an exponential backoff: five attempts starting at one second and doubling each time. After the fifth failure the server flips to Failed and waits for a manual retry through /mcp reconnect. The initial connection, since v2.1.121, retries three times on transient errors such as 5xx responses, connection refused, or timeouts. Authentication failures and 404s are not retried, because retrying a bad credential is pointless. stdio servers are never auto-reconnected.
Permission Rules for MCP Tools
Every MCP tool is exposed to the permission system under a predictable name, which means your settings.json permission rules apply to them directly. The naming format is mcp__<server>__<tool>, with double underscores separating the segments.
Allow and deny patterns
A glob "mcp__*" in the deny list blocks every MCP tool in one stroke, which is the right move for locked-down environments. A more surgical rule like "mcp__github__*" blocks an entire server, while "mcp__github__create_issue" targets one tool.
Permission modes and the MCP exception
The acceptEdits permission mode auto-approves file edits but does not auto-approve MCP tools; they still prompt. The bypassPermissions mode does auto-approve MCP calls, but it also disables every other safety check, so reserve it for ephemeral sandboxes. Server authors who want a prompt regardless can set anthropic/requiresUserInteraction: true in their tool’s _meta.
Enterprise Managed MCP
For fleet-wide control, Claude Code reads a managed configuration file that overrides individual user and project settings. The file location depends on the operating system: /etc/claude-code/ on Linux, /Library/Application Support/ClaudeCode/ on macOS, and C:Program FilesClaudeCode on Windows.
The policy file
{
"allowManagedMcpServersOnly": true,
"allowedMcpServers": [
"https://api.githubcopilot.com/mcp/",
"https://mcp.sentry.dev/mcp"
],
"deniedMcpServers": [
"https://mcp.evil.example.com/mcp"
],
"disableClaudeAiConnectors": true,
"allowAllClaudeAiMcps": false
}
Evaluation order
The deny list always wins. If a server appears in both lists, it is denied. The allow list is evaluated against the server URL for remote servers and against the server command string for stdio servers, so you can gate both transports precisely. disableClaudeAiConnectors set to true blocks the claude.ai connector layer entirely, which is how an enterprise disables consumer integrations while keeping vetted internal servers.
Designing an allowlist for MCP configuration at enterprise scale requires matching the exact string Claude compares against. For a remote HTTP server declared with "url": "https://api.githubcopilot.com/mcp/", the allowlist entry must contain that full URL. Trailing slashes matter. For a stdio server declared with "command": "npx" and "args": ["-y", "@bytebase/dbhub", "--dsn", "..."], the evaluated string is the full command line, so the allowlist entry should match the command prefix. When in doubt, check the resolved string with claude mcp get before adding it to the policy file.
Debugging and the MCP Inspector
When a server misbehaves, the debugging path is layered. Start inside Claude Code and escalate outward only when needed. Note carefully: older tutorials reference a debug flag that does not appear in the current CLI reference. It showed up once in a v0.2.31 changelog and has since been removed from the documented interface; do not rely on any flag you cannot find in claude --help today.
In-session tools
The /mcp panel is the first stop. It shows per-server status, surfaces connection errors verbatim, and offers a reconnect action. For broader tracing, claude --debug turns on general verbose logging, /debug <description> attaches context to a session for support, claude --debug-file <path> writes the trace to disk, and claude doctor runs an environment health check. claude mcp list and claude mcp get <name> confirm the resolved configuration from the command line.
The external MCP Inspector
When you suspect the server itself rather than Claude’s client, run the standalone MCP Inspector against the server in isolation. For a stdio server, point the inspector at the same command Claude would run; for a remote server, pass the URL.
# Inspect a stdio server in isolation
npx @modelcontextprotocol/inspector node dist/index.js
# Inspect a remote HTTP/SSE server
npx @modelcontextprotocol/inspector --url https://mcp.example.com/sse
If the inspector reproduces the failure, the bug is in the server. If only Claude Code fails, the bug is in your client-side configuration, and the /mcp error message is your lead.
Claude Code as an MCP Server
The claude mcp serve subcommand turns Claude Code itself into a stdio MCP server, exposing its tools to other MCP-aware clients. This is the bridge for editors, custom dashboards, or orchestration scripts that want Claude’s capabilities without reimplementing them. Because it speaks stdio, the consumer treats it exactly like any other local MCP server, and all the transport and timeout guidance above applies.
Pairing claude mcp serve with a plugin that bundles curated MCP servers is how you ship a reusable capability package. The Claude Code Plugins guide covers that packaging layer in depth.
A Worked Example
Put the pieces together with a realistic scenario. Imagine a small platform team that wants every engineer to have GitHub, Sentry, and an internal Kubernetes API available in Claude Code, with credentials that rotate hourly and a hard policy that no server outside an approved list may connect.
The team starts at the repository. A senior engineer authors a .mcp.json at the repo root declaring GitHub and Sentry at project scope. Both are HTTP servers using OAuth, so the file carries no secrets, only URLs and the oauth.scopes each needs. She commits the file and opens a pull request so the addition is reviewed like any other infrastructure change.
The internal Kubernetes server is trickier. Its tokens live in a corporate identity provider and expire after five minutes, so a static header would rot almost immediately. Instead, the engineer writes a small shell script that calls kubectl create token and prints a JSON header object, then registers the server with headersHelper pointing at that script. On every connect, and again on any 401, Claude re-runs the script and gets a fresh token, all without a human in the loop.
Because the Kube script depends on the engineer’s local kubeconfig, that server does not belong in the shared .mcp.json. The engineer adds it at user scope with claude mcp add --scope user so it lives in her ~/.claude.json and never touches version control. The shared file stays clean and portable.
Next, the security team steps in. They publish a managed policy file to /etc/claude-code/managed-mcp.json via the standard configuration-management pipeline. The file sets allowManagedMcpServersOnly to true and lists the two approved remote URLs plus the Kube server’s command prefix in allowedMcpServers. It also sets disableClaudeAiConnectors to true so no one can bypass the policy with a consumer claude.ai connector. The deny list stays empty today, but the team knows the deny list always wins if they ever need to block a specific server fast.
The first time an engineer pulls the repo, the GitHub and Sentry servers land in Pending approval. Workspace trust, active since v2.1.196, prevents the cloned .mcp.json from self-approving. She opens /mcp, reads the two pending entries, and approves them interactively. From that moment they are Connected. Her Kube server, registered at user scope, connects immediately and its helper script mints the first token.
Tool search is left at its auto default. With three servers the fleet is small enough that deferral saves only a sliver of context, so Claude loads most tools eagerly and skips the round-trips. As the team grows the fleet to a dozen servers, the same default quietly starts deferring, protecting the context budget without any configuration change.
The engineering lead also reviews the MCP configuration for timeout safety. The default MCP_TOOL_TIMEOUT is generous for interactive use but could mask a hung tool in CI. She sets MCP_TIMEOUT in the project’s environment to a tight 15 seconds for startup and leaves MCP_TOOL_TIMEOUT at its default for calls, since the Kube server occasionally needs a few seconds to mint a token. The per-server "timeout" field on the Sentry entry is set to 30 seconds because Sentry’s issue-search endpoint can be slow on large projects.
A week later an engineer reports that a Sentry call hangs. The team reproduces it with the external MCP Inspector pointed at the Sentry URL; the inspector returns quickly, so the server is healthy. Back inside Claude Code, claude mcp get sentry reveals that an old experiment left a per-server timeout of 500 milliseconds, far below the one-second floor below which values are silently ignored. They correct the entry, flush the config, and the hang clears. The lesson: always verify resolved MCP configuration with claude mcp get, because stale values from prior experiments are the most common silent failure.
Finally, the platform team wraps the whole stack, Kube helper included, as a plugin so a new hire can install one package instead of following a runbook. That plugin is itself an MCP bundle, which is the natural unit of reuse and is covered fully in the extensibility hub. The result is a fleet that is shared where it should be, personal where it must be, and governed everywhere.
MCP configuration: Common Mistakes to Avoid
Even careful engineers repeat a handful of MCP configuration errors. Each one below is verified against current behavior, not the folklore in older forum posts.
- Omitting
typeon a remote server. An entry withurlbut notypereads as stdio and fails with a confusing spawn error. Always pair everyurlwith"type": "http","sse", or"ws". - Committing secrets to
.mcp.json. A bearer token in a project-scoped file lands in version history and in every clone. Use OAuth, environment variables, orheadersHelper, and keep secret-bearing servers at local or user scope. - Recommending SSE for new servers. SSE is deprecated. New remote servers should use HTTP. Keep SSE only for legacy endpoints you cannot yet migrate.
- Expecting scope fields to merge. The highest-precedence scope wins wholesale; fields are not combined. A project entry does not inherit missing keys from a user entry, it replaces it entirely.
MCP configuration: Best Practices
- Default to HTTP for remote servers. It carries OAuth, custom headers, and a robust reconnection strategy. Reserve SSE for legacy and stdio for local processes.
- Share through project scope, keep secrets local. Put URLs and non-sensitive config in
.mcp.json, and register anything with credentials at local or user scope. - Leave tool search on
auto. It defers when the fleet is large and loads eagerly when it is small, protecting your context budget with zero tuning. - Pin critical tools with
alwaysLoad. Exempt must-have utilities from deferral so they are available instantly without a discovery round-trip. - Govern with a managed policy file. Use
allowManagedMcpServersOnlyand a deny list so fleet policy is enforceable and deny always wins over allow.
MCP configuration: Frequently Asked Questions
What is the difference between local and user scope?
Local scope is tied to one project and stored under that project’s path in ~/.claude.json. User scope applies to every project on the machine, under a top-level mcpServers key. Only project scope is shared through version control.
Which transport should I use for a new remote server?
Use HTTP, declared as "type": "http". It supports OAuth, custom headers, and automatic reconnection with exponential backoff. SSE is deprecated, and WebSocket lacks OAuth and has no CLI add flag, so reserve it for specialized low-latency channels.
How does tool search protect my context budget?
Tool search defers loading full tool schemas until the model needs them, shipping only names and truncated instructions at startup. Controlled by ENABLE_TOOL_SEARCH, it defaults to on and can be set to auto, auto:N, or false to tune the behavior.
Can a server refresh short-lived tokens automatically?
Yes. Set headersHelper to a command that prints a JSON header object. Claude runs it at connect time and, on any 401 or 403, reconnects and retries once before failing. The helper gets a ten-second budget and two environment variables.
How do I lock down MCP for an entire team?
Publish a managed policy file with allowManagedMcpServersOnly set to true and an allowedMcpServers list. The deny list always wins over the allow list, and disableClaudeAiConnectors blocks consumer claude.ai connectors entirely. Evaluation matches server URL or command string.
MCP configuration is the discipline of matching transport to workload, scope to audience, and policy to risk. Remember the four transports and their tradeoffs, the three scopes and their winner-takes-all precedence, the tool-search layer that protects your context, and the managed file that lets an enterprise say a final, enforceable no.