Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild
Back to Blog AI Tooling

Claude Code MCP Server Setup: The Configuration That Actually Works After the First Restart

Sean

Platform Writer

Jun 19, 2026
8 min read

Claude Code reads MCP servers from ~/.claude.json for global servers and .mcp.json for project-scoped ones, accepts a command plus args (or a url for HTTP), and only picks them up after a full restart of the editor process. That is the entire config surface. Everything that goes wrong in setup is downstream of one of those three facts being missed: a half-restart that the IDE does not advertise, a project config that the agent cannot see, or a command that fails before Claude can even call it.

The reason this page ranks for the keyword is that the official docs explain the schema and stop there. The interesting decisions — when to use global vs project scope, how to keep secrets out of the config, how to debug a server that “is configured but never appears” — are all the things the docs assume you already know.

This post is what the missing manual looks like. It is for the engineer who has read the schema once, hit a problem, and wants the rest of the story.

Claude Code MCP server setup: the config that works after the first restart

Table of contents

The direct answer

For a single server, scoped to one project, the minimal config is:

// .mcp.json (project root, committed to git)
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]
    }
  }
}

For a global server, the equivalent lives in ~/.claude.json:

{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "ghp_replace_me"
      }
    }
  }
}

Both forms are correct. Which one you want depends on whether the server is part of the project or part of your toolbox.

Global vs project scope: the decision nobody flags

The MCP config has two scopes, and the docs do not push you toward one or the other. The right answer depends on what the server actually does.

Project-scoped (/.mcp.json) is for servers that the project needs to make sense. A database MCP for the project’s Postgres instance, a filesystem server pinned to the project root, an internal API server that only exists in this codebase. Commit the config (without secrets) so every contributor gets the same tools. The trade-off: every contributor sees the same command and the same args, which means the dev environment has to be able to run that command on every contributor’s machine.

Global (~/.claude.json) is for servers that are part of your toolbox, not the project. Your GitHub token, your personal notes MCP, the local tunnel you keep open for debugging. The trade-off: every project on your machine inherits the server, including the ones you would rather not have access to it.

A useful rule of thumb: if a new contributor should see the server, project-scope it. If the server is keyed to you, global-scope it. The mistake teams make is global-scoping everything and then wondering why every new project has random access to production data.

The two transport shapes: stdio and HTTP

MCP has two transport shapes, and the choice between them is the single biggest source of “the server is configured but does nothing” reports.

stdio transport is the default. Claude Code spawns the command directly, talks to it over stdin/stdout, and tears it down when the editor closes. This is the right shape for almost every local server. The command has to be a real binary on PATH (or a path you fully resolve), and it has to stay running long enough to respond. A common failure: pointing command at a shell script that exits immediately because of a syntax error, then wondering why the server never appears.

http transport is for remote servers. The config uses url instead of command:

{
  "mcpServers": {
    "remote-api": {
      "url": "https://mcp.example.com/v1",
      "headers": {
        "Authorization": "Bearer replace-me"
      }
    }
  }
}

The trade-off: the remote server has to be reachable from your editor process (which on a corporate network usually means it is not, even when the URL works from your browser). Local stdio is the path of least resistance. Reach for HTTP only when the server genuinely lives somewhere else.

Secrets, env vars, and the file you actually want

The config file is JSON, which means secrets end up as plain strings unless you do something about it. The schema supports an env block per server, and that block is the right place for tokens, keys, and connection strings. It is not, however, a substitute for a real secret store.

For local development, a .env.local loaded into the server’s process is the right shape:

{
  "mcpServers": {
    "my-db": {
      "command": "node",
      "args": ["./mcp-server.js"],
      "env": {
        "DATABASE_URL": "postgresql://user:pass@localhost:5432/mydb"
      }
    }
  }
}

Two rules worth enforcing from day one:

  1. Never commit the secret. If the file is committed, the secret is in git history forever. Use .gitignore on the config that contains secrets, or use a secret loader (1Password CLI, direnv, a CI-injected value) and reference the variable name only.
  2. Rotate anything that ever landed in a committed file. Tokens and keys do not have an “undo commit” button. The cleanest path after a leak is rotation, not scrubbing.

For production, a hosted secret store is the only honest answer. The MCP config is a deployment artifact, not a vault, and treating it as a vault is how credentials end up in PRs.

The restart that does not work the way you think

