What MCP is for
The Model Context Protocol is an open standard for connecting AI tools to external systems — databases, APIs, ticketing systems, error logs. An MCP server exposes capabilities; Claude Code calls them directly.
The practical value is narrow and real: it removes the copy-and-paste step. Instead of pulling an error from your monitoring dashboard, pasting it in, then pasting back the fix, Claude queries the monitoring system itself. Instead of describing your database schema, it reads it.
That also describes when not to bother. If the data is already in the repository, MCP adds nothing — Claude Code can already read files. MCP earns its place when the information lives somewhere the filesystem cannot reach.
The four transports
| Transport | Flag | Use for |
|---|---|---|
| HTTP | --transport http |
Remote services. The recommended default, with OAuth support. |
| Stdio | --transport stdio |
A local process on your machine. |
| SSE | --transport sse |
Legacy remote services. Deprecated — use HTTP where available. |
| WebSocket | none | Persistent bidirectional connections. Configured only through .mcp.json or claude mcp add-json. |
In practice you will use HTTP for hosted services and stdio for anything running locally. If a server's documentation still tells you to use SSE, check whether it offers HTTP first.
Adding a server
A remote HTTP server:
claude mcp add --transport http stripe https://mcp.stripe.com
With a static auth header:
claude mcp add --transport http github https://api.githubcopilot.com/mcp/ \
--header "Authorization: Bearer YOUR_GITHUB_PAT"
A local stdio server:
claude mcp add --env AIRTABLE_API_KEY=YOUR_KEY --transport stdio airtable \
-- npx -y airtable-mcp-server
The -- separator is required for stdio servers and is the single most common mistake here. Everything before it is Claude's options; everything after is the command to run. Omit it and your server command gets parsed as flags.
The rest of the surface:
claude mcp list # every server, with health status
claude mcp get <name> # details for one
claude mcp remove <name> # remove it
claude mcp login <name> # OAuth
claude mcp logout <name> # clear credentials
claude mcp add-json <name> '...' # add from raw JSON config
claude mcp reset-project-choices # re-run project approvals
claude mcp serve # run Claude Code itself as an MCP server
Inside a session, /mcp opens a panel where you can authenticate and toggle servers on and off.
Scopes, and which file each writes to
Three scopes, and choosing wrong is why a colleague cannot see the server you added.
| Scope | Available in | Shared with the team | Written to |
|---|---|---|---|
| Local (default) | This project only | No | ~/.claude.json |
| Project | This project only | Yes — commit it |
.mcp.json at the project root |
| User | All your projects | No | ~/.claude.json |
claude mcp add --transport http shared-server --scope project https://example.com/mcp
claude mcp add --transport http hubspot --scope user https://mcp.hubspot.com/anthropic
Note that local and user scope both land in ~/.claude.json — local under a per-project key, user at the top level. Neither is shareable. Only --scope project produces a committable file.
The recommendation for teams is unambiguous: one central team configures the servers and commits .mcp.json, so everyone gets them without wiring anything up. That is also a security benefit — one team reviews what is connected, rather than a long tail of individual setups nobody has audited.
The .mcp.json format
{
"mcpServers": {
"issues": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"headers": {
"Authorization": "Bearer ${API_TOKEN}"
},
"timeout": 600000
},
"local-db": {
"command": "/path/to/server",
"args": ["--config", "${CLAUDE_PROJECT_DIR}/config.json"],
"env": {
"DB_URL": "${DATABASE_URL:-sqlite://default.db}"
}
}
}
}
Two things make this file safe to commit.
Environment variable expansion. ${VAR} expands from the environment, and ${VAR:-default} falls back. It works in command, args, env, url, and headers — which is exactly the set you need to keep secrets out of the file. Commit the reference, never the token.
${CLAUDE_PROJECT_DIR} resolves to the project root, so paths do not depend on where anyone checked the repository out.
For authentication schemes that need a fresh value each time — Kerberos, short-lived tokens, internal SSO — use headersHelper instead of static headers. It points at a script that prints a JSON object of header key-value pairs to stdout, runs with a 10-second timeout, and is called fresh on every connection.
Authenticating remote servers
For an OAuth server, add it and then authenticate:
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp
claude mcp login sentry
Or run /mcp in a session and follow the browser flow. On a headless machine, claude mcp login sentry --no-browser.
If you have your own client credentials, pass them rather than going through discovery:
claude mcp add --transport http \
--client-id your-client-id --client-secret --callback-port 8080 \
my-server https://mcp.example.com/mcp
--client-secret with no value prompts with masked input. For scripting, set MCP_CLIENT_SECRET in the environment instead — which is the right pattern, because a secret on a command line ends up in your shell history.
How MCP tools appear
MCP tools are namespaced:
mcp__<server-name>__<tool-name>
mcp__github__create_issue
mcp__notion__query_database
You need this format any time you write a permission rule, a subagent tools list, or an --allowedTools argument. Getting the double underscores wrong is a quiet failure — the rule simply never matches.
Servers bundled in a plugin carry a longer name:
mcp__plugin_<plugin-name>_<server-name>__<tool-name>
Reading the status output
claude mcp list reports a state per server, and most of them mean something specific:
| Status | Meaning |
|---|---|
| ✔ Connected | Working |
| ! Needs authentication | Run claude mcp login <name> or /mcp
|
| ⏸ Pending approval | A project .mcp.json server you have not trusted yet |
| ⊘ Disabled for this project | Toggled off in /mcp
|
| ✘ Failed to connect | With a detail explaining why |
| cached 2h ago | Tools came from the discovery cache, not a live connection |
That last one matters when you are debugging: a cached entry can look healthy while the server is down.
The risk you are taking on
This section is the reason to read the article rather than skim the commands.
Verify you trust a server before connecting it. A server that fetches external content can expose you to prompt injection — content it returns is text Claude reads, and text Claude reads can attempt to instruct it. That is a genuine attack surface, not a theoretical one.
Practical rules:
- Prefer read-only where the work is read-only. A database server pointed at a read-only user cannot be talked into a write.
- Scope credentials narrowly. A personal access token with full repository access does more damage than one scoped to issues.
-
Review project servers before accepting the trust dialogue. That prompt is doing real work; a
.mcp.jsonarriving in a pull request is a capability grant arriving in a pull request.
Non-interactive runs cannot show an approval dialogue, so claude -p and Agent SDK sessions need project servers pre-approved in settings, or --strict-mcp-config to use only what --mcp-config supplies. If you are running Claude Code in CI, that distinction is the one to get right.
Limits that bite in practice
| Limit | Default | Override |
|---|---|---|
| Tool output warning | 10,000 tokens | — |
| Tool output cap | 25,000 tokens | MAX_MCP_OUTPUT_TOKENS |
| Long calls move to background | 2 minutes | CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS |
| Idle timeout | 5 min HTTP, 30 min stdio | CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT |
| Reconnect attempts | 5, exponential backoff | — |
The output cap is the one that surprises people. A query returning a large result set gets truncated at 25,000 tokens, and the truncation is not always obvious in the response. If you are querying a database, put the LIMIT in the query rather than relying on the cap to do it for you.
Which servers are actually worth adding
An honest filter, because it is easy to accumulate servers that never get used and cost context on every session.
Add a server when the data lives outside your repository and you reference it often. Error tracking, issue tracking, a database schema you query while building, analytics you check while optimising.
Do not add one for data already in the repository. Claude Code reads files. An MCP server that reads your code is a slower version of a tool it already has.
Do not add one you will use twice. A one-off query is faster to run yourself and paste.
Two concrete examples worth the setup for most web projects:
# GitHub — issues and pull requests without leaving the terminal
claude mcp add --transport http github https://api.githubcopilot.com/mcp/ \
--header "Authorization: Bearer YOUR_GITHUB_PAT"
# A read-only database connection for schema questions
claude mcp add --transport stdio db -- npx -y @bytebase/dbhub \
--dsn "postgresql://readonly:pass@host:5432/db"
Note the readonly user in the second. That is the pattern: connect with the least privilege that makes the tool useful.
Where to go next
MCP extends what Claude Code can reach. Skills package what it does with that reach, subagents isolate the work and restrict the tools, and plugins ship all three to a team in one install. For enforcing rules around MCP calls, see hooks. New to the tool entirely? Start with getting started in the terminal.
For where a connected toolchain fits in real work, the complete website workflow covers the full build, the prompt library covers directing it, and a production CLAUDE.md covers the context every session starts from.
Sources and further reading
- Claude Code: MCP — commands, scopes, transports, authentication, limits
- Model Context Protocol — the open standard itself
- Claude Code: security — including prompt injection