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.
| Transport | When to use | Latency | Setup |
|---|---|---|---|
stdio | Local processes, CLI tools | Lowest | Simplest |
sse (Server-Sent Events) | Remote servers, shared team servers | Medium | Needs HTTP server |
http | REST-compatible MCP servers | Medium | Needs 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 launchedclaude.
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)
Brave Search#
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#
- Server not starting —
claude mcp addvalidates startup but doesn’t always surface the error; run the server command manually and watch stderr. - Env vars not expanded —
${VAR}only expands insidemcpServers.*.envandmcpServers.*.args; literal$VARstrings are passed through unchanged. - 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). - 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. npx -ycaching —npx -yre-downloads the package on every cold start; for slow networks, install globally withnpm i -g @modelcontextprotocol/server-githuband reference it directly.- Server crashes mid-session — when a server dies, Claude sees tool call failures but doesn’t auto-restart; check
/mcpand runclaude mcp restart <name>. - Shared SSE server auth tokens leak — never commit
Authorizationheaders to.claude/settings.json; use.claude/settings.local.jsonor${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.