Chapter 5 · MCP 01 / 20
Mastering Claude Code · A 10-Part Series

MCP — Model Context Protocol

How Claude Code reaches outside the machine — live tools, APIs, and data sources behind one protocol: transports, OAuth, scopes, tool search, and code execution at scale.

Sid Dani · June 2026
01 · The Problem

Memory remembers the past — MCP reaches live systems right now

Chapter 2's memory stores what doesn't change: preferences, conventions, learned context. But your GitHub issues, database rows, and Slack channels change by the minute. MCP (Model Context Protocol) is a standardized way for Claude to access external tools, APIs, and real-time data sources — live access to changing data, not a snapshot.

Use Memory forUser preferences, conversation history, learned context — persistent, rarely changing
Use MCP forCurrent GitHub issues, live database queries, real-time services — external, frequently changing
The decision is two questions: need external data? If no → memory. Does it change often? If yes → MCP. The two combine — MCP fetches live data, memory keeps the rich context around it.
02 · Architecture

Every service sits behind the same loop: request, query, data, response

Claude sends a tool request (list_issues, create_issue) to an MCP server; the server queries or acts on the external service and returns parsed data — real-time, no caching. One protocol, many servers: real-time access, live sync, extensible architecture, secure authentication, tool-based interactions.

FilesystemFile operations — read, write, delete · auth: OS permissions
GitHubRepository management — list_prs, create_issue, push · auth: OAuth
SlackTeam communication — send_message, list_channels · auth: token
DatabaseSQL queries — query, insert, update · auth: credentials
Google DocsDocument access — read, write, share · auth: OAuth
Asana / StripeProject tasks / payment data · auth: API key
Memory serverPersistent store — the one ecosystem server that is not real-time
03 · Transports

Three transports: HTTP first, stdio for local servers, SSE only as legacy

# HTTP — recommended
claude mcp add --transport http notion \
  https://mcp.notion.com/mcp
# ...with an auth header
claude mcp add --transport http secure-api \
  https://api.example.com/mcp \
  --header "Authorization: Bearer your-token"

# stdio — locally running servers
claude mcp add --transport stdio myserver \
  --env KEY=value -- npx @myorg/mcp-server

# SSE — deprecated, still supported
claude mcp add --transport sse legacy \
  https://example.com/sse
  • HTTP is the recommended transport — remote servers, header-based or OAuth auth
  • stdio spawns a local process; since v2.1.139 every stdio server gets CLAUDE_PROJECT_DIR in its environment, and ${CLAUDE_PROJECT_DIR} substitutes into command, args, and env before launch
  • SSE is deprecated in favor of http
  • Native Windows (not WSL): wrap npx as cmd /c npx -y @some/package
${CLAUDE_PROJECT_DIR} is the same convention hooks use — your stdio server can read files relative to the repo root no matter where Claude Code was launched.
04 · OAuth

OAuth is handled end to end — one browser trip, tokens in your keychain

Add an OAuth-enabled server and Claude Code runs the entire authentication flow. For automation, pass --client-id, --client-secret, and --callback-port to pre-configure credentials non-interactively.

Interactive OAuthType /mcp to trigger the browser-based flow
Pre-configured clientsBuilt-in OAuth clients for common services like Notion and Stripe (v2.1.30+)
Token storageTokens stored securely in your system keychain
Step-up authSupported for privileged operations
Discovery cachingOAuth discovery metadata cached for faster reconnections
Metadata overrideoauth.authServerMetadataUrl in .mcp.json points discovery at a working OIDC endpoint (must be https; v2.1.64+)
The override exists for one failure shape: the server errors on /.well-known/oauth-authorization-server but exposes a working OIDC configuration URL — point Claude Code there instead.
05 · Claude.ai Connectors

Connectors from your claude.ai account just appear — no config required

MCP servers configured in your Claude.ai account are automatically available in Claude Code — anything set up through the web interface works in the terminal with zero additional configuration. (Requires logging in with a Claude.ai account.)

  • Available in --print mode since v2.1.83 — connectors work in non-interactive and scripted usage too
  • Since v2.1.117, local and claude.ai servers connect concurrently by default (previously serial) — faster startup with multiple servers
  • Opt out entirely: ENABLE_CLAUDEAI_MCP_SERVERS=false claude
