Level 4: MCP Servers (Connecting Claude Code to Everything)
The definitive guide to the Model Context Protocol -- from understanding the architecture to connecting Claude Code to your databases, issue trackers, design tools, and APIs.
Table of Contents
- Overview & Goal
- How MCP Works
- Adding MCP Servers
- MCP Scopes & Configuration Files
- Managing MCP Servers
- Referencing MCP Resources
- Popular MCP Integrations
- Setting Up Your First MCP Server
- Subagent MCP Access
- Security & Permissions
- Team Setup Patterns
- Building Your Own MCP Server
- Exercises
- Pro Tips from Boris Cherny
- Anti-Patterns
- Official Documentation Links
1. Overview & Goal
What is MCP?
MCP stands for Model Context Protocol. It is an open-source standard created by Anthropic that defines how AI tools communicate with external services. Think of it as a universal adapter -- a single protocol that lets Claude Code talk to hundreds of different tools, databases, and APIs without needing a custom integration for each one.
The "Bridge" Analogy
Without MCP, your workflow looks like this:
You -> open Sentry -> copy error details -> paste into Claude Code -> read suggestion
You -> open GitHub -> copy PR diff -> paste into Claude Code -> read review
You -> open database tool -> run query -> copy results -> paste into Claude Code -> analyze
With MCP, Claude Code crosses the bridge directly:
You -> "Check Sentry for the top errors, review PR #456 on GitHub,
and query our database for affected users"
Claude Code -> [MCP] -> Sentry, GitHub, PostgreSQL -> combined analysis
No copy-pasting. No context switching. Claude reads from and writes to your tools directly.
Why MCP Matters
- Eliminates context switching -- Claude reads from your actual tools instead of you copying data into prompts.
- Enables end-to-end workflows -- "Find the bug in Sentry, fix it in code, create a PR on GitHub, and post a summary in Slack."
- Standardized protocol -- One protocol works for every tool. Server authors implement MCP once, and every MCP client (Claude Code, Claude Desktop, third-party tools) can use it.
- Team sharing -- Define your MCP servers in
.mcp.json, commit it to git, and every team member gets the same integrations automatically.
What You Will Learn
By the end of this level, you will be able to:
- Explain the MCP architecture (client, server, transports, primitives)
- Add remote and local MCP servers to Claude Code
- Configure servers at the right scope (local, project, user)
- Share MCP configurations with your team via
.mcp.json - Reference MCP resources using
@syntax - Set up integrations with GitHub, databases, Notion, Slack, and more
- Manage security and permissions for MCP servers
- Build a basic MCP server of your own
2. How MCP Works
The Architecture
MCP follows a client-server architecture:
+------------------+ MCP Protocol +------------------+
| | <----------------------> | |
| Claude Code | (stdio, HTTP, or SSE) | MCP Server |
| (MCP Client) | | (e.g., GitHub) |
| | | |
+------------------+ +------------------+
| |
Your Terminal External Service
(GitHub API, DB,
Slack, etc.)
Client (Claude Code): Discovers available MCP servers, calls their tools, reads their resources, and executes their prompts.
Server (MCP Server): A lightweight process or remote endpoint that exposes tools, resources, and prompts from an external service. The server handles authentication, API calls, and data formatting.
Transport: The communication channel between client and server. MCP supports three transport types.
Transport Types
| Transport | How It Works | Best For | Example |
|---|---|---|---|
| stdio | Server runs as a local child process. Claude Code communicates via stdin/stdout. | Local tools, CLI wrappers, filesystem access, custom scripts | npx -y @modelcontextprotocol/server-filesystem |
| HTTP (streamable) | Server is a remote HTTPS endpoint. Claude Code sends HTTP requests. Recommended for remote servers. | Cloud services, SaaS integrations, team-shared servers | https://mcp.notion.com/mcp |
| SSE (deprecated) | Server is a remote endpoint using Server-Sent Events. | Legacy remote servers. Use HTTP instead for new setups. | https://mcp.asana.com/sse |
When to use which:
- stdio -- When the server needs to run on your machine (filesystem access, local databases, custom scripts).
- HTTP -- When connecting to a cloud service (GitHub, Notion, Sentry, Slack). This is the modern standard.
- SSE -- Only if the server does not support HTTP transport yet. SSE is officially deprecated.
The 3 MCP Primitives
Every MCP server can expose three types of capabilities:
1. Tools
Tools are functions that Claude can call to perform actions. They are the most commonly used primitive.
Examples:
github.create_issue(title, body)-- Create a GitHub issuepostgres.query(sql)-- Run a SQL queryslack.send_message(channel, text)-- Send a Slack message
When Claude needs to interact with an external service, it calls the appropriate MCP tool. You see a permission prompt before any tool executes.
2. Resources
Resources are read-only data that Claude can access. They represent structured information from external services.
Examples:
github:issue://123-- The content of GitHub issue #123postgres:schema://users-- The schema of the users tabledocs:file://api/authentication-- API documentation
Resources are referenced using the @ syntax (covered in Section 6).
3. Prompts
Prompts are predefined templates exposed by MCP servers. They appear as slash commands in Claude Code.
Examples:
/mcp__github__list_prs-- List open pull requests/mcp__jira__create_issue "Title" high-- Create a Jira issue/mcp__sentry__recent_errors-- Show recent errors
Type / in Claude Code to see all available MCP prompts.
How Claude Discovers and Uses MCP Tools
- Startup: When Claude Code launches, it connects to all configured MCP servers.
- Discovery: Claude Code asks each server "What tools, resources, and prompts do you offer?"
- Tool list: The available MCP tools are added to Claude's tool inventory alongside built-in tools (Read, Write, Edit, Bash, etc.).
- Invocation: When you ask Claude a question that requires external data, Claude identifies the relevant MCP tool and calls it.
- Permission: You see a permission prompt showing what Claude wants to do. You approve or deny.
- Execution: The MCP server processes the request, calls the external API, and returns the result.
- Response: Claude uses the result to formulate its answer.
Dynamic Tool Updates
MCP servers can send list_changed notifications to inform Claude Code that their available tools have changed. When this happens, Claude Code automatically refreshes the tool list without requiring you to disconnect and reconnect.
MCP Tool Search
When you have many MCP servers configured, tool definitions can consume a significant portion of your context window. Claude Code automatically enables Tool Search when MCP tool descriptions exceed 10% of the context window. Instead of loading all tools upfront, Claude searches for relevant tools on demand.
You can control this behavior with the ENABLE_TOOL_SEARCH environment variable:
| Value | Behavior |
|---|---|
auto |
Activates when tools exceed 10% of context (default) |
auto:<N> |
Activates at custom threshold (e.g., auto:5 for 5%) |
true |
Always enabled |
false |
Disabled; all MCP tools loaded upfront |
# Custom 5% threshold
ENABLE_TOOL_SEARCH=auto:5 claude
# Disable tool search entirely
ENABLE_TOOL_SEARCH=false claude
Tool Search requires Sonnet 4 or later, or Opus 4 or later. Haiku models do not support it.
3. Adding MCP Servers
Method 1: CLI -- claude mcp add
The primary way to add MCP servers is the claude mcp add command.
Adding a Remote HTTP Server (Recommended for Cloud Services)
# Basic syntax
claude mcp add --transport http <name> <url>
# Example: Connect to Notion
claude mcp add --transport http notion https://mcp.notion.com/mcp
# Example: Connect to GitHub
claude mcp add --transport http github https://api.githubcopilot.com/mcp/
# Example: Connect to Sentry
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp
Adding a Remote SSE Server (Deprecated -- Use HTTP Instead)
# Basic syntax
claude mcp add --transport sse <name> <url>
# Example: Connect to Asana
claude mcp add --transport sse asana https://mcp.asana.com/sse
Adding a Local stdio Server
# Basic syntax
claude mcp add --transport stdio <name> -- <command> [args...]
# Example: Filesystem server
claude mcp add --transport stdio filesystem -- npx -y @modelcontextprotocol/server-filesystem /path/to/dir
# Example: PostgreSQL via dbhub
claude mcp add --transport stdio db -- npx -y @bytebase/dbhub \
--dsn "postgresql://readonly:pass@localhost:5432/mydb"
# Example: Memory server
claude mcp add --transport stdio memory -- npx -y @modelcontextprotocol/server-memory
Important: Option Ordering
All flags (--transport, --env, --scope, --header) must come before the server name. The -- (double dash) separates the server name from the command and its arguments.
# Correct ordering:
claude mcp add --transport stdio --env API_KEY=abc123 myserver -- npx -y my-server-package
# WRONG -- flags after the name will be passed to the server command:
claude mcp add myserver --transport stdio -- npx -y my-server-package
Adding with Environment Variables
Use --env flags to pass environment variables to stdio servers:
# Single env var
claude mcp add --transport stdio --env AIRTABLE_API_KEY=key123 airtable \
-- npx -y airtable-mcp-server
# Multiple env vars
claude mcp add --transport stdio \
--env DB_HOST=localhost \
--env DB_PORT=5432 \
--env DB_NAME=analytics \
db-server -- npx -y @company/db-mcp
Adding with Authentication Headers
Use --header flags for servers that require API keys or tokens:
# Bearer token authentication
claude mcp add --transport http secure-api https://api.example.com/mcp \
--header "Authorization: Bearer your-token-here"
# API key authentication
claude mcp add --transport sse private-api https://api.company.com/sse \
--header "X-API-Key: your-key-here"
# Multiple headers
claude mcp add --transport http custom-api https://api.example.com/mcp \
--header "Authorization: Bearer token123" \
--header "X-Org-Id: org-456"
Adding with Scope
Use --scope to control where the configuration is stored:
# Local scope (default) -- just you, this project
claude mcp add --transport http stripe https://mcp.stripe.com
# Project scope -- creates/updates .mcp.json (committed to git, shared with team)
claude mcp add --transport http paypal --scope project https://mcp.paypal.com/mcp
# User scope -- available across all your projects
claude mcp add --transport http hubspot --scope user https://mcp.hubspot.com/anthropic
Method 2: Adding from JSON Configuration
If you have a JSON configuration, use claude mcp add-json:
# HTTP server with headers
claude mcp add-json weather-api \
'{"type":"http","url":"https://api.weather.com/mcp","headers":{"Authorization":"Bearer token"}}'
# stdio server with environment variables
claude mcp add-json local-db \
'{"type":"stdio","command":"npx","args":["-y","@bytebase/dbhub","--dsn","postgresql://localhost/mydb"],"env":{"CACHE_DIR":"/tmp"}}'
# HTTP server with pre-configured OAuth
claude mcp add-json my-server \
'{"type":"http","url":"https://mcp.example.com/mcp","oauth":{"clientId":"your-client-id","callbackPort":8080}}' \
--client-secret
Method 3: The /mcp Interactive Command
Inside a Claude Code session, type /mcp to get an interactive management interface. This is especially useful for:
- Viewing server status -- See which servers are connected, errored, or disconnected
- Authenticating -- Complete OAuth flows for servers that require login
- Clearing authentication -- Revoke OAuth tokens
- Troubleshooting -- Check connection health
> /mcp
MCP Servers:
github [connected] 3 tools, 2 resources
notion [connected] 5 tools, 1 resource
sentry [auth required] -- Select to authenticate
postgres [error] Connection refused
Method 4: Importing from Claude Desktop
If you already have MCP servers configured in Claude Desktop, import them directly:
# Import servers interactively (select which ones to bring over)
claude mcp add-from-claude-desktop
# Import to user scope (available across all projects)
claude mcp add-from-claude-desktop --scope user
This reads the Claude Desktop configuration file from its standard location and presents an interactive dialog to select which servers to import.
Limitations:
- Only works on macOS and Windows Subsystem for Linux (WSL)
- Servers get the same names as in Claude Desktop
- If a name collision occurs, the imported server gets a numeric suffix (e.g.,
server_1)
OAuth Authentication Flow
Many cloud-based MCP servers require OAuth 2.0 authentication:
Step 1: Add the server:
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp
Step 2: Authenticate inside Claude Code:
> /mcp
# Select the server -> Authenticate
# A browser window opens for you to log in
# After login, tokens are stored securely and refreshed automatically
Step 3: Use it:
> What are the most common errors in the last 24 hours?
# Claude calls the Sentry MCP tools automatically
Pre-Configured OAuth Credentials
Some servers do not support automatic OAuth (dynamic client registration). You will see an error like "Incompatible auth server: does not support dynamic client registration." In this case, register an OAuth app through the service's developer portal first:
# Using CLI flags
claude mcp add --transport http \
--client-id your-client-id --client-secret --callback-port 8080 \
my-server https://mcp.example.com/mcp
# Using JSON config
claude mcp add-json my-server \
'{"type":"http","url":"https://mcp.example.com/mcp","oauth":{"clientId":"your-client-id","callbackPort":8080}}' \
--client-secret
# Non-interactive (CI/CD): pass secret via environment variable
MCP_CLIENT_SECRET=your-secret claude mcp add --transport http \
--client-id your-client-id --client-secret --callback-port 8080 \
my-server https://mcp.example.com/mcp
The client secret is stored in your system keychain (macOS) or a credentials file -- not in your configuration files.
4. MCP Scopes & Configuration Files
The Three Scopes
| Scope | Storage Location | Who Can See It | Use Case |
|---|---|---|---|
| Local (default) | ~/.claude.json under your project path |
Only you, only this project | Personal dev servers, experimental configs, sensitive credentials |
| Project | .mcp.json in project root |
Everyone on the team (committed to git) | Team-shared integrations, standard tooling |
| User | ~/.claude.json (global section) |
Only you, across all projects | Personal utility servers you use everywhere |
Scope Precedence
When servers with the same name exist at multiple scopes, Claude Code resolves them in this order:
1. Local (highest priority)
2. Project (.mcp.json)
3. User (lowest priority)
This means you can override a team-shared server configuration with your own local version.
The .mcp.json File (Project Scope)
This is the most important file for team collaboration. It lives at the root of your project and should be committed to git.
Complete .mcp.json Format
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/"
},
"notion": {
"type": "http",
"url": "https://mcp.notion.com/mcp"
},
"sentry": {
"type": "http",
"url": "https://mcp.sentry.dev/mcp"
},
"database": {
"command": "npx",
"args": ["-y", "@bytebase/dbhub", "--dsn", "${DATABASE_URL}"],
"env": {
"NODE_ENV": "development"
}
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "${PROJECT_ROOT:-/tmp}"],
"env": {}
},
"custom-server": {
"command": "${HOME}/tools/my-mcp-server",
"args": ["--config", "${CONFIG_PATH:-./config.json}"],
"env": {
"API_KEY": "${MY_API_KEY}",
"LOG_LEVEL": "${LOG_LEVEL:-info}"
}
}
}
}
Environment Variable Expansion
Claude Code expands environment variables in .mcp.json files. This lets you commit the file to git while keeping secrets out of version control.
Supported syntax:
| Syntax | Behavior |
|---|---|
${VAR} |
Expands to the value of VAR. Fails if VAR is not set. |
${VAR:-default} |
Expands to VAR if set, otherwise uses default. |
Where expansion works:
command-- The server executable pathargs-- Command-line argumentsenv-- Environment variables passed to the serverurl-- For HTTP/SSE serversheaders-- For HTTP server authentication
Example with variable expansion:
{
"mcpServers": {
"api-server": {
"type": "http",
"url": "${API_BASE_URL:-https://api.example.com}/mcp",
"headers": {
"Authorization": "Bearer ${API_KEY}"
}
}
}
}
If API_KEY is not set and has no default, Claude Code will fail to parse the config. Always use defaults for optional variables.
Settings.json MCP-Related Keys
These keys control MCP behavior in your settings.json files (.claude/settings.json, .claude/settings.local.json, or ~/.claude/settings.json):
| Key | Type | Description |
|---|---|---|
enableAllProjectMcpServers |
boolean | Auto-approve all MCP servers in .mcp.json without prompting |
enabledMcpjsonServers |
array of strings | Specific server names from .mcp.json to auto-approve |
disabledMcpjsonServers |
array of strings | Specific server names from .mcp.json to reject |
Example: Auto-approve all project MCP servers
{
"enableAllProjectMcpServers": true
}
Example: Selectively approve project MCP servers
{
"enabledMcpjsonServers": ["github", "notion", "sentry"],
"disabledMcpjsonServers": ["filesystem"]
}
Resetting Project Approval Choices
If you need to re-evaluate which project-scoped servers to approve:
claude mcp reset-project-choices
5. Managing MCP Servers
Listing Servers
# List all configured servers
claude mcp list
# Output shows name, transport, scope, and status
# Example output:
# github http local https://api.githubcopilot.com/mcp/
# notion http project https://mcp.notion.com/mcp
# database stdio user npx -y @bytebase/dbhub
Getting Server Details
# Get full details for a specific server
claude mcp get github
# Shows: name, transport, URL/command, scope, env vars, auth status
Removing Servers
# Remove a server by name
claude mcp remove github
# Remove from a specific scope
claude mcp remove --scope user github
Enabling and Disabling Project Servers
In settings.json:
{
"enabledMcpjsonServers": ["github", "sentry"],
"disabledMcpjsonServers": ["dangerous-server"]
}
Or auto-approve everything:
{
"enableAllProjectMcpServers": true
}
Checking Server Status (In-Session)
Inside Claude Code, use /mcp to see real-time server status:
> /mcp
MCP Servers:
github [connected] 12 tools
notion [connected] 5 tools, 3 resources
sentry [auth required]
database [error: connection refused]
Troubleshooting Connections
Server shows "error":
- Check that the server process can start: run the command manually in your terminal.
- Verify environment variables are set:
echo $MY_API_KEY - Check network connectivity for remote servers.
- Look at Claude Code logs for detailed error messages.
Server shows "auth required":
- Run
/mcpinside Claude Code. - Select the server and choose "Authenticate."
- Complete the browser-based OAuth flow.
Server times out on startup:
Configure the startup timeout using the MCP_TIMEOUT environment variable:
# Set a 10-second timeout (default is lower)
MCP_TIMEOUT=10000 claude
Large outputs cause warnings: When MCP tool output exceeds 10,000 tokens, Claude Code displays a warning. Increase the limit if needed:
# Allow up to 50,000 tokens per MCP tool output
MAX_MCP_OUTPUT_TOKENS=50000 claude
Windows-specific issues:
On native Windows (not WSL), stdio servers using npx require the cmd /c wrapper:
claude mcp add --transport stdio my-server -- cmd /c npx -y @some/package
6. Referencing MCP Resources
The @ Syntax
MCP servers can expose resources -- read-only data like files, database schemas, issue contents, and more. You reference these using @ mentions:
@server:protocol://resource/path
Using Resources in Prompts
Single resource:
> Can you analyze @github:issue://123 and suggest a fix?
Multiple resources:
> Compare @postgres:schema://users with @docs:file://database/user-model
Discovering resources:
Type @ in your prompt to see available resources from all connected MCP servers. Resources appear alongside local files in the autocomplete menu.
Resource Types
Resources can contain any type of content:
| Resource Type | Example | What It Contains |
|---|---|---|
| GitHub issues | @github:issue://456 |
Issue title, body, comments, labels |
| Database schemas | @postgres:schema://users |
Table definition, columns, types, constraints |
| API documentation | @docs:file://api/auth |
Endpoint descriptions, parameters, examples |
| Notion pages | @notion:page://abc123 |
Page content in structured format |
| File content | @filesystem:file:///path/to/file |
Raw file content |
How Resources Work Under the Hood
- When you type
@, Claude Code queries all connected MCP servers for their available resources. - You select a resource from the autocomplete.
- Claude Code fetches the resource content from the MCP server.
- The content is attached to your prompt as context.
- Claude uses the resource content alongside your question.
7. Popular MCP Integrations
Integration Quick Reference Table
| Integration | Transport | Setup Command | Use Cases |
|---|---|---|---|
| GitHub | HTTP | claude mcp add --transport http github https://api.githubcopilot.com/mcp/ |
PR reviews, issues, code search, repo management |
| Notion | HTTP | claude mcp add --transport http notion https://mcp.notion.com/mcp |
Page reading/writing, database queries, knowledge base |
| Slack | HTTP | claude mcp add --transport http slack https://slack-mcp.anthropic.com/mcp |
Read/send messages, channel management, search |
| Sentry | HTTP | claude mcp add --transport http sentry https://mcp.sentry.dev/mcp |
Error monitoring, stack traces, release tracking |
| Linear | HTTP | claude mcp add --transport http linear https://mcp.linear.app/sse |
Issue tracking, project management, sprint planning |
| Asana | SSE | claude mcp add --transport sse asana https://mcp.asana.com/sse |
Task management, project tracking |
| PostgreSQL | stdio | claude mcp add --transport stdio db -- npx -y @bytebase/dbhub --dsn "postgresql://..." |
Database queries, schema exploration, data analysis |
| Filesystem | stdio | claude mcp add --transport stdio fs -- npx -y @modelcontextprotocol/server-filesystem /path |
Local file access beyond the project directory |
| Memory | stdio | claude mcp add --transport stdio memory -- npx -y @modelcontextprotocol/server-memory |
Persistent knowledge across sessions |
| Brave Search | stdio | claude mcp add --transport stdio --env BRAVE_API_KEY=key brave -- npx -y @anthropic/mcp-server-brave-search |
Web search, research |
| Puppeteer | stdio | claude mcp add --transport stdio puppeteer -- npx -y @anthropic/mcp-server-puppeteer |
Browser automation, scraping, testing |
| Figma | HTTP | See Figma developer docs for MCP URL | Design specs, component inspection, asset export |
| Google Drive | stdio | claude mcp add --transport stdio --env GOOGLE_CREDENTIALS=path gdrive -- npx -y @anthropic/mcp-server-gdrive |
Docs, sheets, file management |
| AirTable | stdio | claude mcp add --transport stdio --env AIRTABLE_API_KEY=key airtable -- npx -y airtable-mcp-server |
Record management, base queries, automations |
| Stripe | HTTP | claude mcp add --transport http stripe https://mcp.stripe.com |
Payment data, customer info, subscription management |
| PayPal | HTTP | claude mcp add --transport http paypal https://mcp.paypal.com/mcp |
Payment processing, transaction history |
| HubSpot | HTTP | claude mcp add --transport http hubspot https://mcp.hubspot.com/anthropic |
CRM data, contacts, deals |
| Cloudflare | HTTP | claude mcp add --transport http cloudflare https://mcp.cloudflare.com/sse |
Workers, DNS, analytics |
| Grafana | HTTP | claude mcp add --transport http grafana https://mcp.grafana.com/mcp |
Dashboards, metrics, alerting |
Detailed Integration: GitHub
Setup:
claude mcp add --transport http github https://api.githubcopilot.com/mcp/
Authenticate:
> /mcp
# Select GitHub -> Authenticate -> Log in via browser
What you can do:
> "Review PR #456 and suggest improvements"
> "Create a new issue for the login bug we found"
> "Show me all open PRs assigned to me"
> "List recent commits on the main branch"
> "Search the codebase for uses of deprecated API"
Detailed Integration: PostgreSQL / Databases
Setup:
# Using dbhub (supports PostgreSQL, MySQL, SQLite)
claude mcp add --transport stdio db -- npx -y @bytebase/dbhub \
--dsn "postgresql://readonly:[email protected]:5432/analytics"
What you can do:
> "Show me the schema for the orders table"
> "What's our total revenue this month?"
> "Find customers who haven't purchased in 90 days"
> "How many users signed up last week, broken down by referral source?"
Security note: Always use a read-only database user for MCP database connections. Never expose write credentials through MCP.
Detailed Integration: Notion
Setup:
claude mcp add --transport http notion https://mcp.notion.com/mcp
Authenticate via /mcp in Claude Code.
What you can do:
> "Find the product roadmap page in Notion and summarize the Q2 priorities"
> "Create a new page in the Engineering wiki about our auth system"
> "Query the bugs database for all P0 items"
Detailed Integration: Slack
Setup:
claude mcp add --transport http slack https://slack-mcp.anthropic.com/mcp
What you can do:
> "Check the #incidents channel for the latest production issue"
> "Summarize the discussion in #engineering from today"
> "Post a deployment notification to #releases"
Detailed Integration: Sentry
Setup:
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp
What you can do:
> "What are the most common errors in the last 24 hours?"
> "Show me the stack trace for error ID abc123"
> "Which deployment introduced these new errors?"
8. Setting Up Your First MCP Server
Walkthrough 1: GitHub MCP (Remote HTTP)
This is the most straightforward MCP setup -- a remote HTTP server with OAuth.
Step 1: Add the server
claude mcp add --transport http github https://api.githubcopilot.com/mcp/
Step 2: Launch Claude Code and authenticate
claude
> /mcp
# You will see github listed as [auth required]
# Select it -> Authenticate
# A browser window opens -> Log in to GitHub -> Authorize the app
# Return to Claude Code -> github now shows [connected]
Step 3: Verify it works
> List my open PRs on GitHub
Claude will call the GitHub MCP tools and return your open pull requests.
Step 4 (Optional): Share with your team
# Re-add with project scope so it goes into .mcp.json
claude mcp add --transport http github --scope project https://api.githubcopilot.com/mcp/
git add .mcp.json
git commit -m "Add GitHub MCP server for team"
Walkthrough 2: PostgreSQL Database (Local stdio)
Step 1: Add the server with your connection string
claude mcp add --transport stdio db -- npx -y @bytebase/dbhub \
--dsn "postgresql://readonly_user:password@localhost:5432/myapp_development"
Step 2: Launch Claude Code
claude
Step 3: Verify the connection
> Show me all the tables in the database
Claude will call the database MCP tools and list your tables.
Step 4: Query naturally
> How many users signed up in the last 7 days?
> What's the average order value by product category?
> Show me the schema for the payments table
Walkthrough 3: Notion (Remote HTTP with OAuth)
Step 1: Add the server
claude mcp add --transport http notion https://mcp.notion.com/mcp
Step 2: Authenticate
> /mcp
# Select notion -> Authenticate
# Log in to Notion in the browser -> Select which pages/databases to share
# Return to Claude Code
Step 3: Use it
> Find the page called "Q2 Product Roadmap" and summarize it
> Create a new page in the Engineering workspace titled "Auth System Design"
Walkthrough 4: Filesystem (Local stdio, for Expanding File Access)
By default, Claude Code can access files in your current project directory. The Filesystem MCP server lets you access files elsewhere:
Step 1: Add the server pointing to a directory
claude mcp add --transport stdio filesystem -- npx -y @modelcontextprotocol/server-filesystem \
/Users/you/Documents/shared-configs
Step 2: Use it
> Read the nginx.conf file from the shared configs directory
> Compare the staging and production environment files
9. Subagent MCP Access
How Subagents Access MCP Servers
When Claude spawns subagents (via the Task tool or custom agents), those subagents can access MCP servers too. However, the access must be explicitly configured.
The mcpServers Field in Agent Frontmatter
Custom agents defined in .claude/agents/ can specify which MCP servers they need:
---
name: data-analyst
description: Queries databases and generates reports. Use for data analysis tasks.
tools: Read, Bash, Grep
mcpServers:
- db
- github
---
You are a data analyst. Use the database MCP tools to query data
and GitHub tools to look up related code and issues.
The mcpServers list references servers by name. Those servers must already be configured (via claude mcp add or .mcp.json).
Inline MCP Server Definitions in Agents
You can also define MCP servers directly in the agent frontmatter:
---
name: db-reader
description: Read-only database access for analysis.
mcpServers:
- name: analysis-db
type: stdio
command: npx
args: ["-y", "@bytebase/dbhub", "--dsn", "postgresql://readonly@localhost/analytics"]
---
You have read-only access to the analytics database.
Run SELECT queries only.
Limitations
- Background subagents cannot access MCP servers. MCP connections require an active process, and background agents may not have stable connections.
- MCP servers must be pre-configured. Subagents cannot dynamically add new MCP servers at runtime.
- Each subagent gets its own connection. MCP connections are not shared between agents.
10. Security & Permissions
Managed MCP Configuration (Enterprise)
Organizations can enforce MCP policies using managed configuration files. There are two approaches:
Option 1: Exclusive Control with managed-mcp.json
Deploy a fixed set of MCP servers that users cannot modify or extend.
File locations:
- macOS:
/Library/Application Support/ClaudeCode/managed-mcp.json - Linux/WSL:
/etc/claude-code/managed-mcp.json - Windows:
C:\Program Files\ClaudeCode\managed-mcp.json
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/"
},
"company-internal": {
"type": "stdio",
"command": "/usr/local/bin/company-mcp-server",
"args": ["--config", "/etc/company/mcp-config.json"],
"env": {
"COMPANY_API_URL": "https://internal.company.com"
}
}
}
}
When managed-mcp.json is deployed, users cannot add servers through claude mcp add or their own configuration files.
Option 2: Policy-Based Control with Allowlists/Denylists
Allow users to configure their own servers, but restrict which ones are permitted. These go in managed-settings.json:
{
"allowedMcpServers": [
{ "serverName": "github" },
{ "serverName": "sentry" },
{ "serverCommand": ["npx", "-y", "@modelcontextprotocol/server-filesystem"] },
{ "serverUrl": "https://mcp.company.com/*" },
{ "serverUrl": "https://*.internal.corp/*" }
],
"deniedMcpServers": [
{ "serverName": "dangerous-server" },
{ "serverUrl": "https://*.untrusted.com/*" }
]
}
Key rules:
- Each entry must have exactly one of
serverName,serverCommand, orserverUrl. serverCommandarrays must match exactly (command + all arguments in order).serverUrlsupports wildcards using*.- Denylist always takes precedence. If a server matches both allowlist and denylist, it is blocked.
allowedMcpServers: undefinedmeans no restrictions.allowedMcpServers: []means complete lockdown.
Environment Variable Security
Do not commit secrets to .mcp.json. Use environment variable expansion instead:
{
"mcpServers": {
"database": {
"command": "npx",
"args": ["-y", "@bytebase/dbhub", "--dsn", "${DATABASE_URL}"],
"env": {}
}
}
}
Each team member sets DATABASE_URL in their own shell environment. The secret never appears in version control.
Where to set environment variables:
# In your shell profile (~/.zshrc, ~/.bashrc)
export DATABASE_URL="postgresql://readonly:[email protected]:5432/app"
export AIRTABLE_API_KEY="key_abc123"
# Or in .claude/settings.local.json (gitignored)
# Note: this file is for Claude Code settings, not arbitrary env vars
OAuth Token Management
- OAuth tokens obtained via
/mcpare stored securely in your system keychain (macOS) or a credentials file. - Tokens are refreshed automatically when they expire.
- To revoke access: use
/mcp-> select the server -> "Clear authentication." - Client secrets provided via
--client-secretare also stored in the system keychain, not in config files.
What NOT to Expose
| Do Not | Why | Alternative |
|---|---|---|
Commit API keys to .mcp.json |
Keys in git history are compromised forever | Use ${VAR} expansion |
| Use write-enabled database credentials | Claude could modify production data | Create a read-only database user |
| Expose admin-level API tokens | Over-permissioned access increases blast radius | Use least-privilege tokens |
| Share OAuth tokens between users | Tokens are user-specific | Each user authenticates independently |
| Run untrusted MCP servers | Servers can execute arbitrary code on your machine (stdio) | Vet servers before installing; use managed policies |
MCP Tool Permissions
You can pre-approve or deny specific MCP tools using the permissions system:
{
"permissions": {
"allow": [
"mcp__github__list_prs",
"mcp__github__get_issue",
"mcp__db__query"
],
"deny": [
"mcp__db__execute",
"mcp__slack__send_message"
]
}
}
Or use /permissions inside Claude Code to manage MCP tool permissions interactively.
11. Team Setup Patterns
Pattern 1: Sharing .mcp.json via Git
The standard approach for team MCP configuration:
Step 1: Create the .mcp.json file
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/"
},
"sentry": {
"type": "http",
"url": "https://mcp.sentry.dev/mcp"
},
"database": {
"command": "npx",
"args": ["-y", "@bytebase/dbhub", "--dsn", "${DATABASE_URL}"],
"env": {}
}
}
}
Step 2: Commit it
git add .mcp.json
git commit -m "Add shared MCP server configuration"
Step 3: Document the required environment variables
Add to your project's CLAUDE.md:
## MCP Setup
This project uses MCP servers defined in `.mcp.json`. To get started:
1. Set required environment variables:
- `DATABASE_URL` -- Connection string for the analytics database
(ask the team lead for read-only credentials)
2. Authenticate with cloud services:
- Launch Claude Code: `claude`
- Run `/mcp`
- Authenticate with GitHub and Sentry via the browser flow
3. Verify connections:
- Run `/mcp` to confirm all servers show [connected]
Pattern 2: Environment Variable Templates
Create a .env.example file alongside your .mcp.json:
# .env.example -- Copy to .env and fill in your values
# DO NOT commit .env to git
# Database
DATABASE_URL=postgresql://readonly:[email protected]:5432/app
# Airtable
AIRTABLE_API_KEY=key_YOUR_KEY_HERE
# Brave Search (optional)
BRAVE_API_KEY=YOUR_KEY_HERE
Make sure .env is in your .gitignore.
Pattern 3: Onboarding New Team Members
Create a setup script:
#!/bin/bash
# scripts/setup-mcp.sh -- Run this to configure MCP servers
echo "Setting up MCP servers for the project..."
# Check for required environment variables
if [ -z "$DATABASE_URL" ]; then
echo "ERROR: DATABASE_URL is not set."
echo "Ask the team lead for read-only database credentials."
echo "Then add to your ~/.zshrc: export DATABASE_URL=\"postgresql://...\""
exit 1
fi
echo "Environment variables OK."
echo ""
echo "Next steps:"
echo " 1. Launch Claude Code: claude"
echo " 2. Run /mcp"
echo " 3. Authenticate with GitHub and Sentry"
echo " 4. Verify all servers show [connected]"
Pattern 4: Auto-Approve for Trusted Projects
For teams that trust their .mcp.json configuration, skip the approval prompt:
In .claude/settings.json:
{
"enableAllProjectMcpServers": true
}
Or selectively:
{
"enabledMcpjsonServers": ["github", "sentry", "database"],
"disabledMcpjsonServers": ["experimental-server"]
}
12. Building Your Own MCP Server
When to Build vs. Use Existing
Use an existing server when:
- An MCP server already exists for your service (check the MCP registry)
- A generic server (like dbhub for databases) covers your needs
- The integration is standard and well-supported
Build your own when:
- Your service has a custom internal API with no public MCP server
- You need specialized tool logic (e.g., read-only access to a specific subset of data)
- You want to expose proprietary data or workflows to Claude
The MCP SDK
The official MCP SDK is available in multiple languages:
# TypeScript/JavaScript
npm install @modelcontextprotocol/sdk
# Python
pip install mcp
Basic Server Structure (TypeScript)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new McpServer({
name: "my-custom-server",
version: "1.0.0",
});
// Define a tool
server.tool(
"get_user",
"Look up a user by their email address",
{
email: { type: "string", description: "The user's email address" },
},
async ({ email }) => {
// Your custom logic here
const user = await myDatabase.findUserByEmail(email);
return {
content: [
{
type: "text",
text: JSON.stringify(user, null, 2),
},
],
};
}
);
// Define a resource
server.resource(
"user-schema",
"schema://users",
"The schema of the users table",
async () => {
return {
contents: [
{
uri: "schema://users",
text: "id: integer, email: string, name: string, created_at: timestamp",
},
],
};
}
);
// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);
Basic Server Structure (Python)
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
server = Server("my-custom-server")
@server.list_tools()
async def list_tools():
return [
Tool(
name="get_user",
description="Look up a user by email",
inputSchema={
"type": "object",
"properties": {
"email": {"type": "string", "description": "User email"}
},
"required": ["email"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "get_user":
user = await my_database.find_user(arguments["email"])
return [TextContent(type="text", text=str(user))]
async def main():
async with stdio_server() as (read, write):
await server.run(read, write)
import asyncio
asyncio.run(main())
Registering Your Server with Claude Code
# For a local TypeScript server
claude mcp add --transport stdio my-server -- node /path/to/my-server/dist/index.js
# For a local Python server
claude mcp add --transport stdio my-server -- python /path/to/my-server/server.py
Further Reading
- MCP SDK Quick Start -- Official getting-started guide
- MCP Specification -- Full protocol specification
- MCP GitHub Servers -- Hundreds of community servers for reference
13. Exercises
Exercise 1: Add Your First MCP Server
Goal: Connect Claude Code to GitHub and perform a basic task.
- Run:
claude mcp add --transport http github https://api.githubcopilot.com/mcp/ - Launch Claude Code and run
/mcpto authenticate with GitHub. - Ask Claude: "List my 5 most recent repositories."
- Verify that Claude returns real data from your GitHub account.
Exercise 2: Set Up a Database Connection
Goal: Query a local or remote database through Claude Code.
- Create a local SQLite database (or use an existing PostgreSQL database):
sqlite3 /tmp/test.db "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT); INSERT INTO users VALUES (1, 'Alice', '[email protected]'); INSERT INTO users VALUES (2, 'Bob', '[email protected]');" - Add the database MCP server:
claude mcp add --transport stdio db -- npx -y @bytebase/dbhub --dsn "sqlite:///tmp/test.db" - Ask Claude: "Show me all users in the database."
- Ask Claude: "What's the schema of the users table?"
Exercise 3: Create a Shared .mcp.json
Goal: Set up a project-scoped MCP configuration for team sharing.
- In your project root, create
.mcp.json:{ "mcpServers": { "github": { "type": "http", "url": "https://api.githubcopilot.com/mcp/" }, "memory": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-memory"], "env": {} } } } - Launch Claude Code and run
/mcpto see both servers. - Approve both servers when prompted.
- Ask Claude: "Remember that our API uses JWT authentication with RS256 signing."
- In a new session, ask Claude: "What do you remember about our API authentication?"
Exercise 4: Environment Variable Expansion
Goal: Use environment variables in .mcp.json to keep secrets out of git.
- Set an environment variable:
export MY_API_KEY="test-key-12345" - Create
.mcp.jsonusing variable expansion:{ "mcpServers": { "api-server": { "type": "http", "url": "https://api.example.com/mcp", "headers": { "X-API-Key": "${MY_API_KEY}" } } } } - Launch Claude Code and confirm that
/mcpshows the server (it may fail to connect since the URL is fake, but the configuration should parse successfully). - Verify the key does not appear anywhere in
.mcp.json.
Exercise 5: MCP Resource References
Goal: Use the @ syntax to reference MCP resources.
- Ensure you have the GitHub MCP server connected (from Exercise 1).
- Open a Claude Code session and type
@to see available resources. - Reference a GitHub issue:
> Analyze @github:issue://1 and suggest a fix - Reference multiple resources in one prompt:
> Compare the approach in @github:pr://5 with the design in @github:issue://3
Exercise 6: Multi-MCP Workflow
Goal: Chain multiple MCP servers together in a single workflow.
- Set up at least two MCP servers (e.g., GitHub + Sentry, or GitHub + database).
- Ask Claude a question that requires data from both:
> Check Sentry for the most common error in the last week, > then find the related code on GitHub and suggest a fix. - Observe how Claude calls tools from multiple servers to answer a single question.
Exercise 7: Build a Minimal Custom MCP Server
Goal: Create and connect a simple custom MCP server.
- Create a file called
simple-server.js:import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new McpServer({ name: "simple-server", version: "1.0.0" }); server.tool("current_time", "Get the current date and time", {}, async () => ({ content: [{ type: "text", text: new Date().toISOString() }], })); const transport = new StdioServerTransport(); await server.connect(transport); - Install the SDK:
npm install @modelcontextprotocol/sdk - Add it to Claude Code:
claude mcp add --transport stdio time-server -- node simple-server.js - Ask Claude: "What time is it right now?"
14. Pro Tips from Boris Cherny
Boris Cherny, an engineer at Anthropic who works on Claude Code, has shared several practices that highlight how MCP fits into a high-productivity workflow.
External Tools via MCP
Boris connects Claude Code to the tools his team uses daily:
- Slack -- Read channel messages, search conversations, post updates without leaving the terminal.
- BigQuery -- Query production analytics data and have Claude analyze trends, anomalies, and patterns.
- Sentry -- Pull error data directly into Claude's context so it can correlate bugs with code changes.
The key insight: MCP eliminates the "copy-paste tax." Instead of switching to a browser, finding the data, copying it, and pasting it into your prompt, you just tell Claude what you need and it fetches it.
.mcp.json for Team Sharing
Boris advocates for committing .mcp.json to your repository so every team member gets the same integrations. This creates consistency: when someone says "ask Claude to check Sentry," everyone's Claude has Sentry connected the same way.
/permissions for Pre-Authorizing MCP Tools
One of Boris's productivity techniques is using /permissions to pre-authorize the MCP tools he trusts:
> /permissions
# Add: allow mcp__github__*
# Add: allow mcp__sentry__*
# Add: allow mcp__db__query
This eliminates the permission prompt for trusted MCP tools, letting Claude work faster without interruption. You can also set this in settings.json:
{
"permissions": {
"allow": [
"mcp__github__*",
"mcp__sentry__*",
"mcp__db__query"
]
}
}
The MCP-Enhanced Workflow
Boris's workflow with MCP looks like this:
1. "Check Sentry for the top error this week"
-> Claude calls Sentry MCP, finds the error with stack trace
2. "Find the relevant code and fix it"
-> Claude reads the codebase, makes the fix
3. "Create a PR with the fix"
-> Claude calls GitHub MCP, creates the PR
4. "Post a summary in #engineering on Slack"
-> Claude calls Slack MCP, posts the update
Four steps, zero context switching, zero copy-pasting.
15. Anti-Patterns
Anti-Pattern 1: Committing Secrets to .mcp.json
Problem:
{
"mcpServers": {
"database": {
"command": "npx",
"args": ["-y", "@bytebase/dbhub", "--dsn", "postgresql://admin:[email protected]/app"]
}
}
}
Why it fails: Credentials in git history are compromised permanently. Even if you delete the file later, the secret exists in older commits.
Solution: Use environment variable expansion:
{
"mcpServers": {
"database": {
"command": "npx",
"args": ["-y", "@bytebase/dbhub", "--dsn", "${DATABASE_URL}"]
}
}
}
Anti-Pattern 2: Using Write-Enabled Database Credentials
Problem: Connecting Claude to a database with full read/write/delete permissions.
Why it fails: Claude could accidentally (or be prompted to) modify or delete production data.
Solution: Always create a dedicated read-only database user for MCP connections.
Anti-Pattern 3: Installing Untrusted MCP Servers
Problem: Running claude mcp add --transport stdio random-server -- npx -y some-unknown-package without vetting the package.
Why it fails: stdio MCP servers run as local processes on your machine. A malicious server can access your filesystem, environment variables, network, and anything your user account can reach.
Solution: Only install MCP servers from trusted sources. Review the source code for stdio servers. Use managed policies in enterprise environments.
Anti-Pattern 4: Too Many MCP Servers at Once
Problem: Connecting 20+ MCP servers, each exposing many tools.
Why it fails: Tool descriptions consume context window space. With too many tools, Claude may pick the wrong one or run out of context for your actual work.
Solution: Only connect the servers you actively need. Use disabledMcpjsonServers to disable unused servers. Enable MCP Tool Search for large setups.
Anti-Pattern 5: Ignoring Server Errors
Problem: Seeing [error] in /mcp output and ignoring it because other servers work.
Why it fails: The errored server may be one Claude needs for your current task. You will get confusing failures or incomplete answers.
Solution: Fix errors immediately. Check environment variables, network connectivity, and server process health.
Anti-Pattern 6: Using Project Scope for Personal Servers
Problem: Adding a personal development server with --scope project, which puts it in .mcp.json and affects the whole team.
Why it fails: Other team members may not have the server installed, may not want it, or may not have the required credentials.
Solution: Use local scope (default) for personal servers. Only use project scope for servers the entire team should use.
Anti-Pattern 7: Not Documenting Required Environment Variables
Problem: Committing .mcp.json with ${DATABASE_URL} and ${API_KEY} references but not telling the team what to set.
Why it fails: New team members clone the repo, launch Claude Code, and get cryptic configuration parse errors.
Solution: Document all required environment variables in CLAUDE.md or a .env.example file.
16. Official Documentation Links
Claude Code MCP Documentation
- MCP in Claude Code -- Complete reference for connecting Claude Code to MCP servers
- Claude Code Settings -- Settings file locations, precedence, and MCP-related keys
- Claude Code Sub-Agents -- How subagents access MCP servers via the
mcpServersfrontmatter field - Claude Code Plugins -- Plugin-provided MCP servers
MCP Protocol & SDK
- Model Context Protocol -- The official MCP specification and introduction
- MCP Quick Start (Server) -- Build your first MCP server
- MCP SDK (TypeScript) -- Official TypeScript SDK
- MCP SDK (Python) -- Official Python SDK
MCP Server Directory
- MCP Servers on GitHub -- Hundreds of community and official MCP servers
- Anthropic MCP Registry -- API for discovering registered MCP servers
Related Resources
- How Boris Uses Claude Code -- Boris Cherny's workflow, including MCP usage
- Claude Desktop MCP Config -- Setting up MCP in Claude Desktop (for importing to Claude Code)
Quick Reference: Common Commands
# --- Adding Servers ---
claude mcp add --transport http <name> <url> # Remote HTTP
claude mcp add --transport sse <name> <url> # Remote SSE (deprecated)
claude mcp add --transport stdio <name> -- <command> [args...] # Local stdio
claude mcp add --transport http <name> --scope project <url> # Project scope
claude mcp add --transport http <name> --scope user <url> # User scope
claude mcp add --transport http <name> <url> --header "Auth: token" # With auth header
claude mcp add --transport stdio --env KEY=val <name> -- cmd # With env vars
claude mcp add-json <name> '<json>' # From JSON config
claude mcp add-from-claude-desktop # Import from Desktop
# --- Managing Servers ---
claude mcp list # List all servers
claude mcp get <name> # Server details
claude mcp remove <name> # Remove a server
claude mcp reset-project-choices # Reset .mcp.json approvals
# --- In-Session Commands ---
/mcp # View status, authenticate, manage servers
@server:protocol://resource # Reference an MCP resource
/mcp__server__prompt # Execute an MCP prompt
# --- Environment Variables ---
MCP_TIMEOUT=10000 claude # Set server startup timeout
MAX_MCP_OUTPUT_TOKENS=50000 claude # Increase output token limit
ENABLE_TOOL_SEARCH=auto:5 claude # Custom tool search threshold
Quick Reference: .mcp.json Template
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/"
},
"sentry": {
"type": "http",
"url": "https://mcp.sentry.dev/mcp"
},
"database": {
"command": "npx",
"args": ["-y", "@bytebase/dbhub", "--dsn", "${DATABASE_URL}"],
"env": {}
},
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"],
"env": {}
}
}
}
Quick Reference: Settings for MCP
{
"enableAllProjectMcpServers": true,
"enabledMcpjsonServers": ["github", "sentry"],
"disabledMcpjsonServers": ["experimental"],
"permissions": {
"allow": ["mcp__github__*", "mcp__sentry__*"],
"deny": ["mcp__db__execute"]
}
}
Last Updated: 2026-02-20 Compiled from official Anthropic documentation, MCP specification, and community best practices