MCP servers are configured with JSON. You add a server name, a transport type, and either a command to run locally or a URL to reach remotely. Every major AI client - Claude Desktop, Claude Code, VS Code, Cursor, Windsurf, Cline, Continue, Amazon Q, Yaw Terminal - uses the same basic format. But they all store it in different places, with different scopes and different quirks.

This guide covers everything: the config format, where each client keeps it, how project-level and global configs interact, and the gotchas that trip people up.

The config format

Every MCP config file has the same shape. A top-level mcpServers object where each key is a server name and each value describes how to connect.

There are two transport types that matter: stdio for local servers and Streamable HTTP for remote servers.

stdio - local servers

The client launches the MCP server as a child process on your machine. Communication happens over stdin/stdout. You specify a command, optional arguments, and optional environment variables.

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"],
      "env": {
        "DEBUG": "true"
      }
    }
  }
}

When you save this config and your client reads it, it will run npx -y @modelcontextprotocol/server-filesystem /Users/me/projects as a subprocess. The MCP client and server talk over that process’s stdin and stdout using JSON-RPC.

stdio is the simplest transport. No network, no auth, no ports. But it only works when the server and client are on the same machine.

Streamable HTTP - remote servers

For remote servers, the client connects to a URL over HTTP. The server lives somewhere else - a cloud VM, a container, a managed platform.

{
  "mcpServers": {
    "my-api": {
      "type": "http",
      "url": "https://my-api.example.com/mcp",
      "headers": {
        "Authorization": "Bearer sk-abc123"
      }
    }
  }
}

Streamable HTTP replaced the older SSE transport starting with the March 2025 spec revision (formalized in the 2025-11-25 spec). It uses a single endpoint instead of two, supports stateless operation, and works with standard proxies and load balancers. If your server still uses SSE, it will work for now, but new servers should use Streamable HTTP.

Where configs live

This is where things get fragmented. Every client stores MCP config in a different location, and most support multiple scopes - project-level, user-level, or both. Here is the full map.

Want to skip the file-path archaeology? Run npx -y @yawlabs/mcph install <claude-code | claude-desktop | cursor | vscode> once and mcph picks the right path for your OS, merges the launch entry without clobbering anything you already have, and saves your token to ~/.mcph.json so every other MCP client on the machine inherits it. The full per-client map below is here for the cases where you want to control scope by hand.

Claude Code (CLI)

Claude Code has the most sophisticated config system with three scopes:

You can also manage servers with CLI commands instead of editing JSON directly:

# Add a remote server to the current project
claude mcp add my-server --type http --url https://my-server.example.com/mcp

# Add a local stdio server globally
claude mcp add --scope user db-tools -- npx -y @modelcontextprotocol/server-sqlite ./db.sqlite

# List all configured servers
claude mcp list

# Import servers from Claude Desktop
claude mcp add-from-claude-desktop

Claude Desktop

Single config file, no scope hierarchy:

Same mcpServers format as everything else. No project/user distinction - all servers are global.

Claude.ai (web)

Claude.ai supports MCP through custom connectors in Settings → Integrations. But there is an important limitation: connectors only support remote servers over Streamable HTTP. There is no stdio option - you cannot launch a local process from a browser.

This means if you have a stdio MCP server that you want to use with Claude.ai, you need to host it somewhere as a remote HTTP endpoint first. More on that below.

VS Code (GitHub Copilot)

Important: VS Code uses servers as the root key, not mcpServers. It is the only major client that does this.

// .vscode/mcp.json
{
  "servers": {
    "playwright": {
      "command": "npx",
      "args": ["-y", "@microsoft/mcp-server-playwright"]
    }
  }
}

Cursor

Uses mcpServers as root key. One quirk: environment variable references use ${env:VAR_NAME} syntax instead of the more common ${VAR_NAME}.

Windsurf

Uses mcpServers root key. No project-level config support.

Cline