Two configuration surfaces, one session: local configs and account connectors merge automatically — set a server up once on claude.ai and every machine you log into has it.
06 · Scopes

Three scopes store servers — the closest definition wins a name clash

1 · Local — default · wins all duplicates
~/.claude.json (under project path) — just you, this project · no approval · formerly "project"
private · per-project
2 · Project — checked into git
.mcp.json — shared with team members · approval prompt on first use
shared · in repo
3 · User — formerly "global"
~/.claude.json — just you, all projects · no approval
private · all projects
same server name at multiple scopes → the local config takes precedence
Local precedence is the override mechanism: redefine a project or user server locally to customize it without conflicts. Commit .mcp.json for the team — teammates approve it once on first use.
07 · Managing

claude mcp is the control panel — /mcp shows what's actually connected

claude mcp add --transport http github \
  https://api.github.com/mcp
claude mcp list          # all servers
claude mcp get github    # one server's details
claude mcp remove github
claude mcp reset-project-choices
                         # redo .mcp.json approvals
claude mcp add-from-claude-desktop
                         # import Desktop config

/mcp inside a session

  • Lists connected servers, triggers OAuth flows, inspects connection state
  • v2.1.121+: initial connection retries up to 3× on transient errors
  • v2.1.128+: shows each server's tool count and visually flags servers reporting 0 tools — misconfiguration stands out at a glance
v2.1.136 fixed two long-standing lifecycle bugs: servers from .mcp.json, plugins, and connectors now persist across /clear, and concurrent OAuth token refreshes no longer eat each other — the "re-auth every morning" pattern is gone.
08 · Credentials

Secrets never enter the config — ${VAR} expands at runtime

{
  "mcpServers": {
    "api-server": {
      "type": "http",
      "url": "${API_BASE_URL:-https://api.example.com}/mcp",
      "headers": {
        "Authorization": "Bearer ${API_KEY}"
      }
    },
    "local-server": {
      "command": "${MCP_BIN_PATH:-npx}",
      "env": {
        "DB_URL": "${DATABASE_URL:-postgresql://localhost/dev}"
      }
    }
  }
}
  • ${VAR} — uses the environment variable, errors if not set
  • ${VAR:-default} — falls back to the default if unset
  • Expansion works in five fields: command, args, env, url, headers
  • Export the real values once in ~/.bashrc / ~/.zshrc: GITHUB_TOKEN, DATABASE_URL, SLACK_TOKEN
This is what makes .mcp.json safe to commit: the file carries the shape, the environment carries the secrets.
09 · Worked Example

One JSON block turns Claude into a GitHub operator with 18 tools

// .mcp.json (project root)
{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["@modelcontextprotocol/server-github"],
      "env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
    }
  }
}

> /mcp__github__get_pr 456
Title: Add dark mode support
Author: @alice · Status: OPEN
Reviewers: @bob, @charlie
Pull requestslist_prs, get_pr, create_pr, update_pr, merge_pr, review_pr
Issueslist_issues, get_issue, create_issue, close_issue, add_comment
Repositoryget_repo_info, list_files, get_file_content, search_code
Commitslist_commits, get_commit, create_commit

Or skip the file: claude mcp add --transport stdio github -- npx @modelcontextprotocol/server-github after exporting the token.

10 · Multi-Server

Real workflows chain four servers — metrics, SQL, files, Slack in one pass

The course's daily-report workflow: GitHub MCP pulls PR metrics (42 PRs, 2.3h average merge time) → database MCP runs the sales query (247 orders, $12,450) → Claude combines both into an HTML report → filesystem MCP writes it to /reports/Slack MCP posts the summary to #daily-reports. One .mcp.json defines all four servers; three env vars carry the credentials.

Filesystem — scoped at the root

@modelcontextprotocol/server-filesystem takes the allowed directory as an argument (e.g. /home/user/projects) — list, read, write, edit, search, and delete inside that boundary only.

Database — a placeholder shape

