- Errorfilesystem
Stdio server is missing "command" — Claude Code has nothing to launch.
Fix: Add "command": "npx" (or "node", "uvx", a binary path…) with any flags in "args".
- Errorlinear
Unknown key "serverUrl" — Claude Code will ignore it.
Fix: Rename "serverUrl" to "url".
{
"mcpServers": {
"filesystem": {
"command": "<command>",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"./"
]
},
"linear": {
"type": "http",
"url": "https://mcp.linear.app/mcp"
}
}
}# Adds to user scope (~/.claude.json) by default.
# Append --scope project to write a shareable .mcp.json into the repo instead.
claude mcp add filesystem -- '<command>' -y @modelcontextprotocol/server-filesystem ./
claude mcp add --transport http linear https://mcp.linear.app/mcpWhere configs live: .mcp.json = project scope (checked into the repo, shared with the team) · ~/.claude.json = user scope (what claude mcp add writes by default) · claude_desktop_config.json = Claude Desktop, a different app — same JSON shape, different file.
Three config files, one classic mix-up
Claude Code reads MCP servers from more than one place, and most “my server doesn’t show up” reports are really scope confusion. There are three scopes — and a fourth file that belongs to a different app entirely.
| File | Scope | Who gets it |
|---|---|---|
.mcp.json | Project | Everyone who clones the repo — check it in. Servers need one-time approval per user. |
~/.claude.json | User / local | What claude mcp add writes by default — your machine, every project (user) or one project (local). |
claude_desktop_config.json | Claude Desktop | The desktop chat app — a different program. Same JSON shape, zero effect on Claude Code. |
The claude_desktop_config.json trap deserves emphasis: countless tutorials target Claude Desktop, and their configs look byte-for-byte compatible. They are — but Claude Code never reads that file. If a server works in Desktop and is invisible in your terminal, this is why. For team projects, prefer project scope: a committed .mcp.json means every teammate (and every agent) gets the same tools, which matters once you start running several Claude Code sessions in parallel.
stdio vs http vs sse — when each
A stdio server is a program Claude Code launches for you — it spawns command with args as a child process and speaks JSON-RPC over stdin/stdout. Use it for anything local: filesystem access, a database on localhost, a CLI wrapper. No port, no network, dies with the session.
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": { "DATABASE_URL": "${DATABASE_URL}" }
}
}
}A remote server is something already running elsewhere — you give Claude Code a URL instead of a command. "type": "http" (streamable HTTP) is the modern transport hosted providers use; "type": "sse" is the older Server-Sent Events variant, kept for servers that haven’t migrated yet.
{
"mcpServers": {
"linear": {
"type": "http",
"url": "https://mcp.linear.app/mcp",
"headers": { "Authorization": "Bearer ${LINEAR_TOKEN}" }
}
}
}Rule of thumb: runs on your machine → stdio; lives behind an https endpoint → http; the vendor’s docs literally say SSE → sse. If these terms are new, the AI glossary covers MCP, transports, and the rest of the agent vocabulary in one page.
Env var expansion: the right way to handle secrets
Claude Code expands ${VAR} — and ${VAR:-default} with a fallback — inside command, args, env, url, and headers when it launches a server. The token lives in your shell; the config file stays committable. This is not optional hygiene: .mcp.json is designed to be checked into git, so any literal key you paste there is one git push away from being public.
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}",
"API_BASE": "${API_BASE:-https://api.example.com}"
}The validator above warns when an env or header value looks like a real token (long, token-shaped, or matching known prefixes like ghp_ or sk-). If you see that warning on a config you’ve already committed, rotate the key — scrubbing git history is much harder than exporting a variable.
The three ways MCP configs actually break
After syntax errors (trailing commas, comments, single backslashes in Windows paths), almost every broken config falls into one of three buckets — all silent, because Claude Code skips entries it can’t use rather than crashing.
Stringified args
"args": "-y my-server ./" is one argument, not three. Args must be an array with one string per token — and numbers like ports still get quoted, because process arguments are always strings.Wrong key names
mcp_servers, serverUrl, comand — JSON has no schema, so typos don’t error, they just get ignored. The only accepted top-level key is mcpServers, exact camelCase.Secrets in the repo
${VAR} expansion, keep real values in your shell, and let the file be boring enough to commit.The same discipline that keeps configs clean keeps agents effective: explicit, checked-in, reviewable context. That’s the idea behind a good AGENTS.md / CLAUDE.md file too — and behind choosing a small set of MCP servers that actually earn their context window instead of installing everything.