Uses mcpServers root key. Adds disabled (boolean) and autoApprove (array of tool names) fields per server.

Amazon Q Developer

Uses mcpServers root key. Server names must be unique across both scopes.

Yaw Terminal

Uses mcpServers root key. Yaw Terminal supports both stdio and Streamable HTTP transports, and reads the same .mcp.json project config that Claude Code uses.

Project-level config: .mcp.json

The .mcp.json file at your project root is the most important config file if you work on a team. You check it into version control, and every teammate gets the same MCP servers when they clone the repo. No setup docs, no “add this to your local config” Slack messages.

// .mcp.json (project root)
{
  "mcpServers": {
    "database": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-sqlite", "./dev.db"]
    },
    "staging-api": {
      "type": "http",
      "url": "https://staging-api.example.com/mcp",
      "headers": {
        "Authorization": "Bearer ${STAGING_API_KEY}"
      }
    }
  }
}

When a teammate opens the project in Claude Code, they get prompted to approve the project-scoped servers on first use. This is a security feature - project configs run commands on your machine, so you should review them before approving.

Notice the ${STAGING_API_KEY} in the config. Claude Code expands environment variables in commands, args, URLs, and headers. This means you can check in the config without hardcoding secrets - each developer sets the variable in their own shell environment.

Override behavior

When the same server name exists at multiple scopes, the most specific scope wins:

  1. Local (highest priority) - your personal config for this project
  2. Project - the .mcp.json in the repo
  3. User/Global (lowest priority) - your global config

This means a team can define shared defaults in .mcp.json, and individual developers can override specific servers locally. For example, the project config might point at a shared staging server, but you override it locally to point at your own dev instance.

Common pitfalls

Windows and npx

On native Windows (not WSL), npx commands in stdio configs need a cmd /c wrapper:

{
  "mcpServers": {
    "my-server": {
      "command": "cmd",
      "args": ["/c", "npx", "-y", "@some/mcp-server"]
    }
  }
}

Without the wrapper, the process often fails to spawn. This trips up a lot of people.

VS Code uses a different root key

If you copy a config from Claude Desktop or Cursor into .vscode/mcp.json, it will not work. VS Code expects servers, not mcpServers. Every other client uses mcpServers.

Env var syntax varies by client

There is no standard. If you switch clients, double-check how environment variables are referenced.

SSE is deprecated

If your config uses "type": "sse", it still works in most clients but is deprecated as of the March 2025 spec update. Switch to "type": "http" when you can.

stdio vs. remote: the trade-off

stdio servers are simple. No infrastructure, no auth, no deployment. Clone the repo, run the server, done. But they have real limitations:

Remote servers solve all of this but add deployment complexity. You need a host, a domain, TLS, auth, and monitoring. That is a lot of infrastructure work for what started as a simple tool.

There is a middle ground for teams that want remote access without public exposure: tailscale-mcp lets you run MCP servers over a Tailscale private network. Your server stays on your infrastructure with no public endpoint, but any teammate on the tailnet can reach it as a remote Streamable HTTP server. It bridges the gap between “localhost only” and “fully public.”

The orchestration option

Once you are running more than two or three MCP servers, the config file itself becomes the problem. Every new machine is another round of JSON edits, every secret rotation is a cross-device chore, and every teammate has their own slightly-different setup.

mcp.hosting collapses all of that into a single install. You add servers in a web dashboard, install the mcph CLI once, and drop one entry into your client config:

{
  "mcpServers": {
    "mcph": {
      "command": "npx",
      "args": ["-y", "@yawlabs/mcph"],
      "env": { "MCPH_TOKEN": "mcp_pat_..." }
    }
  }
}

Same config on every machine. Same servers for every teammate. Add, remove, or rotate credentials from the dashboard and every device picks up the change on next run - no JSON edits, no version drift.


Jeff Yaw, Yaw Labs. Follow along at tokenlimit.news for weekly notes on AI infrastructure.