The course writes @modelcontextprotocol/server-database, but that package isn't published on npm — read it as the generic shape (DATABASE_URL in env, SQL tools exposed) and substitute the real package for your database provider.

Each server stays single-purpose; the composition happens in the conversation. That division — narrow servers, broad orchestration — is the pattern every multi-MCP workflow follows.
11 · Context Discipline

Tool search, a 2 KB cap, and output truncation keep MCP from eating your window

Tool search — definitions on demand

ENABLE_TOOL_SEARCH=autoDefault — activates when tool descriptions exceed 10% of context
auto:<N>Custom threshold of N tools
true / falseAlways on / all descriptions sent in full
"alwaysLoad": truePer-server opt-out (v2.1.121+) — tools stay loaded every turn; use sparingly

Requires Sonnet 4+ or Opus 4+ — Haiku models are not supported.

Hard limits on the other end

  • 2 KB cap per server on tool descriptions and instructions (v2.1.84+) — verbose servers can't flood the context
  • Output warning at 10,000 tokens, truncation at 25,000 (raise via MAX_MCP_OUTPUT_TOKENS)
  • Results over 50,000 characters are persisted to disk instead of held in context
Every always-loaded tool spends context that tool search could have used to surface a more relevant tool — reserve alwaysLoad for servers you genuinely touch every turn.
12 · Interaction Surfaces

Servers talk back — prompts, resources, inline apps, mid-task questions

Prompts → slash commandsA server's prompts appear as /mcp__<server>__<prompt> — a github server's review prompt becomes /mcp__github__review
Resources → @ mentions@server-name:protocol://resource/path (e.g. @database:postgres://mydb/users) pulls MCP resource content inline into context
MCP AppsThe first official MCP extension — tool calls return interactive UI (dashboards, forms, visualizations) rendered directly in the chat
ElicitationServers request structured input mid-workflow via interactive dialogs — confirmations, option lists, required fields (v2.1.49+)
Dynamic tool updateslist_changed notifications — servers add, remove, or modify tools live; no reconnect or restart needed
13 · Both Directions

Claude itself can serve — and plugins and agents bring their own servers

1claude mcp serve

Claude Code becomes a stdio MCP server for other applications. Another Claude instance can mount it — claude mcp add --transport stdio claude-agent -- claude mcp serve — the basis for multi-agent workflows where one instance orchestrates another.

2Plugin-provided

Plugins bundle servers two ways: a standalone .mcp.json in the plugin root, or inline in plugin.json. ${CLAUDE_PLUGIN_ROOT} resolves paths relative to the plugin's install directory — available automatically when the plugin is installed.

3Subagent-scoped

An agent's frontmatter can declare mcpServers: inline — the server exists only inside that agent's execution context, invisible to parent and sibling agents. Give one specialist a tool the rest of the workflow never sees.

Chapter 4's agent files just gained reach: scope the server to the specialist, not the session, and the rest of your context never pays for it.
14 · Enterprise

managed-mcp.json gives IT the final word — and deny beats allow

{
  "allowedMcpServers": [
    { "serverName": "github",
      "serverUrl": "https://api.github.com/mcp" },
    { "serverName": "company-internal",
      "serverCommand": "company-mcp-server" }
  ],
  "deniedMcpServers": [
    { "serverName": "untrusted-*" },
    { "serverUrl": "http://*" }
  ]
}
  • macOS: /Library/Application Support/ClaudeCode/managed-mcp.json · Linux: ~/.config/ClaudeCode/ · Windows: %APPDATA%\ClaudeCode\
  • Allowlist + blocklist match by server name, command, and URL patterns
  • Enforced before user configuration — unauthorized servers never connect
  • allowAllClaudeAiMcps permits claude.ai connectors organization-wide (v2.1.149+)
When a server matches both lists, the deny rule takes precedence — note the blocklist patterns above kill wildcarded names and all plain-http servers at once.
15 · Context Bloat

At scale, direct tool calls drown the context — code execution cuts 98.7%

Two sources of waste: tool definitions load upfront (thousands of tools = hundreds of thousands of tokens before your request is even read), and every intermediate result transits the context — a transcript copied from one service to another flows through the model twice, 50,000+ tokens for a 2-hour meeting.

