Languages: EN RU

Connecting MCP Servers in Claude Code

In the previous article we covered the MCP architecture: three roles (Host/Client/Server), three primitives (tools/resources/prompts), and two transports (stdio and streamable HTTP). Now let's move to practice — how exactly to connect a server to a specific project, where configuration is stored, and how to avoid security mistakes.

The central command: claude mcp add

All MCP server management goes through the claude mcp subcommand. The basic syntax depends on the transport type.

For HTTP servers (cloud services):

# Connect a remote server over HTTP
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp

# With an authorization header (static token)
claude mcp add --transport http github https://api.githubcopilot.com/mcp/ \
  --header "Authorization: Bearer YOUR_GITHUB_PAT"

For stdio servers (local processes):

# Important: the double dash -- separates claude flags from the server command
claude mcp add --transport stdio airtable --env AIRTABLE_API_KEY=key123 \
  -- npx -y airtable-mcp-server

# PostgreSQL via a local proxy
claude mcp add --transport stdio db \
  -- npx -y @bytebase/dbhub --dsn "postgresql://ro:pass@localhost:5432/prod"

Note the -- (double dash): without it, Claude Code will try to parse the server's arguments as its own flags and throw an error. This is a classic gotcha when first working with stdio servers.

Check yourself
You want to connect a stdio server with the command: `claude mcp add --transport stdio myserver -- python server.py --port 8080`. What happens if you remove `--` and write `claude mcp add --transport stdio myserver python server.py --port 8080`?

Managing connected servers — a few commands worth knowing:

claude mcp list          # Show all servers and their status
claude mcp get github    # Details for a specific server
claude mcp remove github # Remove a server

Inside a Claude Code session, /mcp is available — a panel showing the status of each server, the number of tools, and an OAuth authorization button.

/mcp

MCP Servers
──────────────────────────────────────────
● github        Connected        42 tools
● sentry        Connected         8 tools
! notion        Auth Required       [Connect]
○ local-db      Disconnected

Three scopes: where configuration is stored

The scope determines two things: where the configuration physically lives and who else can see it.

graph TD A[claude mcp add] --> B{--scope?} B -->|local (default)| C[~/.claude.json<br/>under project path] B -->|project| D[.mcp.json<br/>in project root] B -->|user| E[~/.claude.json<br/>globally] C --> F[You only,<br/>current project] D --> G[Whole team<br/>via git] E --> H[You only,<br/>all projects]
graph TD
    A[claude mcp add] --> B{--scope?}
    B -->|local (default)| C[~/.claude.json<br/>under project path]
    B -->|project| D[.mcp.json<br/>in project root]
    B -->|user| E[~/.claude.json<br/>globally]
    C --> F[You only,<br/>current project]
    D --> G[Whole team<br/>via git]
    E --> H[You only,<br/>all projects]
The three MCP server scopes: where the configuration is written and who can see it

Local (default) — the server is available only to you, only in the current project. Stored in ~/.claude.json under the project path. Never goes into git; no one else sees it.

# Both calls are equivalent — local is the default
claude mcp add --transport http stripe https://mcp.stripe.com
claude mcp add --transport http stripe --scope local https://mcp.stripe.com

Project — the server is available to the entire team, because the configuration is written to .mcp.json in the project root. This file is committed to git. Every team member who opens the project will see the same server.

claude mcp add --transport http paypal --scope project https://mcp.paypal.com/mcp

The result — .mcp.json is created or updated:

{
  "mcpServers": {
    "paypal": {
      "type": "http",
      "url": "https://mcp.paypal.com/mcp"
    }
  }
}

User — the server is available to you across all projects. Also stored in ~/.claude.json, but not tied to any specific project.

claude mcp add --transport http hubspot --scope user https://mcp.hubspot.com/anthropic

Quick-reference table:

ScopeLoaded inVisible to othersStored in
localCurrent projectNo~/.claude.json
projectCurrent projectYes, via git.mcp.json
userAll projectsNo~/.claude.json
Check yourself
A manager asked you to configure an MCP server for an internal database so that all team developers automatically receive it when cloning the repository. Which scope should you use, and what gets committed to git?

The .mcp.json file: manual configuration and environment variables

The .mcp.json file is not just an artifact produced by claude mcp add — it can and should be edited by hand, especially for team-wide settings. A typical scenario: the config is committed to the repository, but each developer supplies their own API key via an environment variable.

Claude Code supports variable expansion directly in .mcp.json:

{
  "mcpServers": {
    "api-server": {
      "type": "http",
      "url": "${API_BASE_URL:-https://api.example.com}/mcp",
      "headers": {
        "Authorization": "Bearer ${API_KEY}"
      }
    },
    "local-db": {
      "command": "npx",
      "args": ["-y", "@bytebase/dbhub", "--dsn", "${DATABASE_URL}"],
      "env": {
        "LOG_LEVEL": "${LOG_LEVEL:-info}"
      }
    }
  }
}

The ${VAR:-default} syntax is standard shell style: if the variable is not set, the value after :- is used. Expansion works in the command, args, env, url, and headers fields. If a required variable is not set (no default provided), Claude Code will be unable to parse the config.

