Originally published at heycc.cn. This is a mirrored copy — the canonical version is kept up to date at the source.
How to Set Up an MCP Server in Claude Code (2026 Guide)
By default, Claude Code only knows what is in your repo and what you paste into the chat. The moment you find yourself copying an issue-tracker ticket, a monitoring stack trace, or a database query result into the prompt, you have hit that wall. The Model Context Protocol (MCP) is how you knock it down. MCP is an open-source standard for AI-tool integrations, and an MCP server gives Claude Code direct, structured access to your tools, databases, and APIs instead of working from whatever you manually paste.
A stdio server that runs cleanly on macOS and fails on a teammate's Windows laptop; a project server that silently overrides the one you defined yourself; a token in the wrong header that makes Claude report a dead connection instead of prompting for OAuth. Those are the specifics ahead — the commands, the configuration model, and the failure modes that actually trip people up. The version-sensitive details were checked against the official Claude Code MCP reference and the MCP architecture spec on 2026-06-28, both linked in Sources.
How MCP actually works
MCP uses a client-server architecture. The MCP host (Claude Code, in this case) spins up one MCP client per server, and each client holds a dedicated connection to its server. The data layer speaks JSON-RPC 2.0 underneath, and every server can expose up to three core primitives:
- Tools - executable functions Claude can call (create a PR, run a query).
- Resources - contextual data Claude can read (files, records, API responses).
-
Prompts - reusable interaction templates the server defines, which surface in Claude Code as
/mcp__servername__promptnamecommands.
Servers talk to Claude Code over one of two transports. stdio runs a local process on your machine and communicates over standard input/output, which is ideal for tools that need direct system access. Streamable HTTP uses HTTP POST for client-to-server messages with optional Server-Sent Events for streaming, and supports bearer tokens, API keys, custom headers, and OAuth. There is also a legacy SSE transport, but it is deprecated - Anthropic advises using HTTP servers instead wherever they are available. (Claude Code can also drive a WebSocket transport via JSON config, but HTTP is the right default because only HTTP supports OAuth and the --transport flag.)
That is the standard MCP model, and it is worth knowing because it explains the rest of this guide: scopes decide where a server is defined, transports decide how Claude reaches it, and the primitives decide what it can do once connected.
Choose your transport and scope first
Before typing any commands, make two decisions: which transport the server uses, and which scope it should live in. This table maps the choices to where the configuration ends up.
| Decision | Option | Use when | Stored in |
|---|---|---|---|
| Transport | HTTP (recommended for remote) | Hosted/cloud server you do not run yourself | n/a (URL-based) |
| Transport | stdio (local) | Server runs as a process on your machine | n/a (command-based) |
| Transport | SSE (deprecated) | Only if a vendor offers nothing else | n/a |
| Scope |
local (default) |
Personal/experimental, current project only | ~/.claude.json |
| Scope | project |
Shared with your team via version control |
.mcp.json in project root |
| Scope | user |
Personal tools you want in every project | ~/.claude.json |
When the same server name is defined in more than one scope, Claude Code connects once using the highest-precedence definition: local has the highest precedence, then project, then user. The entire entry from the winning scope is used - fields are not merged across scopes. In practice that means a local server entry fully replaces a project entry of the same name, rather than overriding it field by field. (Plugin-provided servers and claude.ai connectors sit below all three named scopes and are matched by endpoint, not by name.)
Add a remote HTTP server
HTTP is the recommended transport for remote servers. The pattern is:
claude mcp add --transport http <name> <url>
For example, to connect Notion:
claude mcp add --transport http notion https://mcp.notion.com/mcp
If the server is shared infrastructure your whole team uses, add --scope project so it lands in .mcp.json and travels with the repo:
claude mcp add --transport http paypal --scope project https://mcp.paypal.com/mcp
If a vendor only documents a deprecated SSE endpoint, the syntax is claude mcp add --transport sse <name> <url> - but prefer HTTP when both exist.
Add a local stdio server
Local servers run as a child process. The double dash is the important part:
claude mcp add [options] <name> -- <command> [args...]
Everything after -- is passed to the server untouched, separating Claude's own flags (--transport, --env, --scope) from the command that launches the server. The official docs use Airtable as the stdio example, which also shows how --env injects credentials:
claude mcp add --env AIRTABLE_API_KEY=YOUR_KEY --transport stdio airtable \
-- npx -y airtable-mcp-server
A second real example from the docs is a read-only Postgres connection via dbhub:
claude mcp add --transport stdio db -- npx -y @bytebase/dbhub \
--dsn "postgresql://readonly:pass@prod.db.com:5432/analytics"
Note that one --env flag accepts multiple KEY=value pairs, but the server name must not come immediately after --env, or the CLI reads the name as another pair and rejects it. Keep at least one other flag between them, as in the Airtable example.
The Windows npx gotcha
On native Windows (not WSL), local servers launched with npx typically need a cmd /c wrapper to execute properly. So a stdio server that runs fine on macOS becomes, on Windows:
claude mcp add db -- cmd /c npx -y @bytebase/dbhub --dsn "postgresql://..."
This is the single most common reason a stdio server "works on my Mac" but fails on a teammate's Windows box. If you maintain a shared .mcp.json, document both invocations.
The .mcp.json format
A project-scoped server lives in a .mcp.json file at the repo root, using a standardized top-level mcpServers object. HTTP entries use type and url; stdio entries use command, args, and env:
{
"mcpServers": {
"shared-server": {
"command": "/path/to/server",
"args": [],
"env": {}
},
"notion": {
"type": "http",
"url": "https://mcp.notion.com/mcp"
}
}
}
Two practical notes. First, the type field accepts streamable-http as an alias for http - the MCP spec uses that name for the transport, so a config block copied straight from a vendor's docs usually works without edits. Second, Claude Code supports environment variable expansion using ${VAR} and ${VAR:-default} syntax across the command, args, env, url, and headers fields:
{
"mcpServers": {
"db": {
"type": "http",
"url": "${DB_MCP_URL:-https://localhost:8080/mcp}",
"headers": { "Authorization": "Bearer ${DB_TOKEN}" }
}
}
}
If a referenced variable is unset and has no default, Claude Code fails to parse the config entirely - so always supply a default for anything that might be missing on a teammate's machine. For project-relative paths in a stdio command or args, the docs recommend ${CLAUDE_PROJECT_DIR}, which Claude Code sets in the spawned server's environment to the project root.
You can also skip the file and add a server from raw JSON:
claude mcp add-json <name> '<json>'
Authentication with OAuth
Claude Code supports OAuth 2.0 for remote servers. It flags a server as needing auth when the server returns 401 Unauthorized or 403 Forbidden. Complete the flow from inside a session with the /mcp command, or - as of v2.1.186 - straight from your shell:
claude mcp login sentry
claude mcp login sentry --no-browser
Use --no-browser (or run over SSH without a display) to have Claude Code print the authorization URL instead of trying to open a browser; you then paste the full redirect URL back at the prompt. Tokens are stored securely and refreshed automatically. To clear them later, run claude mcp logout <name>.
Managing and verifying servers
| Command | What it does |
|---|---|
claude mcp list |
List all servers with status |
claude mcp get <name> |
Show details for one server |
claude mcp remove <name> |
Remove a server |
claude mcp reset-project-choices |
Reset project-scope approvals |
/mcp (in session) |
Check status, complete OAuth |
The status indicators are where you should be careful: the official docs explicitly confirm only two strings. In claude mcp list, a project-scoped server awaiting your approval shows as ⏸ Pending approval. In claude mcp get <name>, pending servers show as ⏸ Pending approval and ones you declined show as ✗ Rejected. For connection state, treat the CLI's own output as the source of truth: it surfaces status indicators such as connected, needs-authentication, and failed-to-connect, but the exact wording is not pinned in the docs, so do not script against a specific string without checking your installed version. The /mcp panel additionally shows the live tool count next to each connected server and flags servers that advertise tools but expose none.
Project-scoped servers from .mcp.json require explicit approval before first use. This is a security boundary, not bureaucracy: a server that fetches external content can expose you to prompt-injection risk, so confirm you trust each one before connecting. If you need to re-trigger the approval prompt, run claude mcp reset-project-choices.
Troubleshooting checklist
-
Startup timeout. The first run of a stdio server can be slow while
npxdownloads the package. The official docs do not publish a fixed default startup timeout number; instead, startup time is configurable via theMCP_TIMEOUTenvironment variable (their example,MCP_TIMEOUT=10000 claude, sets a 10-second window). Bump it for slow first installs:MCP_TIMEOUT=60000 claude(value in milliseconds). Separately, servers markedalwaysLoad: trueblock startup only until they connect, capped at a documented 5-second connect timeout. -
Failed to connect. For a stdio server, run the command directly in your shell. For HTTP,
curl -Ithe URL. The error you see there is almost always the real problem. Note that if you set anAuthorizationheader and the server rejects it, Claude Code reports a failed connection rather than falling back to OAuth - check the token or remove the header to use the OAuth flow. -
Relative paths. A very common stdio failure is a relative path in
commandorargs. Paths resolve against the directory Claude Code was launched from, not the location of.mcp.json. Use absolute paths or${CLAUDE_PROJECT_DIR}. -
Zero tools. If a server connects but its tool count stays at zero, the
/mcppanel flags it; runclaude --debug mcpto surface the server's stderr.
If you are also tuning how Claude Code behaves day to day, our guide to writing an effective CLAUDE.md pairs well with a clean MCP setup.
Scaling: tool search keeps context cheap
A real worry with MCP is context bloat - every tool definition normally eats tokens, so a handful of chatty servers can crowd out your actual conversation. Claude Code addresses this with tool search, which is enabled by default. Only tool names and short server instructions load at session start; the full tool schemas are deferred and discovered on demand when a task actually needs them. From your side, the tools behave exactly as before, but adding more servers has minimal impact on your context window.
If you would rather load schemas upfront when they are small, set ENABLE_TOOL_SEARCH=auto to load them when they fit within 10% of the context window and defer only the overflow, or ENABLE_TOOL_SEARCH=false to load everything. For a single server that Claude needs on every turn, set "alwaysLoad": true on that server's entry so its tools are always visible without a search step.
Matching a server to the right install path
Use this quick triage when you are not sure which option fits a given integration.
| Situation | Recommended path | Why |
|---|---|---|
| Vendor offers a hosted MCP URL | HTTP + local scope to start |
Fastest setup, no process to manage, OAuth supported |
| Whole team needs the same tool | HTTP or stdio + --scope project
|
Travels in .mcp.json, everyone approves once |
| Tool needs local filesystem/DB access | stdio | Direct system access, no network hop |
| You use it across every repo | --scope user |
Available everywhere, stays private to you |
| Server needs a secret/token |
--env or ${VAR} in .mcp.json
|
Keeps credentials out of version control |
| Vendor only ships an SSE endpoint | SSE, but ask for HTTP | SSE is deprecated; HTTP is the supported path |
Each install step, traced to its doc
The commands, scope precedence, .mcp.json schema, OAuth flow, MCP_TIMEOUT behavior, and status strings here trace to the official Claude Code MCP reference; the client-server model, JSON-RPC layer, three primitives, and stdio-vs-HTTP transport descriptions trace to the MCP architecture overview. Both are linked below and were read on 2026-06-28. Two details are deliberately hedged because the docs themselves leave them open: there is no published fixed default startup timeout (only the MCP_TIMEOUT knob), and ⏸ Pending approval and ✗ Rejected are the only connection-status strings the reference pins down. If you script against any other status wording, read it off your own installed CLI rather than this article.
Sources
-
Connect Claude Code to tools via MCP - official reference - commands, scopes,
.mcp.jsonformat, OAuth,MCP_TIMEOUT, status indicators, tool search, version notes. - Model Context Protocol - Architecture overview - client-server model, JSON-RPC 2.0, the three core primitives, and the stdio vs Streamable HTTP transports.

Top comments (0)