The definitive guide to the Model Context Protocol -- from understanding the architecture to connecting Claude Code to your databases, issue
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.
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.
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.
.mcp.json, commit it to git, and every team member gets the same integrations automatically.By the end of this level, you will be able to:
.mcp.json@ syntaxMCP 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 | 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:
Every MCP server can expose three types of capabilities:
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 messageWhen Claude needs to interact with an external service, it calls the appropriate MCP tool. You see a permission prompt before any tool executes.
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 documentationResources are referenced using the @ syntax (covered in Section 6).
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 errorsType / in Claude Code to see all available MCP prompts.
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.
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.
claude mcp addThe primary way to add MCP servers is the claude mcp add command.
# 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
# Basic syntax
claude mcp add --transport sse <name> <url>
# Example: Connect to Asana
claude mcp add --transport sse asana https://mcp.asana.com/sse
# 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
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
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
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"
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
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
/mcp Interactive CommandInside 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
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:
server_1)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
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.
| 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 |
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.
.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.
.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}"
}
}
}
}
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 authenticationExample 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.
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 |
{
"enableAllProjectMcpServers": true
}
{
"enabledMcpjsonServers": ["github", "notion", "sentry"],
"disabledMcpjsonServers": ["filesystem"]
}
If you need to re-evaluate which project-scoped servers to approve:
claude mcp reset-project-choices
# 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
# Get full details for a specific server
claude mcp get github
# Shows: name, transport, URL/command, scope, env vars, auth status
# Remove a server by name
claude mcp remove github
# Remove from a specific scope
claude mcp remove --scope user github
In settings.json:
{
"enabledMcpjsonServers": ["github", "sentry"],
"disabledMcpjsonServers": ["dangerous-server"]
}
Or auto-approve everything:
{
"enableAllProjectMcpServers": true
}
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]
Server shows "error":
echo $MY_API_KEYServer shows "auth required":
/mcp inside Claude Code.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
@ SyntaxMCP servers can expose resources -- read-only data like files, database schemas, issue contents, and more. You reference these using @ mentions:
@server:protocol://resource/path
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.
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 |
@, Claude Code queries all connected MCP servers for their available resources.| 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 |
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"
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.
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"
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"
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?"
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"
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
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"
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
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.
mcpServers Field in Agent FrontmatterCustom 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).
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.
Organizations can enforce MCP policies using managed configuration files. There are two approaches:
managed-mcp.jsonDeploy a fixed set of MCP servers that users cannot modify or extend.
File locations:
/Library/Application Support/ClaudeCode/managed-mcp.json/etc/claude-code/managed-mcp.jsonC:\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.
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:
serverName, serverCommand, or serverUrl.serverCommand arrays must match exactly (command + all arguments in order).serverUrl supports wildcards using *.allowedMcpServers: undefined means no restrictions. allowedMcpServers: [] means complete lockdown.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
/mcp are stored securely in your system keychain (macOS) or a credentials file./mcp -> select the server -> "Clear authentication."--client-secret are also stored in the system keychain, not in config files.| 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 |
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.
.mcp.json via GitThe 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]
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.
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]"
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"]
}
Use an existing server when:
Build your own when:
The official MCP SDK is available in multiple languages:
# TypeScript/JavaScript
npm install @modelcontextprotocol/sdk
# Python
pip install mcp
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);
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())
# 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
Goal: Connect Claude Code to GitHub and perform a basic task.
claude mcp add --transport http github https://api.githubcopilot.com/mcp//mcp to authenticate with GitHub.Goal: Query a local or remote database through Claude Code.
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]');"
claude mcp add --transport stdio db -- npx -y @bytebase/dbhub --dsn "sqlite:///tmp/test.db"
.mcp.jsonGoal: Set up a project-scoped MCP configuration for team sharing.
.mcp.json:{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/"
},
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"],
"env": {}
}
}
}
/mcp to see both servers.Goal: Use environment variables in .mcp.json to keep secrets out of git.
export MY_API_KEY="test-key-12345"
.mcp.json using variable expansion:{
"mcpServers": {
"api-server": {
"type": "http",
"url": "https://api.example.com/mcp",
"headers": {
"X-API-Key": "${MY_API_KEY}"
}
}
}
}
/mcp shows the server (it may fail to connect since the URL is fake, but the configuration should parse successfully)..mcp.json.Goal: Use the @ syntax to reference MCP resources.
@ to see available resources.> Analyze @github:issue://1 and suggest a fix
> Compare the approach in @github:pr://5 with the design in @github:issue://3
Goal: Chain multiple MCP servers together in a single workflow.
> Check Sentry for the most common error in the last week,
> then find the related code on GitHub and suggest a fix.
Goal: Create and connect a simple custom MCP server.
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);
npm install @modelcontextprotocol/sdkclaude mcp add --transport stdio time-server -- node simple-server.js
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.
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 SharingBoris 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 ToolsOne 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"
]
}
}
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.
.mcp.jsonProblem:
{
"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}"]
}
}
}
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.
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.
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.
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.
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.
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.
mcpServers frontmatter field# --- 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
.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": {}
}
}
}
{
"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
New techniques, real-world patterns, and Claude updates โ delivered as the guides evolve.