skip to content

Claude Code MCP Servers

Add, configure, and build Model Context Protocol (MCP) servers for Claude Code — HTTP, SSE, and stdio transports, management commands, permission rules, and a curated list of useful servers.

10 min read 53 snippets deep dive

Claude Code MCP Servers#

What it is#

MCP (Model Context Protocol) is an open standard by Anthropic for connecting AI models to external tools, data sources, and resources through a common JSON-RPC interface. Claude Code acts as an MCP client — you register MCP servers that expose additional tools (databases, APIs, GitHub, Slack, custom scripts), and Claude can call them exactly like its built-in tools during a session. Reach for MCP when the built-in Read/Edit/Bash/WebFetch tools are insufficient and you need Claude to interact with proprietary systems or specialized external APIs. The closest alternative is exposing the same functionality via a shell script that the Bash tool calls, which is simpler but lacks structured input schemas and discoverability.

How it works#

Without MCP, Claude Code has built-in tools: Read, Edit, Write, Bash, Glob, Grep, WebSearch, WebFetch. With an MCP server, you add arbitrary new tools — databases, APIs, Slack, GitHub, custom scripts — and Claude can call them like any other tool.

Claude Code (MCP client)
    ↕  MCP protocol (JSON-RPC over stdio / SSE / HTTP)
MCP Server (exposes tools)
    ↕  native API
Postgres / GitHub / Slack / your service

Each registered server contributes a set of tools that show up alongside the built-ins. Tool names are prefixed mcp__<server-name>__<tool-name> so they cannot collide with built-ins or with each other.

Transport methods#

MCP supports three transports. Pick by latency requirements and where the server lives.

TransportWhen to useLatencySetup
stdioLocal processes, CLI toolsLowestSimplest
sse (Server-Sent Events)Remote servers, shared team serversMediumNeeds HTTP server
httpREST-compatible MCP serversMediumNeeds HTTP server

stdio is the default and most common — Claude Code spawns the server as a subprocess and talks JSON-RPC over its stdin/stdout. sse and http are for servers running elsewhere (or shared across a team) and require the server to expose an authenticated HTTP endpoint.

Add a server — CLI#

The claude mcp add subcommand registers an MCP server in your user-global config and immediately validates that it starts and lists tools.

# Add a stdio server (local process)
claude mcp add my-server -- node /path/to/server.js

# Add an SSE server (remote)
claude mcp add my-remote-server --transport sse https://mcp.example.com/sse

# Add an HTTP server (remote)
claude mcp add my-http --transport http https://mcp.example.com/rpc

# Add with environment variables
claude mcp add my-db --env DATABASE_URL=postgresql://localhost/mydb -- node /path/to/db-server.js

Output:

✅ Added MCP server "my-server" (8 tools)

The trailing -- separates claude mcp add’s own flags from the command to run. Everything after -- is the server command line.

List connected servers#

claude mcp list

Output:

my-server       stdio   node /path/to/server.js                          ✅ connected (8 tools)
github          stdio   npx @modelcontextprotocol/server-github          ✅ connected (12 tools)
postgres        stdio   npx @modelcontextprotocol/server-postgres        ✅ connected (3 tools)
remote-svc      sse     https://mcp.example.com/sse                      ❌ disconnected

Inspect a server’s tools#

claude mcp tools github

Output:

github exposes 12 tools:
  search_repositories(query: string, sort?: string, order?: string)
  get_file_contents(owner: string, repo: string, path: string, branch?: string)
  create_or_update_file(...)
  create_issue(...)
  create_pull_request(...)
  list_commits(...)
  ...

Remove a server#

claude mcp remove my-server

Output:

Removed MCP server "my-server".

Restart a server#

If a server hangs or you change its environment, restart it without exiting Claude Code:

claude mcp restart github

Output:

Restarted "github" (12 tools available).

MCP in settings.json#

For team-shared or committed server configs, define them in .claude/settings.json under mcpServers. These are picked up by every session opened in the project.

{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
      }
    },
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres", "${DATABASE_URL}"]
    },
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/alice/Code"]
    },
    "remote": {
      "type": "sse",
      "url": "https://mcp.example.com/sse",
      "headers": {
        "Authorization": "Bearer ${MCP_API_KEY}"
      }
    }
  }
}

[!TIP] Use ${ENV_VAR} syntax in settings.json to reference environment variables without hardcoding secrets. The value is expanded at runtime from the shell environment that launched claude.

Useful MCP servers#

The MCP ecosystem ships dozens of community servers. The ones below are the most common.

GitHub#

Gives Claude tools to search repos, read files, create issues, open PRs, and review code — all authenticated with your GitHub token.

claude mcp add github -- npx -y @modelcontextprotocol/server-github

Output:

✅ Added MCP server "github" (12 tools)

