Claude Code's CLI is, in my experience running it across a dozen production repos, the most predictable of the four tools when it comes to MCP configuration. It has a first-class claude mcp subcommand, a clear scoping model (user, project, local), and settings files that are plain JSON rather than some bespoke format you have to reverse-engineer. This topic walks through the full setup — from install to a working, verified connection — the way I'd walk a new hire through it on day one.
Prerequisites: Installing Claude Code CLI and Preparing Your Environment
Before touching MCP, get the base CLI installed and authenticated. Claude Code ships as an npm package and requires Node.js 18 or newer (Node 20 LTS is what I run in CI to avoid surprises with native module resolution).
node --version
npm install -g @anthropic-ai/claude-code
claude --version
Log in once, interactively, so the CLI has a valid session before you start wiring up servers:
claude
Two things matter before you add any MCP server:
- Know your working directory. Claude Code resolves project-scoped MCP config relative to the directory you launch
claudefrom — usually your repo root. If you runclaudefrom a subfolder, project-scoped servers defined at the repo root won't be picked up unless you're in that exact directory tree Claude Code recognizes as the project root (it walks up looking for a.gitfolder). - Have your MCP server's run command ready. For a local stdio server this is typically an
npxoruvxinvocation; for a remote server it's a URL plus auth header. Test that command standalone first — e.g., runnpx -y @modelcontextprotocol/server-filesystem /pathdirectly in a terminal — before wiring it into Claude Code. Debugging a broken server binary through the MCP layer is much harder than debugging it directly.
npx -y @modelcontextprotocol/server-filesystem ~/projects
Tips
- Pin your Node version withnvmor.nvmrc— MCP server child processes inherit whatever Node is onPATH, and a stale global Node causes crypticENOENTerrors that look like MCP bugs but aren't.
- Runclaude doctorafter install; it flags missing dependencies (likeuvfor Python-based MCP servers) before you waste time debugging a "server failed to start" error.
Adding an MCP Server with the claude mcp add Command
The claude mcp add command is the fastest path and the one I default to. Syntax:
claude mcp add <name> [--scope local|project|user] [--transport stdio|sse|http] -- <command> [args...]
For a local stdio server (the most common case — most reference MCP servers run this way):
claude mcp add filesystem --scope project -- npx -y @modelcontextprotocol/server-filesystem /Users/me/projects
For a remote HTTP/SSE server, such as a hosted Sentry or Linear MCP endpoint:
claude mcp add sentry --transport sse --scope user \
https://mcp.sentry.dev/sse
If the remote server needs an auth header, pass it with --header:
claude mcp add linear --transport http --scope user \
--header "Authorization: Bearer ${LINEAR_MCP_TOKEN}" \
https://mcp.linear.app/http
Scope matters and this is where I've seen the most confusion on teams:
| Scope | Stored in | Shared with team? | Use for |
|---|---|---|---|
local |
~/.claude.json under the project path |
No | Personal experiments, secrets you don't want committed |
project |
.claude/settings.json (or .mcp.json at repo root) |
Yes, via git | Team-standard servers everyone needs (Figma, internal docs) |
user |
~/.claude/settings.json |
No, but follows you across projects | Personal tools you use everywhere (a personal Notion, personal GitHub PAT-based server) |
Check what's registered at any time:
claude mcp list
filesystem npx -y @modelcontextprotocol/server-filesystem /Users/me/projects ✓ connected
sentry https://mcp.sentry.dev/sse (sse) ✓ connected
linear https://mcp.linear.app/http (http) ✗ failed
Remove a server you no longer need:
claude mcp remove linear
Tips
- Default toprojectscope for anything the whole team should have — it turns MCP setup into agit pullinstead of a Slack thread of "which command do I run again?"
- Never bake a secret token directly into aproject-scope command string that gets committed. Use--headerwith an env var reference (${VAR}) or keep the server atlocal/userscope.
Configuring MCP Servers Manually in .claude/settings.json
claude mcp add is really just a CLI wrapper that edits JSON files for you. Editing them directly is faster once you know the schema, and it's the only realistic way to configure a dozen servers at once or version them cleanly in a repo.
Project-scoped config lives at .claude/settings.json (or the older/root-level .mcp.json, which Claude Code also reads for backwards compatibility with team-shared configs):
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "${DATABASE_URL}"
}
},
"sentry": {
"type": "sse",
"url": "https://mcp.sentry.dev/sse"
},
"linear": {
"type": "http",
"url": "https://mcp.linear.app/http",
"headers": {
"Authorization": "Bearer ${LINEAR_MCP_TOKEN}"
}
}
}
}
User-scoped config at ~/.claude/settings.json uses the identical mcpServers shape — Claude Code merges project and user config at startup, with project-scope entries winning on name collisions.
A few schema details that trip people up:
command+argsis for stdio transport — notypefield needed; it's inferred.type: "sse"ortype: "http"plusurlis for remote transports.envvalues support${VAR}interpolation from your shell environment — Claude Code does not load a.envfile automatically, so export the variable in your shell profile or prefix the launch command:DATABASE_URL=postgres://... claude.- Claude Code validates this JSON at startup; a syntax error silently disables MCP servers rather than crashing the whole CLI, so always run
claude mcp listafter a manual edit to confirm it parsed.
cat .claude/settings.json | python3 -m json.tool > /dev/null && echo "valid JSON"
Tips
- Commit.claude/settings.jsonto the repo for team-shared servers, but add a.claude/settings.local.json(gitignored by default) for anything personal — Claude Code merges both automatically.
- Keep one server per logical concern rather than one giant server exposing everything — it makesclaude mcp listoutput and permission prompts much easier to reason about.
Testing and Verifying Your MCP Connection in Claude Code CLI
Once a server is registered, verify it end-to-end rather than trusting claude mcp list's green checkmark alone — that check only confirms the transport connected, not that the tools actually work.
Inside an interactive session, ask Claude to enumerate available tools:
> What MCP tools do you currently have access to?
Or check directly with the slash command:
/mcp
This opens an interactive panel showing each server's connection status, the tools it exposes, and any auth state (useful for OAuth-based remote servers where the token might have expired).
For a stdio server, if it fails to connect, run it manually with the exact same command Claude Code uses and read stderr:
npx -y @modelcontextprotocol/server-postgres
Nine times out of ten a "failed to connect" status traces back to one of:
- A missing environment variable that only exists in your interactive shell, not in the environment Claude Code's child process inherits (this bites people using
direnvor shell-specific.zshrcexports). - A version mismatch — some npm-based MCP servers pin exact protocol versions; running
npx -y package@latestversus a pinned version can silently change behavior. - A remote server behind SSO/OAuth where the browser-based auth flow needs to be re-triggered via
/mcp→ select server → "Reauthenticate".
For a deeper trace, launch Claude Code with debug logging:
claude --mcp-debug
This prints the raw JSON-RPC handshake between Claude Code and each MCP server to stderr, which is the ground truth when a tool call silently returns nothing.
Tips
- Always test a newly added server with a trivial, side-effect-free tool call first (e.g., alistorreadoperation) before trusting it with anything that writes or deletes.
- If/mcpshows a server as connected but Claude never calls its tools, check that the tool's description in the server isn't vague — Claude picks tools based on how well the description matches the task, and generic descriptions get ignored in favor of built-in tools.
Tips
Tips
- Treat.claude/settings.jsonas infrastructure-as-code for your AI tooling — review changes to it in PRs the same way you'd review a Dockerfile change.
- Runclaude mcp listright after pulling teammates' changes; a newly added project-scoped server won't connect until its underlying binary or credentials exist locally too.
- When a server needs secrets, preferlocal/userscope over hardcoding, and document the required env vars in your repo's README so onboarding doesn't turn into a scavenger hunt.