In practice this means: URLs and server names can safely be committed to .mcp.json, but secrets (tokens, passwords) should be moved to .env or the system environment. Add .env to .gitignore, but not .mcp.json.

Transports in detail

We already covered stdio and streamable HTTP in the previous article at the protocol level. Here are the practical nuances of connecting them.

Streamable HTTP — the recommended option for cloud services. In the JSON config the type is streamable-http (per the spec), but the CLI accepts both http and streamable-http.

{
  "mcpServers": {
    "my-api": { "type": "streamable-http", "url": "https://api.example.com/mcp" }
  }
}

SSE (Server-Sent Events) — a legacy transport, officially deprecated. If a server offers a choice, go with HTTP. Some older servers still use SSE so support exists, but new integrations should not be built on it.

WebSocket — for servers that push events on their own (CI notifications, monitoring). Does not support OAuth or the --transport CLI flag; configured only via .mcp.json or claude mcp add-json:

claude mcp add-json events-server \
  '{"type":"ws","url":"wss://mcp.example.com/socket","headers":{"Authorization":"Bearer TOKEN"}}'

OAuth for remote servers

Most cloud MCP servers (Sentry, Notion, Slack, and others) require authentication via OAuth 2.0. The process is two steps:

1. Add the server with claude mcp add — no token needed, just the URL.

2. Run /mcp inside Claude Code and follow the instructions in the browser.

# Step 1: add the server
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp

# Step 2: inside the session run /mcp → click Connect → the browser opens automatically

Claude Code detects that the server returned 401 Unauthorized and marks it in /mcp as requiring authentication. Tokens are stored in the system keychain (macOS) or in a credentials file — not in .mcp.json. They are refreshed automatically when they expire.

If the OAuth provider requires a pre-registered redirect_uri (does not support Dynamic Client Registration), you need to explicitly set a port and register the application in advance:

claude mcp add --transport http \
  --client-id your-client-id --client-secret --callback-port 8080 \
  my-service https://mcp.example.com/mcp

The --client-secret flag without a value enables masked input in the terminal — the secret will not end up in the shell history. For CI, use the MCP_CLIENT_SECRET environment variable instead.

Check yourself
You added a Sentry server with the command `claude mcp add --transport http sentry https://mcp.sentry.dev/mcp`. On first use, Claude Code cannot retrieve data — a 401 error occurs. What is the next step?

Security: what to know before connecting

MCP servers execute tools on your behalf. A few rules always worth following:

Verify the server's source. A server that loads external content (web pages, documents, API responses) could potentially pass hostile instructions to Claude — this is prompt injection. For unfamiliar servers, read the source code or look for security audits.

Grant minimal permissions. For a GitHub PAT, select only the needed repositories with the minimum set of permissions — not repo:write for the whole account when read-only access to one repository is enough. For databases, use a read-only user if you don't need write access.

Project-scope requires explicit approval. Claude Code will not silently connect a .mcp.json server — on first launch it will show an approval prompt. This is protection against accidentally pulling in malicious servers from cloned repositories. To reset approval choices: claude mcp reset-project-choices.

Secrets go in the environment, not the config. .mcp.json is committed to git. If a token ends up in there, consider it compromised. Use ${VAR} and .env (in .gitignore).

# Bad — token in the config, which will go into git
{
  "mcpServers": {
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp/",
      "headers": { "Authorization": "Bearer ghp_real_token_here" }
    }
  }
}

# Good — token from an environment variable
{
  "mcpServers": {
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp/",
      "headers": { "Authorization": "Bearer ${GITHUB_PAT}" }
    }
  }
}

Diagnosing problems

A few common situations and how to debug them:

  • Server hangs on startup. MCP_TIMEOUT=10000 claude — reduce the timeout to 10 seconds to get an error quickly instead of waiting indefinitely.
  • Server returns too many tokens. MAX_MCP_OUTPUT_TOKENS=50000 claude — increase the limit (default is 25,000 tokens; a warning appears when 10,000 tokens are exceeded).
  • HTTP/SSE server dropped mid-session. Claude Code reconnects automatically with exponential backoff (up to 5 attempts, starting at 1 s). Status is visible in /mcp.
  • Server in .mcp.json stuck as "Pending approval". Run claude interactively — an approval prompt will appear.

Quick recall
Почему в .mcp.json (project scope) можно коммитить конфиг, а секреты передавать через переменные окружения?
Quick recall
Какой скоуп используется по умолчанию, если при подключении MCP-сервера не указан `--scope`?
Quick recall
Зачем нужно двойное тире `--` при подключении stdio-сервера через `claude mcp add`?

See also

  • Model Context Protocol: архитектура и основы — primitives, transports, and MCP's place in the ecosystem
  • Создание собственного MCP-сервера — FastMCP, SDK, MCP Inspector
  • Практика: GitHub, базы данных и веб-API через MCP — real-world integrations and security patterns
  • Hooks — события жизненного цикла — deterministic hooks as an alternative to MCP for local automation
  • Что выбрать: команда, навык, субагент, MCP или хук — when MCP is the only right choice
  • Настройки и иерархия конфигурации — how MCP scopes fit into the overall Claude Code settings hierarchy

Sources

  1. Connect Claude Code to tools via MCP — официальная документация