Required env: GITHUB_PERSONAL_ACCESS_TOKEN

Tools added: create_or_update_file, search_repositories, create_issue, create_pull_request, list_commits, get_file_contents, and more.

PostgreSQL#

Read-only access to a Postgres database — schema inspection, query execution, table listings.

claude mcp add postgres -- npx -y @modelcontextprotocol/server-postgres "$DATABASE_URL"

Output:

✅ Added MCP server "postgres" (3 tools)

Tools added: query, list_tables, describe_table.

Example use in-session:

> What are the 5 most recently created users in the database?

Output:

[Calls mcp__postgres__query with: SELECT id, email, created_at FROM users ORDER BY created_at DESC LIMIT 5]

Filesystem (extended)#

Extends Claude’s file access beyond the current working directory. Useful for referencing shared docs or a monorepo root from a subdirectory.

claude mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /path/to/root

Output:

✅ Added MCP server "filesystem" (6 tools)

Fetch (web scraping)#

Gives Claude a fetch tool for retrieving web pages as plain text. Useful for pulling documentation from URLs during a session without the limits of WebFetch.

claude mcp add fetch -- npx -y @modelcontextprotocol/server-fetch

Output:

✅ Added MCP server "fetch" (1 tool)

Replaces or complements the built-in WebSearch with Brave Search results, which can be more complete.

claude mcp add brave-search -- npx -y @modelcontextprotocol/server-brave-search

Output:

✅ Added MCP server "brave-search" (1 tool)

Required env: BRAVE_API_KEY (free tier available)

Slack#

Read channels, post messages, and search Slack history.

claude mcp add slack -- npx -y @modelcontextprotocol/server-slack

Output:

✅ Added MCP server "slack" (4 tools)

Required env: SLACK_BOT_TOKEN, SLACK_TEAM_ID

Memory (persistent)#

Gives Claude a key-value store that persists between sessions. Claude can save and recall facts using store_memory and retrieve_memory tools.

claude mcp add memory -- npx -y @modelcontextprotocol/server-memory

Output:

✅ Added MCP server "memory" (2 tools)

Puppeteer (headless browser)#

Drive a real browser for scraping or testing — navigate, click, fill forms, screenshot.

claude mcp add puppeteer -- npx -y @modelcontextprotocol/server-puppeteer

Output:

✅ Added MCP server "puppeteer" (8 tools)

SQLite#

Read/write access to a SQLite file. Great for local prototyping and analytics workflows.

claude mcp add sqlite -- npx -y @modelcontextprotocol/server-sqlite /path/to/db.sqlite

Output:

✅ Added MCP server "sqlite" (4 tools)

Build your own MCP server#

A minimal stdio MCP server in Python. The server reads JSON-RPC requests from stdin and writes responses to stdout.

# my_server.py
import json, sys

def handle(request):
    method = request["method"]
    if method == "initialize":
        return {"protocolVersion": "2024-11-05", "capabilities": {"tools": {}}, "serverInfo": {"name": "ci", "version": "0.1"}}
    if method == "tools/list":
        return {
            "tools": [{
                "name": "get_build_status",
                "description": "Get the current CI build status for a branch.",
                "inputSchema": {
                    "type": "object",
                    "properties": {
                        "branch": {"type": "string", "description": "Git branch name"}
                    },
                    "required": ["branch"]
                }
            }]
        }
    if method == "tools/call":
        if request["params"]["name"] == "get_build_status":
            branch = request["params"]["arguments"]["branch"]
            # Replace with real CI API call
            return {"content": [{"type": "text", "text": f"Branch '{branch}': passing"}]}
    return {"error": {"code": -32601, "message": "Method not found"}}

for line in sys.stdin:
    if not line.strip():
        continue
    req = json.loads(line)
    result = handle(req)
    response = {"jsonrpc": "2.0", "id": req.get("id"), "result": result}
    sys.stdout.write(json.dumps(response) + "\n")
    sys.stdout.flush()

Register it:

claude mcp add ci-status -- python3 /path/to/my_server.py

Output:

✅ Added MCP server "ci-status" (1 tool)

Now Claude can call get_build_status(branch="main") during a session.

Using the official SDKs#

Anthropic publishes MCP SDKs for Python and TypeScript that handle the JSON-RPC plumbing for you. They’re worth using once your server has more than two or three tools.

# Python
pip install mcp

# TypeScript
npm install @modelcontextprotocol/sdk

Output:

Successfully installed mcp-1.x.x

A Python SDK-based server is much shorter than the raw version above:

from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent

app = Server("ci")

@app.list_tools()
async def list_tools():
    return [Tool(
        name="get_build_status",
        description="Get the current CI build status for a branch.",
        inputSchema={"type": "object", "properties": {"branch": {"type": "string"}}, "required": ["branch"]},
    )]