The most common cause of “I added the config and nothing happened” is a partial restart. Claude Code reads the MCP config on launch and caches it for the lifetime of the process. There is no in-editor “reload MCP servers” button that does what you want it to do, and editing the config while the editor is open will not retrigger discovery.

The sequence that actually works:

  1. Save the config.
  2. Quit Claude Code completely (not just close the window — quit the process).
  3. Relaunch.
  4. Confirm the server appears in the MCP panel or via /mcp (or whatever the version you are on uses).

If the server is project-scoped, the relaunch has to happen from inside the project directory. If it is global, the relaunch has to be a full process restart, not a workspace switch.

Debugging a server that loads but never responds

The next-most-common failure is the server that registers in the panel but times out on every call. The causes cluster into three groups.

The command exits before responding. Run the command by hand in a terminal. If it prints something and exits, Claude never gets a chance to talk to it. Wrap the server in a loop or a long-lived entry point.

The command works alone but not as a child of the editor. Environment differences are the usual suspect. A shell alias, a PATH set in your ~/.zshrc, an nvm shim that only loads in interactive shells — none of those exist in the editor’s child process. Test the exact command you put in the config by running it in a non-interactive shell: bash -lc 'your-command-here'. If it fails there, it will fail inside Claude.

The server needs a working directory it does not have. A common pattern: the server reads a local file relative to process.cwd(). Inside Claude, cwd is the project root. Set cwd explicitly in the config or use absolute paths in the server itself.

The fastest diagnostic, in order: run the command alone, then run it in a non-interactive shell, then add logging to the server so you can see what Claude sent.

A reference setup that survives a fresh clone

A team-friendly setup looks like this:

project/
├── .mcp.json                  # committed, contains commands and arg shapes
├── .mcp.local.json            # gitignored, contains env block with secrets
└── mcp-servers/
    └── my-internal-api/
        ├── package.json
        └── index.js

.mcp.json references the local override:

{
  "mcpServers": {
    "my-internal-api": {
      "command": "node",
      "args": ["./mcp-servers/my-internal-api/index.js"]
    }
  }
}

.mcp.local.json adds the secret the server needs:

{
  "mcpServers": {
    "my-internal-api": {
      "env": {
        "INTERNAL_API_TOKEN": "replace-me"
      }
    }
  }
}

The order Claude Code reads is global first, then project, then project-local. The deeper files win on conflict, which is the behavior you want for secret overrides.

For a real deploy, the same shape works behind a managed runtime: the MCP server runs as a private service, the config file is generated by the deploy pipeline, and the secret is injected from the platform’s secret store. If you are shipping an MCP server that other teams will use, the deploy story is the part that decides whether the tool is a demo or part of someone’s daily workflow. The RunxBuild deploy path is built for exactly this — one manifest, one private network, logs and secrets in the same place the rest of the app already lives.

How this fits the rest of the stack

An MCP server that gets used by other teams is a real piece of infrastructure, and the cost of running it should be modelled the same way as any other service — runtime, memory, bandwidth, storage, and database if it has one. The RunxBuild hosting calculator is the quick way to model that — pick the runtime size, the memory tier, the storage, and the expected request volume, and the calculator shows what the MCP server costs to run at the team’s actual usage rather than what the free tier hides.

Useful related references:

FAQ

Where does Claude Code store the MCP config?

Global servers live in ~/.claude.json. Project-scoped servers live in .mcp.json at the project root. Both files use the same mcpServers schema.

Why is my server in the panel but not in the agent’s tool list?

The panel shows registered servers; the agent only gets the tools for servers that responded successfully to the initial handshake. Run the server’s command in a non-interactive shell to see why the handshake is failing.

Can I use an MCP server without restarting the editor?

No. Claude Code reads the config on launch and caches it. The only reliable way to pick up a new server is a full process restart.

Should I commit .mcp.json to git?

Yes, but only the command and arg shape. Never commit the env block with real secrets — use .mcp.local.json (gitignored) or a secret loader for that.

How do I keep tokens out of the config file?

Use the env block and load the actual value from a .env.local (gitignored) or a platform secret store. Rotate any token that ever landed in a committed file — git history is forever.

What is the difference between stdio and HTTP transport?

Stdio spawns the command directly and talks over stdin/stdout — the right choice for local servers. HTTP uses a url instead and is for remote servers. Reach for HTTP only when the server genuinely lives on another host.

#claude code mcp server setup#claude code mcp config#mcp server#claude desktop config#claude code secrets#mcp stdio