Claude Code MCP Servers: The Production Setup Guide
Add HTTP and stdio MCP servers to Claude Code, pick the right scope for your team, pin OAuth scopes, and set the timeouts production needs.

One claude mcp add command connects Claude Code to any MCP server, but the setup that survives a team is the one that gets scope, approval, and timeouts right on day one. Local scope is the default and stays private to you; .mcp.json at project scope is what your team actually shares. Here is the full setup, and the settings that decide whether it holds up in production.
The three commands that cover 90% of setups
Use HTTP transport for anything remote, stdio for anything local, and skip SSE entirely: it is deprecated in favor of HTTP.
# Remote HTTP server (the recommended transport for cloud services)
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp
# Remote server with a static auth header
claude mcp add --transport http github https://api.githubcopilot.com/mcp/ \
--header "Authorization: Bearer YOUR_GITHUB_PAT"
# Local stdio server (a process on your machine)
claude mcp add --env AIRTABLE_API_KEY=YOUR_KEY --transport stdio airtable \
-- npx -y airtable-mcp-serverTwo details trip up most first attempts. For stdio servers, the -- separates Claude Code's own flags from the command that runs the server; everything after it is passed through untouched. And in JSON configs, an entry with a url but no type field is read as a stdio server and skipped with an error, so always set "type": "http" explicitly (the spec's streamable-http also works as an alias).
There is a fourth transport, WebSocket, for servers that push events to the client. Configure it via claude mcp add-json with "type": "ws", but know its limits: auth is header-only, no OAuth, and the --transport flag does not accept it. When your server only responds to requests, HTTP is the right call.
Verify the connection before you build on it:
claude mcp list # all servers with connection status
claude mcp get sentry # one server's config and stateInside a session, /mcp shows each connected server with its tool count, and flags servers that advertise tools but expose none.
Scopes: where the config lives decides who gets it
Scope is the first real decision, because it controls whether your teammates get the server or only you do.
The default is local, which means the server you just added is invisible to everyone else and to your other projects. That is correct for a personal experiment and wrong for the issue-tracker server your whole team needs, so most "it works on my machine but not on the new hire's" reports are a scope mistake, not a bug.
When the same server name is defined at more than one scope, Claude Code connects once, using the highest-precedence definition: local beats project, project beats user, and user beats plugin-provided servers and claude.ai connectors. The winning entry is used whole; fields are never merged across scopes. That precedence is a feature: an engineer can point the team's postgres server at a staging database by redefining it at local scope, without touching the shared file.
The team rollout: .mcp.json in version control
Project scope is the team distribution mechanism. One engineer runs the add command with --scope project, commits the resulting .mcp.json, and every teammate gets the same server on their next pull:
claude mcp add --transport http paypal --scope project https://mcp.paypal.com/mcpKeep secrets out of the file with environment variable expansion, which works in command, args, env, url, and headers:
{
"mcpServers": {
"internal-api": {
"type": "http",
"url": "${API_BASE_URL:-https://api.example.com}/mcp",
"headers": { "Authorization": "Bearer ${INTERNAL_API_KEY}" }
}
}
}${VAR} expands from each engineer's environment and ${VAR:-default} supplies a fallback. If a required variable is unset with no default, Claude Code fails to parse the config, which is the failure mode you want: loud at startup, not silent at tool-call time.
Two gates stand between a committed .mcp.json and a running server, and both exist to protect you:
This matters because MCP servers are code execution and data access. A malicious .mcp.json in a cloned repo could otherwise exfiltrate data the moment anyone opened the project, and servers that fetch external content carry prompt injection risk on top. The approval gate is the human checkpoint; do not train your team to click through it. For what belongs on the server side of that boundary, see the MCP security practices that hold up in production.
Auth that passes security review
For OAuth-backed remote servers, add the server first and authenticate second: Claude Code flags any server that answers 401 or 403, and either /mcp in a session or claude mcp login <name> from the shell (v2.1.186+) runs the OAuth flow. Tokens are handled by Claude Code; nothing lands in the repo.
The setting security teams actually care about is oauth.scopes. By default the requested scopes come from what the server advertises. Pin them to an approved subset instead:
{
"mcpServers": {
"crm": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"oauth": { "scopes": "contacts.read deals.read" }
}
}
}The value is a space-separated string, and the pinned set takes precedence over whatever the server's metadata advertises. If a tool call later fails with insufficient_scope, widen the pin deliberately rather than removing it. This is the difference between "the agent can read deals" and "the agent has whatever the vendor's OAuth app asks for."
Three edge cases worth knowing before they cost you an afternoon: servers without Dynamic Client Registration need --client-id (and sometimes --client-secret) from an app you register in the vendor's portal; servers with pre-registered redirect URIs need --callback-port so the callback matches; and for non-OAuth schemes like Kerberos or an internal SSO, headersHelper runs a command of yours at connection time and merges its output into the request headers, which is the supported way to inject short-lived tokens.
The five production settings nobody sets
Defaults are tuned for a laptop demo, not a team working against real systems. Five settings change that.
Set a startup timeout that matches your servers
MCP_TIMEOUTgoverns server startup, e.g.MCP_TIMEOUT=10000 claudeallows ten seconds. Slow-starting stdio servers (a Python process importing half of PyPI) otherwise get marked failed before they finish booting.Cap tool-call time per server
Unset, the tool timeout
MCP_TOOL_TIMEOUTdefaults to roughly 28 hours of wall clock. Set a per-server"timeout"in.mcp.json(milliseconds, minimum 1000) so one hung migration tool cannot stall a session for a day:"timeout": 600000gives ten minutes, hard, and progress notifications do not extend it.Know the idle timeout is already protecting you
A tool call that sends no response and no progress notification aborts after five minutes on HTTP, SSE, and WebSocket servers, and 30 minutes on stdio (v2.1.187+, stdio from v2.1.203). Tune it with
CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUTin milliseconds; long-running server tools must emit progress notifications or they will be cut off.Budget tool output
Claude Code warns at 10,000 tokens of MCP tool output and caps at 25,000 by default. A
SELECT *against a wide table blows through that instantly. Raise it withMAX_MCP_OUTPUT_TOKENS=50000when a server legitimately returns large payloads, but the better fix is a server that paginates.Leave tool search on and write server instructions for it
Tool search is on by default: only tool names and server instructions load at session start, and full definitions are fetched when needed, so there is no fixed cap on how many servers you can attach. The lever you control is the server instructions field, truncated at 2KB, which tells Claude when to search for your tools. If you run through a proxy with a custom
ANTHROPIC_BASE_URL, tool search silently falls back to loading everything upfront, and your context budget becomes the real cap again.