@app.call_tool()
async def call_tool(name, arguments):
    branch = arguments["branch"]
    return [TextContent(type="text", text=f"Branch '{branch}': passing")]

if __name__ == "__main__":
    import asyncio
    asyncio.run(stdio_server(app))

Output: (none — runs as a stdio MCP server)

Check MCP status in-session#

> /mcp

Output:

Connected MCP servers:
  github      (12 tools)  ✅
  postgres    (3 tools)   ✅
  memory      (2 tools)   ✅

MCP permission rules#

MCP tools follow the same permission system as built-in tools. Deny or allow specific MCP tools in settings.json. Tool names follow the pattern mcp__<server-name>__<tool-name>.

{
  "permissions": {
    "allow": [
      "mcp__github__search_repositories",
      "mcp__github__get_file_contents",
      "mcp__postgres__query",
      "mcp__memory__retrieve_memory"
    ],
    "deny": [
      "mcp__github__create_pull_request",
      "mcp__github__delete_file",
      "mcp__slack__post_message"
    ]
  }
}

Wildcards work the same way as for built-in tools:

{
  "permissions": {
    "allow": ["mcp__postgres__*"],
    "deny":  ["mcp__github__delete_*", "mcp__github__create_pull_request"]
  }
}

Authentication patterns#

MCP servers that talk to external services need credentials. There are three common patterns:

Environment variables passed via settings.json#

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

Secret-manager-backed env via apiKeyHelper-style script#

{
  "mcpServers": {
    "github": {
      "command": "bash",
      "args": ["-c", "GITHUB_PERSONAL_ACCESS_TOKEN=$(op read op://Personal/GitHub/token) exec npx -y @modelcontextprotocol/server-github"]
    }
  }
}

OAuth for remote (SSE/HTTP) servers#

Some MCP servers (Gmail, Google Calendar, Mintlify) implement an OAuth flow. The first call to a tool from such a server triggers an authenticate step that returns a URL; the user opens it, completes consent, and the server stores the resulting token.

> /mcp tools gmail
gmail: 2 tools
  authenticate()                ← call this first
  send_email(to, subject, body)

Output:

> Use gmail/authenticate, then send a test email to alice@example.com
[Calls mcp__gmail__authenticate → returns URL → user completes → token stored]

Common pitfalls#

  1. Server not startingclaude mcp add validates startup but doesn’t always surface the error; run the server command manually and watch stderr.
  2. Env vars not expanded${VAR} only expands inside mcpServers.*.env and mcpServers.*.args; literal $VAR strings are passed through unchanged.
  3. Tool name length — MCP tool names are namespaced mcp__<server>__<tool>; servers with long names produce unwieldy permission entries — keep server names short (≤8 chars).
  4. Permission deny for a server prefix — there’s no mcp__github__* deny rule for ALL tools; you must list them individually or use a glob if your version supports it.
  5. npx -y cachingnpx -y re-downloads the package on every cold start; for slow networks, install globally with npm i -g @modelcontextprotocol/server-github and reference it directly.
  6. Server crashes mid-session — when a server dies, Claude sees tool call failures but doesn’t auto-restart; check /mcp and run claude mcp restart <name>.
  7. Shared SSE server auth tokens leak — never commit Authorization headers to .claude/settings.json; use .claude/settings.local.json or ${ENV_VAR}.

Real-world recipes#

Database-aware code review#

Add a Postgres MCP server in read-only mode so Claude can verify migrations against the live schema during reviews.

{
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres", "${READONLY_DATABASE_URL}"]
    }
  },
  "permissions": {
    "allow": ["mcp__postgres__query", "mcp__postgres__list_tables", "mcp__postgres__describe_table"],
    "deny":  ["mcp__postgres__execute"]
  }
}

Team-wide GitHub server#

A shared SSE MCP server lets the whole team share one access token and audit log. Run it on an internal host and reference it from each developer’s settings.

{
  "mcpServers": {
    "team-github": {
      "type": "sse",
      "url": "https://mcp.internal.example.com/github/sse",
      "headers": {"Authorization": "Bearer ${TEAM_MCP_TOKEN}"}
    }
  }
}

Custom CI status server#

Wire your build system into Claude with a small Python MCP server. Useful when Claude is iterating on a flaky test and needs to verify whether main is green.

claude mcp add ci-status -- python3 ~/bin/ci-mcp-server.py

Output:

✅ Added MCP server "ci-status" (3 tools)

Then in-session:

> Is the build green on main?
[Calls mcp__ci-status__get_build_status(branch="main") → "Branch 'main': passing"]

Memory across sessions#

Use the memory MCP server as Claude’s “scratchpad that survives /clear”. Drop key facts there so the next session can pick up where this one left off.

> Save to memory: the auth module uses bcrypt rounds=12, never lower.
[Calls mcp__memory__store_memory]

Output:

Stored.