MCP
The Model Context Protocol is an open standard that lets Claude Code act as a client to external servers, which expose tools, resources, and prompts that Claude can call directly.
What it is
MCP is a client/server protocol. Claude Code is the client. A server is a process or an HTTP endpoint that advertises three kinds of capability: tools Claude can call, resources Claude can read by URI, and prompts that appear in your session as /mcp__<server>__<prompt> commands. Tools are the part you will use most; resources and prompts are reached through ListMcpResourcesTool, ReadMcpResourceTool, and the command menu.
Servers connect over one of four transports. http is the recommended remote transport and the only one that supports OAuth. sse still works but is deprecated. stdio runs a local process and talks to it over standard input and output, which suits anything needing direct system access. ws holds a persistent bidirectional socket for servers that push events unprompted.
Configuration lives at three scopes. Local scope, the default, stores the server in ~/.claude.json under your current project path and keeps it private to you. Project scope writes .mcp.json at the repository root so the whole team gets it. User scope stores it in ~/.claude.json for every project on your machine. When the same name appears in more than one, local wins, then project, then user, then plugin servers, then claude.ai connectors — and the whole entry from the winning source is used, with no field merging.
What it does for you
- It ends the copy-paste loop between Claude Code and your other tools. Instead of pasting a Jira ticket into chat, Claude reads it, and instead of describing a query result, Claude runs the query.
- It puts external state behind the same permission system as everything else.
mcp__github__*inpermissions.allowpre-approves one server;mcp__*inpermissions.denyremoves every MCP tool from Claude’s context. - It scales past the context budget. Tool definitions are deferred by default, so a server with sixty tools contributes its names, not sixty full schemas, and Claude loads the ones it needs through tool search.
How it works
01Claude Code reads your MCP configuration at startup
It merges local, project, user, plugin, and connector sources, resolves duplicates by the precedence order above, and begins connecting each server.
02Project servers wait for approval
A server defined in
.mcp.jsonprompts in interactive sessions before it is used, and shows as⏸ Pending approvalinclaude mcp listuntil you accept.claude -pruns, SDK sessions, and cloud sessions cannot show that prompt, so they load project servers without asking.03Each server advertises its capabilities
After connecting, Claude Code issues
tools/list,prompts/list, andresources/list. Tool names becomemcp__<server>__<tool>; a plugin-bundled server becomesmcp__plugin_<plugin>_<server>__<tool>.04Tool definitions stay deferred by default
Only the names enter the prompt. Claude calls
ToolSearchto load the full schema of a tool it wants. Because deferred definitions sit after the cache breakpoint, a server connecting or disconnecting mid-session does not invalidate the prompt cache.05Calls run under timeouts, then move to the background
A main-conversation call still running after two minutes becomes a background task so the session is not blocked. A call that sends nothing for the idle window — five minutes for remote servers, thirty for stdio — aborts with an error.
06Remote servers reconnect on their own
An HTTP or SSE server that drops mid-session is retried up to five times with exponential backoff starting at one second. Stdio servers are local processes and are not reconnected automatically.
How to implement it
01Add the server at the right scope
Run
claude mcp add --transport http <name> <url>for a personal server, add--scope projectto write.mcp.jsonfor the team, or--scope userto get it in every project.02Pass credentials as headers or environment variables
Use
--header "Authorization: Bearer $TOKEN"for remote servers and--env KEY=valuefor stdio ones. In a committed.mcp.json, reference secrets as${GITHUB_TOKEN}so the value stays out of git.03Verify the connection
Run
claude mcp listand look for✔ Connected. A✘ Failed to connectline carries the HTTP status and the server’s own error text. Run/mcpinside a session to authenticate an OAuth server.04Scope what the server may do
Add
mcp__<server>__<tool>entries topermissions.allowfor the calls you want unprompted, anddenyentries for anything destructive. Rules follow the same deny-then-ask-then-allow order as every other tool.05Commit .mcp.json and document the environment variables
Teammates get the server definition from git and supply their own credentials. Note the required variables in
CLAUDE.mdso a fresh clone does not fail silently.
Examples
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer ${GITHUB_MCP_TOKEN}"
}
},
"figma": {
"type": "http",
"url": "https://mcp.figma.com/mcp"
},
"postgres": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "${DATABASE_URL:-postgres://localhost:5432/app_dev}"
},
"timeout": 60000
}
}
}{
"enabledMcpjsonServers": ["github", "figma", "postgres"],
"permissions": {
"allow": [
"mcp__github__get_*",
"mcp__github__list_*",
"mcp__figma__*",
"mcp__postgres__query"
],
"deny": [
"mcp__postgres__execute"
]
}
}Use it when
- Implementing a ticket end to end: Claude reads the issue from your tracker and opens the pull request without you pasting either.
- Answering questions against a real database instead of guessing from the schema file.
- Turning a Figma frame into components, with the design read directly rather than described.
- Checking a monitoring dashboard mid-debug so the error rate in the conversation is the live one.
- Letting a server push events into your session as a channel, so Claude reacts to CI results while you are away.
Avoid it when
- A CLI already does the job.
gh,psql, andawsare one Bash permission rule away and cost no tool definitions, no connection, and no failure mode. - You do not trust the server. An MCP server that fetches external content can feed prompt-injection payloads straight into your session, and its tools run with your credentials.
- The server exposes dozens of tools you will never call and tool search is unavailable in your configuration, because then every definition sits in the cached prefix and every reconnect invalidates it.
- You need the connection to be reliable in CI. Project servers load without approval under
-p, stdio servers are not auto-reconnected, and a failed connection is reported in the init event rather than failing the run.
Common mistakes
SYMPTOMA server in
.mcp.jsonnever connects and Claude behaves as if its tools do not exist.FIXRun
claude mcp list.⏸ Pending approvalmeans you have not approved the project server yet; startclaudeinteractively and accept.✘ Failed to connectnow carries the status code and the server’s error text.SYMPTOMA remote entry fails with
command: expected string, received undefinedor a message about a missing type.FIXAn entry with a
urlbut notypeis read as a stdio server. Add"type": "http"— or"sse"/"ws"— to the entry.SYMPTOMAuthentication fails intermittently after you paste a token into the config.
FIXA pasted token often carries a trailing newline. Claude Code warns about
Leading or trailing whitespace in: headers.Authorizationin/mcpandclaude mcp list, but it does not trim it — edit the file.SYMPTOMAdding a stdio server on the command line fails to parse the server’s own flags.
FIXPut
--before the command:claude mcp add --env KEY=v --transport stdio db -- python server.py --port 8080. Everything after--is passed through untouched.SYMPTOMEvery session starts with an expensive uncached turn after you add a server.
FIXThat happens when tool definitions load into the prompt instead of being deferred. Check whether tool search is disabled by a custom
ANTHROPIC_BASE_URL,ENABLE_TOOL_SEARCH=false, or analwaysLoadmarker on the server.
Best practices
- Prefer
httpfor remote servers; SSE is deprecated and WebSocket supports neither OAuth nor the--transportflag. - Keep credentials in environment variables referenced as
${VAR}from.mcp.json, never as literals in a committed file. - Read the server’s source or its Directory listing before connecting it, and treat everything it returns as untrusted input.
- Write allow rules per tool rather than per server, so read calls are frictionless and writes still stop for approval.
- Set a per-server
timeouton anything slow, and remember it is a hard wall-clock limit that progress notifications do not extend. - Run
/mcpwhen something behaves oddly: it shows connection state, tool counts, and the server’s own error text.
Try it in five minutes
Connect a stdio MCP server, see its tools appear, and watch a deny rule remove one.
- 1.Run
claude mcp add --transport stdio memory -- npx -y @modelcontextprotocol/server-memoryin any project. - 2.Run
claude mcp listand confirm the server shows✔ Connected. - 3.Start
claude, run/mcp, and note the tool count next to the server. - 4.Ask Claude to store and then recall a fact, and watch the
mcp__memory__*tool calls in the transcript. - 5.Add
"deny": ["mcp__memory__*"]underpermissionsin.claude/settings.json, start a new session, and confirm in/mcpthat the tools are gone from Claude’s context.
Related concepts
Verified against code.claude.com/docs/en/mcp on 2026-08-09. See content/SOURCES.md for the full table.
← / → MOVE BETWEEN CONCEPTS