One more constraint that reads like trivia until it bites: the names workspace, claude-in-chrome, computer-use, Claude Preview, and Claude Browser are reserved for built-in servers. A config that uses one is skipped at load time.
Connection failures are handled sanely out of the box: HTTP and SSE servers reconnect with exponential backoff, five attempts starting at one second and doubling, and initial connections retry three times on transient errors (5xx, refused, timeout) but not on auth failures, which need a config fix, not a retry. Stdio servers are local processes and are not auto-reconnected; that is your supervisor's job if the server matters.
Governing servers org-wide
Past a handful of engineers, "everyone adds what they want" stops being a policy. Claude Code has a real governance layer for this: administrators deploy a fixed server set via managed-mcp.json and restrict what users can add with allowedMcpServers and deniedMcpServers in managed settings, with blocked servers surfaced to the user rather than failing silently.
The overlooked back door is claude.ai connectors: servers an engineer added on claude.ai appear in their Claude Code sessions automatically when they authenticate with that account. If your org wants configuration to flow only through reviewed files, set disableClaudeAiConnectors: true in settings. It uses any-source-true semantics, so a policy-level true cannot be re-enabled by a project file, and individual connectors can be blocked by name or URL pattern via deniedMcpServers.
The pattern we recommend for a team rollout, in order: a reviewed .mcp.json per repo for shared servers, pinned OAuth scopes on anything touching customer data, per-server timeouts on anything that writes, and the managed allowlist once more than one team is involved. At that point the question shifts from connecting vendor servers to exposing your own systems, and that is a build decision: the guide to building an MCP server for production internal APIs covers the server side of the same boundary.
What is the difference between local, project, and user scope in Claude Code?
Local and user scopes are private to you and stored in ~/.claude.json; local applies to one project, user to all of them. Project scope lives in .mcp.json at the repo root, is designed to be committed, and is the only scope your teammates inherit. When a name collides, local wins over project, and project over user.
How do I add the GitHub MCP server to Claude Code?
Generate a fine-grained personal access token in GitHub's token settings, then run claude mcp add --transport http github https://api.githubcopilot.com/mcp/ --header "Authorization: Bearer YOUR_PAT". Scope the token to the repositories the agent should reach, not your whole account.
Why is my .mcp.json server stuck on Pending approval?
Project-scoped servers require per-user approval, and a cloned repository cannot approve its own: committed enableAllProjectMcpServers settings are ignored until the workspace is trusted. Run claude interactively in the folder, accept the trust dialog, then approve the server. claude mcp reset-project-choices re-runs the decision.
How many MCP servers can Claude Code handle?
There is no fixed cap. Tool search defers tool definitions until needed, so at session start only names and server instructions load, and the practical limit is your context window budget. The exception is proxy setups where tool search is disabled and every schema loads upfront; there, each server has a real context cost.
Does Claude Code support SSE MCP servers?
Yes, but the SSE transport is deprecated. Use HTTP for new remote servers; it is the most widely supported transport and the only one with full OAuth support.
Connecting vendor servers is a config exercise. The value shows up when Claude Code can reach your own systems, your internal APIs, your data, your runbooks, behind typed tools with auth, approvals, and logs. That server is the part you build.
Build an MCP Server
We design and ship the MCP server that exposes your internal systems to Claude Code and other agents, with OAuth, approval gates, and production logging built in.
Jul 12, 2026