Model
Writes code
instead of issuing tool calls
code
Sandboxed execution env
Calls tools directly
intermediate data lives and dies here
tools
MCP Servers
Data stays in the env
never enters model context
Direct tool calls
all data through context
≈150,000 tokens
Code execution
only the final result returns
≈2,000 tokens — a 98.7% reduction
16 · Code Execution

Tools become typed functions in a file tree — the agent imports what it needs

servers/
├── google-drive/
│   ├── getDocument.ts   // typed wrapper
│   └── index.ts
└── salesforce/
    └── updateRecord.ts

// agent-written orchestration:
const transcript = (await gdrive
  .getDocument({ documentId: 'abc123' })).content;
await salesforce.updateRecord({
  objectType: 'SalesMeeting',
  data: { Notes: transcript }  // never touches context
});
  • Progressive disclosure — browse the tree, load only the tool definitions needed
  • Context-efficient results — filter 10,000 rows in the sandbox, return 5 to the model
  • Powerful control flow — loops and retries (e.g. polling Slack for "deployment complete") run in code, no round-trips
  • Privacy — PII and sensitive records stay in the execution environment
  • State persistence — save intermediate files, build reusable skill functions
The trade-off is infrastructure: a secure sandbox, monitoring, and logging of agent-written code. A few servers → direct calls are simpler. Dozens of servers, hundreds of tools → code execution wins decisively.
17 · MCPorter

MCPorter packages the pattern — typed clients and CLIs from any server

MCPorter is a TypeScript runtime and CLI toolkit for calling MCP servers without boilerplate — the infrastructure layer for the code-execution approach. Instead of loading every tool from every server, you discover, inspect, and call specific tools on demand.

Zero-config discoveryAuto-finds servers from Cursor, Claude, Codex, or local configs
mcporter emit-tsGenerates .d.ts interfaces + ready-to-run typed wrappers
createServerProxy()Tools as camelCase methods with .text() / .json() / .markdown() helpers
generate-cliAny MCP server → standalone CLI, with --include-tools / --exclude-tools filtering
Parameter hidingOptional params hidden by default — less schema verbosity
# discover instantly, no install
npx mcporter list

# call one tool directly
npx mcporter call linear.create_comment \
  issueId:ENG-123 body:'Looks good!'

// compose in TypeScript
const runtime = await createRuntime();
const gdrive = createServerProxy(
  runtime, "google-drive");
18 · Best Practices

Treat every server like production access — least privilege, env-var secrets

Do

  • Environment variables for all credentials; rotate tokens monthly
  • Read-only tokens where possible; minimum access scope per server
  • OAuth over static tokens when the service offers it
  • Monitor usage and access logs; rate-limit requests
  • Test connections before production; document active servers; keep packages updated

Don't

  • Hardcode credentials in configs or commit secrets to git
  • Share tokens in chats/email, or use personal tokens for team projects
  • Grant unnecessary permissions, or ignore authentication errors
  • Expose MCP endpoints publicly, or run servers as root/admin
  • Cache sensitive data in logs, or disable auth mechanisms
Operationally: version-control .mcp.json (secrets in env), isolate servers in separate processes, log requests for audit trails — and for speed, cache at the app level, keep queries specific, and batch multi-operation work.
19 · Recap

MCP is Claude Code's reach — everything else decides how far

MCP puts every external service behind one request loop. Servers arrive over three transports (HTTP first), authenticate via end-to-end OAuth, and live at three scopes where the closest definition wins. The context bill is managed for you — tool search, the 2 KB description cap, output truncation — and at real scale, code execution moves the data flow out of the model entirely: ≈150k tokens down to ≈2k. Servers aren't passive either: they expose slash-command prompts, @-mention resources, inline apps, and mid-task questions — and Claude itself can be one with claude mcp serve.

Start tonight: drop a github server into .mcp.json, export GITHUB_TOKEN, and run /mcp to watch it connect. Chapter 6: hooks — where you stop asking Claude to behave and start enforcing it.
Sid Dani