← Back to learning path
Level 4

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,…

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

  1. Overview & Goal
  2. How MCP Works
  3. Adding MCP Servers
  4. MCP Scopes & Configuration Files
  5. Managing MCP Servers
  6. Referencing MCP Resources
  7. Popular MCP Integrations
  8. Setting Up Your First MCP Server
  9. Subagent MCP Access
  10. Security & Permissions
  11. Team Setup Patterns
  12. Building Your Own MCP Server
  13. Exercises
  14. Pro Tips from Boris Cherny
  15. Anti-Patterns
  16. 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

  1. Eliminates context switching -- Claude reads from your actual tools instead of you copying data into prompts.
  2. 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."
  3. 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.
  4. 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:


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:

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:

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:

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:

Type / in Claude Code to see all available MCP prompts.

How Claude Discovers and Uses MCP Tools

  1. Startup: When Claude Code launches, it connects to all configured MCP servers.
  2. Discovery: Claude Code asks each server "What tools, resources, and prompts do you offer?"
  3. Tool list: The available MCP tools are added to Claude's tool inventory alongside built-in tools (Read, Write, Edit, Bash, etc.).
  4. Invocation: When you ask Claude a question that requires external data, Claude identifies the relevant MCP tool and calls it.
  5. Permission: You see a permission prompt showing what Claude wants to do. You approve or deny.
  6. Execution: The MCP server processes the request, calls the external API, and returns the result.
  7. 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:

> /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:

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:

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":

  1. Check that the server process can start: run the command manually in your terminal.
  2. Verify environment variables are set: echo $MY_API_KEY
  3. Check network connectivity for remote servers.
  4. Look at Claude Code logs for detailed error messages.

Server shows "auth required":

  1. Run /mcp inside Claude Code.
  2. Select the server and choose "Authenticate."
  3. 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

  1. When you type @, Claude Code queries all connected MCP servers for their available resources.
  2. You select a resource from the autocomplete.
  3. Claude Code fetches the resource content from the MCP server.
  4. The content is attached to your prompt as context.
  5. 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


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:

{
  "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:

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

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:

Build your own when:

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


13. Exercises

Exercise 1: Add Your First MCP Server

Goal: Connect Claude Code to GitHub and perform a basic task.

  1. Run: claude mcp add --transport http github https://api.githubcopilot.com/mcp/
  2. Launch Claude Code and run /mcp to authenticate with GitHub.
  3. Ask Claude: "List my 5 most recent repositories."
  4. 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.

  1. 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]');"
    
  2. Add the database MCP server:
    claude mcp add --transport stdio db -- npx -y @bytebase/dbhub --dsn "sqlite:///tmp/test.db"
    
  3. Ask Claude: "Show me all users in the database."
  4. 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.

  1. 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": {}
        }
      }
    }
    
  2. Launch Claude Code and run /mcp to see both servers.
  3. Approve both servers when prompted.
  4. Ask Claude: "Remember that our API uses JWT authentication with RS256 signing."
  5. 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.

  1. Set an environment variable:
    export MY_API_KEY="test-key-12345"
    
  2. Create .mcp.json using variable expansion:
    {
      "mcpServers": {
        "api-server": {
          "type": "http",
          "url": "https://api.example.com/mcp",
          "headers": {
            "X-API-Key": "${MY_API_KEY}"
          }
        }
      }
    }
    
  3. Launch Claude Code and confirm that /mcp shows the server (it may fail to connect since the URL is fake, but the configuration should parse successfully).
  4. Verify the key does not appear anywhere in .mcp.json.

Exercise 5: MCP Resource References

Goal: Use the @ syntax to reference MCP resources.

  1. Ensure you have the GitHub MCP server connected (from Exercise 1).
  2. Open a Claude Code session and type @ to see available resources.
  3. Reference a GitHub issue:
    > Analyze @github:issue://1 and suggest a fix
    
  4. 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.

  1. Set up at least two MCP servers (e.g., GitHub + Sentry, or GitHub + database).
  2. 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.
    
  3. 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.

  1. 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);
    
  2. Install the SDK: npm install @modelcontextprotocol/sdk
  3. Add it to Claude Code:
    claude mcp add --transport stdio time-server -- node simple-server.js
    
  4. 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:

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 Protocol & SDK

MCP Server Directory

Related Resources


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