Connecting MCP Servers to Claude Code: Setup Guide That Works
Claude Code MCP setup comes down to one command: claude mcp add, run with the right transport and scope flags. Verify the connection with claude mcp list or the /mcp command inside a session, and the server's tools are available to your next prompt. Everything that goes wrong lives in the details around that command: three scopes that store config in three different places, two transports with different flags, an OAuth flow hidden behind a slash command, and a Windows quirk that kills servers with a silent connection error.
This guide walks the whole path: local stdio servers, remote HTTP servers with OAuth, team-shared .mcp.json files, and the debugging sequence for the failure modes people actually hit. Every command is copy-paste runnable.
What MCP Actually Adds to Claude Code
Out of the box, Claude Code reads files, edits code, runs shell commands, and searches your repo. That covers most coding work. What it cannot do natively is reach the systems around your code: GitHub issues, Sentry errors, a live browser, your database schema, internal APIs.
The Model Context Protocol (MCP) is the standard plug for that gap. An MCP server is a small program, local or remote, that exposes three kinds of things:
- Tools: functions Claude can call, like
create_issue,query_database, orbrowser_click - Resources: data you can pull into context with @ mentions, like a specific issue or a doc page
- Prompts: prebuilt prompt templates that appear as slash commands
Once a server is connected, its tools show up in Claude's toolset automatically. Ask "check the failing CI run and fix the test" with the GitHub server connected, and Claude fetches the run logs itself instead of asking you to paste them. The value is less copy-pasting and fewer context-free guesses.
One mental-model correction before setup: MCP servers are not plugins running inside Claude Code. Stdio servers are separate processes that Claude Code spawns and talks to over stdin and stdout. Remote servers are HTTP endpoints someone else hosts. This matters for debugging, because most setup failures are process-spawning or network problems, not Claude problems.
Claude Code MCP Setup in Three Commands
The core workflow looks like this:
# add a server (stdio transport, the default)
claude mcp add playwright -- npx @playwright/mcp@latest
# see what is configured and whether it connects
claude mcp list
# remove it
claude mcp remove playwrightThree details in that first command deserve attention.
First, playwright is a name you choose. It becomes the namespace for the server's tools (mcp__playwright__browser_click), so keep it short and lowercase.
Second, everything after -- is the literal command Claude Code will spawn. The -- separator is required whenever the server command has its own flags; without it, the CLI tries to parse -y or --headless as its own options and errors out.
Third, there is no restart flag because there is nothing to restart globally: new sessions pick up the config. If a session is already open, exit and start again, or run /mcp to see the current state.
claude mcp list performs a health check on every configured server and prints a connected or failed status for each. claude mcp get <name> shows the stored config for one server, including which scope it came from. These two commands, plus the /mcp slash command inside the REPL, are your entire visibility surface, so learn them before anything breaks.
Pick the Right Scope Before You Add Anything
Every server lives in exactly one of three scopes, and the wrong choice is the most common source of "it worked yesterday, where did my server go":
- local (the default): private to you, active only in the project directory where you ran the add command. Stored in
~/.claude.jsonunder that project's entry. Right for experiments and servers holding personal credentials. - project: written to a
.mcp.jsonfile at the project root, meant to be committed to git. Everyone who clones the repo gets the same servers. Right for team-standard tooling. - user: available to you across every project on the machine. Also stored in
~/.claude.json, but globally. Right for personal utilities you always want, like a browser automation server.
Select a scope with --scope:
claude mcp add --scope user playwright -- npx @playwright/mcp@latest
claude mcp add --scope project --transport http sentry https://mcp.sentry.dev/mcpWhen two scopes define the same server name, the more specific one wins: local beats project, and project beats user. That precedence lets you override a team server with a personal variant without touching the shared file.
A practical rule: default to local while testing, promote to user once you trust the server and want it everywhere, and use project only when the whole team should run it. Project scope has a safety catch: because .mcp.json arrives via git from other people, Claude Code asks you to approve those servers on first use in each project. If you rejected one and regret it, reset the stored choices:
claude mcp reset-project-choicesAdding Local stdio Servers
Stdio is the default transport and the workhorse for anything running on your machine. The pattern is always claude mcp add <name> -- <command that starts the server>.
Some real examples:
# browser automation via Playwright
claude mcp add playwright -- npx @playwright/mcp@latest
# filesystem access outside the project root
claude mcp add docs -- npx -y @modelcontextprotocol/server-filesystem ~/engineering-docs
# current, version-accurate library documentation
claude mcp add context7 -- npx -y @upstash/context7-mcpServers written in any language work the same way, because stdio only cares that the process speaks MCP on stdin and stdout:
# a Python server
claude mcp add tickets -- python /Users/you/tools/ticket_server.py
# the same server managed by uv
claude mcp add tickets -- uv run --directory /Users/you/tools ticket-serverEnvironment variables go in with --env (or -e), not by exporting them in your shell, because Claude Code spawns the server with its own environment:
claude mcp add my-api \
--env API_KEY=sk-your-key \
--env API_BASE=https://internal.example.com \
-- node /Users/you/tools/mcp-server.jsTwo habits prevent most stdio pain. Pin versions in anything you rely on (@playwright/mcp@0.0.41 style rather than @latest) so a server update does not change tool behavior underneath you. And prefer absolute paths for both the runtime and the script: a server that works in your terminal but fails inside Claude Code is almost always a PATH difference, typically nvm or pyenv shims resolving differently outside your interactive shell. Something like claude mcp add app -- /Users/you/.nvm/versions/node/v22.12.0/bin/node server.js is ugly and reliable.
Adding Remote Servers Over HTTP
Remote servers skip the local process entirely: the vendor hosts the server, and you point Claude Code at a URL. This is now the standard way to connect SaaS tools, and it is the easiest setup in the whole ecosystem because there is nothing to install.
# GitHub's hosted MCP server
claude mcp add --transport http github https://api.githubcopilot.com/mcp/
# Sentry's hosted MCP server
claude mcp add --transport http sentry https://mcp.sentry.dev/mcpMost hosted servers authenticate with OAuth, and the flow lives inside the REPL:
- Start
claude, then run/mcp - Select the server that shows it needs authentication
- Choose Authenticate and finish the consent screen that opens in your browser
- Return to the terminal; the session picks up the token
Claude Code stores and refreshes the token for you; you repeat the dance only when the vendor revokes or expires the grant. The /mcp menu also has a clear-authentication option, which fixes most stuck-auth states.
For internal services that use static bearer tokens instead of OAuth, pass headers at add time:
claude mcp add --transport http internal https://mcp.internal.example.com/mcp \
--header "Authorization: Bearer ${INTERNAL_MCP_TOKEN}"You will still meet the older SSE transport in the wild, since some vendors have not migrated. It works the same way with --transport sse and a URL that usually ends in /sse. Treat SSE as legacy: when a vendor documents both endpoints, take the HTTP one.
The .mcp.json File: Claude Code MCP Setup for Teams
Project scope earns its own section because it is the difference between "works on my machine" and a team-wide standard. When you run claude mcp add --scope project, Claude Code writes a .mcp.json at the repo root. You can also write it by hand:
{
"mcpServers": {
"playwright": {
"type": "stdio",
"command": "npx",
"args": ["@playwright/mcp@latest"]
},
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/"
}
}
}Commit that file and every teammate gets both servers after approving them on first run. Two features make this workable in real teams.
The first is environment variable expansion. .mcp.json supports ${VAR} and ${VAR:-default} syntax in the command, args, env, url, and headers fields, so secrets and machine-specific values stay out of git:
{
"mcpServers": {
"internal": {
"type": "http",
"url": "${MCP_GATEWAY_URL:-https://mcp.example.com/mcp}",
"headers": {
"Authorization": "Bearer ${INTERNAL_MCP_TOKEN}"
}
}
}
}Each developer exports INTERNAL_MCP_TOKEN in their shell profile and the shared file stays clean. Note that the braces are required: $VAR without braces is not expanded.
The second is per-server enablement in settings. In .claude/settings.json (or its local variant), enableAllProjectMcpServers: true auto-approves everything in .mcp.json, while enabledMcpjsonServers and disabledMcpjsonServers act as allow and deny lists by server name. CI containers and devcontainers typically set the blanket approval; on laptops, leaving the interactive prompt on is the safer default.
Two adjacent tricks are worth knowing. claude mcp add-json accepts a raw JSON blob, handy when a server's README gives you a config snippet:
claude mcp add-json my-server '{"type":"stdio","command":"npx","args":["-y","some-mcp-package"]}'And if you already configured servers in Claude Desktop, claude mcp add-from-claude-desktop imports them interactively instead of making you retype everything. Claude Code plugins can also bundle MCP servers, so installing a plugin sometimes registers servers without you touching any of this.
Windows Quirks That Break Everything
On native Windows (not WSL), the single most common failure is adding an npx-based server exactly as the README says and getting a "connection closed" error. The cause: npx on Windows is a .cmd shim, not a real executable, and spawned processes cannot execute it directly. Wrap it in cmd /c:
claude mcp add playwright -- cmd /c npx @playwright/mcp@latestThe same applies to anything .cmd or .bat based. In .mcp.json, the wrapper belongs in the command field:
{
"mcpServers": {
"playwright": {
"type": "stdio",
"command": "cmd",
"args": ["/c", "npx", "@playwright/mcp@latest"]
}
}
}Inside WSL none of this applies, because you are on Linux as far as Claude Code is concerned. Remote HTTP servers are also immune, which is one more argument for preferring them on Windows machines.
Debugging a Claude Code MCP Setup That Fails to Connect
When a server misbehaves, resist the urge to re-add it five different ways. Work through this sequence instead.
Start with claude mcp list. It health-checks every server. A server missing from the list entirely is a scope problem: you added it in a different directory (local scope) or expected a .mcp.json that never got committed. claude mcp get <name> confirms which scope a server came from.
If the server is listed but failing, run the server command by hand. For stdio servers, copy the exact command out of claude mcp get and run it in your terminal. If it crashes there, the problem is the server, not Claude Code: missing runtime, missing package, bad flag. If it runs fine in the terminal but fails in Claude Code, suspect environment differences: PATH, unexported variables, version-manager shims. Absolute paths and explicit --env flags fix nearly all of these.
Next, launch with debug output:
claude --debugDebug mode prints MCP connection attempts, stderr from the spawned process, and the location of the per-server log files where Claude Code records the full protocol traffic. The actual error is almost always sitting in there, and it is usually mundane: an expired token, a typo in a URL, a binary that is not installed.
Slow servers are their own category. Claude Code enforces a startup timeout, and heavyweight servers (a headless browser, a large Python environment) sometimes need more. Both the startup and tool-execution timeouts are tunable through environment variables, in milliseconds:
# give servers 15s to start and tools 5 minutes to run
MCP_TIMEOUT=15000 MCP_TOOL_TIMEOUT=300000 claudeThen there is output size. MCP tool results are capped by MAX_MCP_OUTPUT_TOKENS (the default is 25,000 tokens). A database query or a log fetch that returns megabytes gets truncated. You can raise the cap, but the better fix is asking the server for less: tighter queries, pagination, filters.
Finally, the auth failure loop: a remote server that keeps demanding authentication, or starts returning 401 after weeks of working. Open /mcp, select the server, clear authentication, and authenticate again. If that fails, remove and re-add the server, which discards its stored token entirely.
For anything still broken after all that, MCP Inspector (the official debugging tool from the MCP project) talks to your server directly, outside Claude Code, and shows the raw request and response for each tool call. If Inspector cannot talk to the server either, file the bug with the server maintainer, not against your own setup.
Using the Servers Once They Connect
Setup is half the story; the other half is knowing what you actually got.
Tools appear in Claude's toolset with generated names in the form mcp__<server>__<tool>, so the Playwright server's click tool is mcp__playwright__browser_click. You rarely type these; Claude picks tools by their descriptions. Where the names matter is permissions. In /permissions or settings.json, the rule mcp__playwright allows every tool from that server, and mcp__playwright__browser_click allows exactly one. Wildcards like mcp__playwright__* are not valid syntax; the bare server prefix already means all tools.
Resources come in through @ mentions. Type @ in the prompt and connected servers' resources appear alongside your files, addressed as @server:protocol://path. This is the cleanest way to say "look at this specific ticket" without pasting its contents into the prompt.
Prompts exposed by servers become slash commands, namespaced the same way: /mcp__github__list_prs, with arguments passed after the command. Run /mcp and open a server's details to see which tools, resources, and prompts it ships.
Two operational notes. Every connected server adds its tool definitions to the model's context, so twenty connected servers make every request bigger and tool selection mushier. Keep the always-on roster small and lean on scopes: user scope for the three or four servers you genuinely always want, project scope for team needs, and everything else added when a task calls for it. And when a tool call produces something huge, remember the output cap from the debugging section; prefer prompts that ask servers for filtered data.
Five Servers Worth Adding First
Skip the awesome-lists with five hundred entries. These five cover most engineering workflows, and each is maintained by the vendor whose product it wraps:
- GitHub (remote HTTP, OAuth): issues, PRs, Actions logs, code search. The highest-leverage connection for most teams because it closes the loop of read issue -> write fix -> open PR -> check CI.
- Playwright (stdio): drives a real browser. Ask Claude to load your dev server, click through a flow, and screenshot the result, which upgrades "the change compiles" to "the change works".
- Sentry (remote HTTP, OAuth): production errors with stack traces, straight into context. "Fix the top unresolved issue in project X" becomes a one-line prompt.
- Context7 (stdio): fetches current, version-accurate library documentation, which kills the classic failure of Claude writing code against an API that changed after its training data was collected.
- Chrome DevTools (stdio): performance traces, network inspection, and console access against a real Chrome instance, for the debugging sessions where an action-level browser tool is not enough.
Add each one, run a small end-to-end task with it, and drop whichever ones do not earn their context cost within a week.
Security Notes Before You Wire Up Everything
Three risks are worth naming plainly.
A stdio server is arbitrary code running with your user account's permissions. Installing one from npm deserves the same skepticism as any other dependency: check the publisher, pin the version, and prefer official vendor packages over community wrappers of the same API.
Tool outputs are untrusted input. A GitHub issue body or a scraped web page can contain text engineered to steer the model. Claude Code's permission prompts exist for exactly this, so keep write-capable tools behind prompts rather than blanket-allowing a server's whole surface, and hand servers read-only credentials whenever the workflow allows it.
Secrets belong in environment variables, never in a committed .mcp.json. The ${VAR} expansion syntax exists so the shared file can circulate freely inside the team while each machine holds its own tokens. If a token does land in git history, rotate it; deleting the line is not enough.
A Working Setup, End to End
Here is the whole guide compressed into a five-minute checklist. Add a remote server with claude mcp add --transport http github https://api.githubcopilot.com/mcp/, authenticate through /mcp, and confirm with claude mcp list. Add a local one with claude mcp add playwright -- npx @playwright/mcp@latest, prefixing cmd /c on native Windows. Promote the keepers to --scope user, move team servers into .mcp.json with ${VAR} placeholders for secrets, and when anything fails, run the sequence: claude mcp list, execute the command by hand, claude --debug, read the log. That order resolves nearly every claude code mcp setup problem before it costs you an afternoon.
FAQ
Do I need to restart Claude Code after adding an MCP server?
Config changes are picked up when a session starts, so exit and relaunch after claude mcp add or after editing .mcp.json. Inside a running session, /mcp shows the live connection state and can reconnect a server that dropped mid-session.
Where does Claude Code store MCP configuration?
Local-scoped and user-scoped servers live in ~/.claude.json (per-project entries for local scope, a global section for user scope). Project-scoped servers live in the .mcp.json file at the repo root. There is no separate MCP config file beyond those two.
Why does my server work in the terminal but fail in Claude Code?
Environment differences. Claude Code spawns stdio servers without your interactive shell's full setup, so nvm and pyenv shims, PATH additions from .zshrc, and unexported variables all disappear. Use absolute paths to the runtime and pass variables explicitly with --env.
How do I pass an API key to an MCP server?
For CLI adds, use --env KEY=value on the add command. For .mcp.json, reference shell variables with ${KEY} in the env or headers block so the secret never enters git. For OAuth-based remote servers, skip keys entirely and authenticate through /mcp.
Can Claude Code and Claude Desktop share MCP servers?
They read different config files, but claude mcp add-from-claude-desktop imports your Desktop servers into Claude Code in one interactive step (available on macOS and within WSL). After the import, the two configs evolve independently.
What is the difference between the http and sse transports?
Both are remote transports. Streamable HTTP is the current MCP standard; SSE is the older mechanism kept for backward compatibility. You add them the same way, only the --transport value and the vendor's URL differ. Prefer http whenever the vendor offers it.
How many MCP servers is too many?
There is no hard limit, but every connected server's tool definitions ride along in context on every request, which costs tokens and dilutes tool selection. Keep the always-connected set small, scope situational servers to the projects that need them, and prune anything you have not used in a couple of weeks.
Can Claude Code itself act as an MCP server?
Yes. Running claude mcp serve exposes Claude Code's own tools (Read, Edit, Bash, and the rest) over MCP, which lets other MCP clients, including Claude Desktop, drive it as a coding backend.
AI CodingShip full-stack AI apps at conversation speed — specs, agents, deploys, all from the terminal.
Claude CodeGo from zero to confident with Claude Code, the terminal agent that reads, edits, runs, and verifies real code.