Adding MCP servers to Claude Code, claude mcp add, .mcp.json, scopes and passing tokens from the environment
claude mcp add for stdio and HTTP servers, the local / project / user scopes and which wins a name clash, .mcp.json with ${VAR} tokens, the approval prompt, checks and permissions.
Contents
MCP (Model Context Protocol) is the common standard for connecting external tools and data sources to AI agents. Add an MCP server to Claude Code and it can drive a browser, read GitHub issues or query a database on its own initiative.
This article covers the whole setup in one place: the claude mcp add command, the scopes that decide whether a server is yours only, shared with the team on this project, or available in all your projects, how to share .mcp.json without committing tokens, and how to check connections and set permissions.
KEY POINT
What you will learn
- The basic form of
claude mcp addand the difference between stdio and HTTP servers - The local / project / user scopes: where each is stored, who sees it, and which wins a name clash
- Writing
.mcp.json, and passing tokens with${VAR}instead of committing them - The project-scope approval prompt, connection checks and permission rules
The add command
stdio servers (a local process)
Most MCP servers ship as npm packages and start with npx.
claude mcp add <name> -- <command> [args...]
Everything after -- is the command that starts the server. Pass environment variables with -e:
claude mcp add playwright -- npx @playwright/mcp@latest
claude mcp add github -e GITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxxx -- npx -y @modelcontextprotocol/server-github
HTTP / SSE servers (remote)
Connect to a hosted server with --transport http. Servers that require authentication can be signed into from the /mcp command during a session (OAuth in the browser).
claude mcp add --transport http <name> <URL>
用語解説
stdio versus HTTP: stdio starts a local server process and talks to it over standard input and output. HTTP connects to a server that is already running at a URL. Tools that touch local files or a browser are usually stdio; official servers offered by SaaS products are usually HTTP.
Scopes, where the configuration lives
The --scope option decides where the server is saved and who sees it. Three things separate the scopes: where they are stored, who they are shared with, and which one wins a name clash.
| Scope | Reach | Stored in | Shared with the team | Good for |
|---|---|---|---|---|
local (default) | This project, you only | ~/.claude.json, under that project's path | No | Trying things out, experimental configurations, servers with credentials you don't want in version control |
project | Everyone on the project | .mcp.json at the repository root | Yes, through version control | Tools the project relies on |
user | All your projects | ~/.claude.json | No | General-purpose tools (search, notes) |
Both local and user land in ~/.claude.json. The difference is where inside it: the documentation says Claude Code stores a local server under that project's path, so the same server won't appear in your other projects. Omitting --scope gives you local.
# Shared with the team (written to .mcp.json)
claude mcp add --scope project playwright -- npx @playwright/mcp@latest
# Available in all your projects
claude mcp add --transport http --scope user hubspot https://mcp.hubspot.com/anthropic
When the same name appears twice
When a server is defined in more than one place, Claude Code connects once, using the definition from the highest-precedence source. The whole entry from that source is used; fields are not merged across scopes. The order is local, then project, then user, then plugin-provided servers, then claude.ai connectors. Define the same name in two scopes with different endpoints and Claude Code warns about the conflict in claude mcp list output and in /mcp.
Writing .mcp.json and sharing it
Adding with --scope project creates or updates .mcp.json at the project root. You can also write it by hand. Commit it to share the configuration.
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
}
},
"internal-api": {
"type": "http",
"url": "${INTERNAL_MCP_URL:-https://mcp.example.internal/mcp}",
"headers": {
"Authorization": "Bearer ${INTERNAL_MCP_TOKEN}"
}
}
}
}
Passing tokens with ${VAR}
.mcp.json is committed, so a token written into env is visible to everyone who can read the repository. Claude Code expands ${VAR} references inside the file, so the token itself stays in your shell environment and only its name is committed.
| Form | Meaning |
|---|---|
${GITHUB_TOKEN} | Replaced with the value of GITHUB_TOKEN |
${VAR:-default} | Uses the default when VAR is unset |
Expansion is not limited to env: it works in command, args, url and headers too. Put the export in your shell profile (export GITHUB_TOKEN="ghp_xxxx" in ~/.zshrc or ~/.bashrc) and it is there whenever you start claude from a terminal. For the VS Code extension, VS Code itself must carry the variable: add it to the profile and restart VS Code, or launch it from a terminal with code .. If the project uses direnv, .envrc gives you per-directory values and belongs in .gitignore.
The ${VAR} approach fits the case where the whole team uses the same server but each person authenticates as themselves. A server only you use is simpler at local scope. As team conventions, agree on three things: .mcp.json carries only ${VAR} references, never a value; the README and .env.example list the required variable names; tokens start with read-only permissions.
If a token does get committed
Revoke and reissue the token before you worry about rewriting history. Removing it from the history does nothing about the copies already cloned by others.
Project scope comes with an approval prompt
The documentation states that for security reasons Claude Code prompts for approval in interactive sessions before using project-scoped servers from .mcp.json. To reset those choices, run claude mcp reset-project-choices.
The cases where the prompt does not appear are documented too. In claude -p runs, Agent SDK sessions and cloud sessions, Claude Code can't show the prompt, so it loads project-scoped servers without asking. It also skips the prompt in a session started in bypassPermissions mode with skipDangerousModePermissionPrompt set in user or managed settings. To keep a server out anyway, there are three options:
| Option | Effect |
|---|---|
Add it to disabledMcpjsonServers | Blocks it in every permission mode |
Exclude project settings with --setting-sources (SDK: settingSources) | Project settings are not read at all |
Start with --strict-mcp-config | Only the servers you pass with --mcp-config are used |
Checking the connection
claude mcp list # servers and their status
claude mcp get github # one server's configuration
claude mcp remove github --scope local
During a session, /mcp shows each server's status, the tools it exposes and whether it needs authentication. When a server shows as failed, check in this order:
- Start it by hand and read the error (
npx @playwright/mcp@latest) - If it uses an environment variable, run
echo $GITHUB_TOKENto confirm there is a value - Confirm the shell you exported in is the shell that launched Claude Code
- Check the spelling of the variable name in
.mcp.json
Allowing tools to run without prompts
MCP tools are confirmed before running, like any other tool. Frequently used ones can be allowed in settings.json. mcp__server covers every tool of a server; mcp__server__tool targets one tool. Keep write operations under ask.
{
"permissions": {
"allow": [
"mcp__playwright",
"mcp__github__get_issue",
"mcp__github__list_pull_requests"
],
"ask": [
"mcp__github__create_pull_request"
]
}
}
The full permission syntax is in Claude Code permissions in settings.json. To enable only some of the tools a server exposes, see Allow individual MCP tools in Claude Code.
Which server first?
Start with the one that provides information Claude cannot otherwise see in your daily work: the real browser (Playwright), current library documentation (Context7), or issues and pull requests (GitHub). For HTTP servers that require OAuth, see Sign in to an HTTP MCP server with OAuth. For handling secrets across all three tools, see Stop AI coding tools from reading your API keys and secrets.
Summary
claude mcp add name -- commandadds a stdio server;--transport httpadds a remote one- Scopes: local (default, you only, this project), project (
.mcp.json, shared), user (all your projects). On a name clash the order is local, project, user, and only one definition is used - Reference tokens as
${GITHUB_TOKEN}in.mcp.jsonand never write the value;${VAR:-default}supplies a fallback, and expansion works inenv,args,urlandheaders - Project-scoped servers need approval in interactive sessions; reset with
claude mcp reset-project-choices - Check with
/mcpandclaude mcp list, then decide what to auto-allow in permissions
FAQ
- Where is the MCP server configuration stored?
- It depends on the scope. local and user servers are stored in ~/.claude.json; project servers go in .mcp.json at the repository root. A local server is stored under that project's path, so it does not appear in your other projects.
- What is the default scope if I omit --scope?
- local. The server is available only to you, and only in the project where you added it.
- What happens if the same server name exists in two scopes?
- The definition from the highest-precedence source is used as-is. The order is local, project, user, plugin-provided servers, then claude.ai connectors, and fields are never merged across scopes.
- What happens if the environment variable in .mcp.json isn't set?
- The reference expands to an empty string, or to the default you supplied with ${VAR:-default}. The server then fails to authenticate, so check its state with /mcp.
- Can MCP tools run without confirmation?
- Yes. Add mcp__server__tool (or mcp__server for the whole server) to permissions.allow in settings.json.
Questions & answers
Q. An MCP server I added with claude mcp add shows as failed in /mcp. How do I find out why?
In most cases the server itself is failing to start. The quickest way to see the real error is to run the same command outside Claude Code.
npx @playwright/mcp@latest
Three causes account for most of these:
- The first
npxrun times out: it is still downloading the package. Start it manually once so it lands in the cache, then reconnect - Environment variables aren't reaching the server: check that the value you passed with
--env KEY=value, or a${VAR}in.mcp.json, exists in the shell you launched Claude Code from, withecho $VAR - Node.js is too old: check
node -vagainst the version the server requires, usually 18 or later
There is also an argument-order trap the documentation calls out. If the server name comes directly after --env, the CLI reads that name as another KEY=value pair and rejects it. Put at least one other option, such as --transport stdio, between --env and the server name.
claude mcp add --env AIRTABLE_API_KEY=YOUR_KEY --transport stdio airtable \
-- npx -y airtable-mcp-server
Everything after -- is passed to the server untouched. Without it, Claude Code tries to parse the server's own flags, such as --port, as its own options.
Once it starts, check the state with claude mcp list, and re-run /mcp inside the session.
Primary sources
This article was drafted by AI from official documentation and reviewed by the site operator before publishing. Found a mistake? Let us know via the contact page.