MCP: Connecting External Services
Notion, Figma, databases, Slack, and custom tools
- Connect external services with `claude mcp add` and OAuth-authenticate them via `/mcp`
- Choose the right scope (local/project/user) and load servers from config with `--mcp-config` and `--strict-mcp-config`
- Explain MCP tool search: why definitions are deferred, how schemas load on demand, and how `ENABLE_TOOL_SEARCH` and `alwaysLoad` change that
- Reference MCP resources with @ mentions and run MCP prompts as `/mcp__server__prompt` commands
- Audit per-server context and cost with `/mcp` and `/usage`, and skip MCP entirely with `--bare`
MCP (Model Context Protocol) lets Claude Code reach beyond your repo into Notion, Figma, Slack, Jira, databases, Google Drive, and custom tools. This lesson covers adding and authenticating servers, the config and scoping flags, OAuth, referencing MCP resources, and the tool-search mechanism that keeps dozens of MCP tools from flooding your context window.
- 1The mental model: a USB port for your tools
- 2Adding servers: claude mcp add and transports
- 3Scopes and loading from config files
- 4OAuth: authenticating remote servers via /mcp
- 5Tool search: why 200 MCP tools don't blow up your context
- 6Resources, prompts, and watching the cost
The mental model: a USB port for your tools
Out of the box, Claude Code knows your repository — files, git, the shell. It knows nothing about your Notion docs, your Figma designs, your Jira backlog, your Postgres database, or your Slack channels. The moment you find yourself copying data out of another tool and pasting it into chat, that is the signal you want MCP.
Model Context Protocol (MCP) is an open standard for AI-to-tool integrations. Think of it as a universal port: instead of Anthropic hand-building an integration for every service, any service can ship an MCP server that speaks the protocol, and Claude Code (the MCP client) plugs into it. Connect the server once and Claude can read and act on that system directly — no more paste-shuffling.
There are two pieces of vocabulary worth pinning down up front:
- MCP server — the thing you connect to (Notion's server, GitHub's server, a local database script). It exposes tools (actions Claude can call), resources (data Claude can read with
@), and prompts (canned commands). - Transport — how Claude talks to the server. HTTP is the recommended transport for remote cloud services; stdio runs a server as a local process on your machine; SSE is deprecated in favor of HTTP.
Once a server is connected, a request like "Add the feature in JIRA ENG-4521 and open a PR on GitHub" spans two external systems and your repo — Claude reads the issue, writes the code, and creates the PR, all in one loop.
Key insight
The 'I keep pasting this' heuristic
You don't need MCP for everything. The clean trigger from the docs: connect a server when you find yourself copying data into chat from another tool — an issue tracker, a monitoring dashboard, a design file. If you only touch a system once, paste is fine. If you touch it constantly, wire it up.
Watch out
Trust is part of the threat model
An MCP server can fetch external content, and that content can carry prompt-injection instructions. Only connect servers you trust. Browse vetted connectors in the Anthropic Directory (claude.ai/directory) rather than wiring up arbitrary endpoints, and remember that a server which reads the web is a path for hostile text to reach Claude.
Adding servers: claude mcp add and transports
You add servers from the terminal with claude mcp add. The flag that matters most is --transport, which picks how Claude connects.
Remote HTTP (recommended for cloud services). Most hosted servers — Notion, Stripe, Sentry, Slack — are remote HTTP:
# Basic syntax
claude mcp add --transport http <name> <url>
# Real example: connect to Notion
claude mcp add --transport http notion https://mcp.notion.com/mcp
# With a static Bearer token in a header
claude mcp add --transport http secure-api https://api.example.com/mcp \
--header "Authorization: Bearer your-token"Local stdio (for tools that need direct system access). A stdio server is a local process Claude spawns and talks to over standard in/out. Note the -- that separates Claude's flags from the command Claude should run:
# Basic syntax
claude mcp add [options] <name> -- <command> [args...]
# Example: a read-only PostgreSQL server
claude mcp add --transport stdio db -- npx -y @bytebase/dbhub \
--dsn "postgresql://readonly:pass@prod.db.com:5432/analytics"Managing what you've added:
claude mcp list # all configured servers
claude mcp get notion # details for one server
claude mcp remove notion # remove itThe official docs span the full menagerie of services — Notion, Figma, Slack, Jira, GitHub, Sentry, Google Drive, PostgreSQL, Airtable, and custom servers you build yourself — but they all reduce to the same three shapes above: a remote HTTP URL, a deprecated SSE URL, or a local stdio command.
Watch out
Option ordering: flags before the name, command after the --
Every option (--transport, --env, --scope, --header) must come before the server name; the -- then separates the name from the command and args passed to the server. claude mcp add --transport stdio --env KEY=value myserver -- python server.py --port 8080 runs python server.py --port 8080 with KEY=value in its environment. Putting flags after the name is the most common beginner error.
Tip
Custom and JSON-defined servers
If you already have a JSON config, claude mcp add-json <name> '<json>' adds it directly. To scaffold your own server, the official mcp-server-dev plugin (/plugin install mcp-server-dev@claude-plugins-official) has Claude build a remote HTTP or local stdio server for you. You can even expose Claude Code itself as a server with claude mcp serve.
Scopes and loading from config files
Where a server's configuration lives determines which projects see it and whether your team shares it. Set this with --scope:
| Scope | Loads in | Shared with team? | Stored in |
|---|---|---|---|
| local (default) | Current project only | No | ~/.claude.json |
| project | Current project only | Yes, via version control | .mcp.json in project root |
| user | All your projects | No | ~/.claude.json |
claude mcp add --transport http stripe https://mcp.stripe.com # local (default)
claude mcp add --transport http paypal --scope project https://mcp.paypal.com/mcp # team-shared
claude mcp add --transport http hubspot --scope user https://mcp.hubspot.com/anthropic # everywhereProject scope is the team play. It writes a .mcp.json at the repo root that you commit to version control, so every teammate gets the same tools. For safety, Claude prompts for approval before using project-scoped servers from .mcp.json (reset those choices with claude mcp reset-project-choices).
Loading servers ad hoc with --mcp-config. Instead of registering servers permanently, you can point a single session at a config — a file (or several), or inline JSON:
claude --mcp-config ./mcp.json # load servers from a file
claude --mcp-config '{"mcpServers":{...}}' # or inline JSON--strict-mcp-config ignores everything else. Normally --mcp-config servers load in addition to your local/project/user servers. Add --strict-mcp-config and Claude uses only the servers from --mcp-config — nothing from ~/.claude.json or .mcp.json. This is the clean, reproducible setup for CI and scripts, where you want a known-exact tool surface and no surprise servers leaking in.
Tip
Precedence when names collide
If the same server name is defined in several places, Claude connects once using the highest-precedence definition (it does not merge fields). The order is: local → project → user → plugin-provided → claude.ai connectors. Scopes match by name; plugins and connectors match by endpoint URL/command.
Note
Env-var expansion keeps secrets out of git
.mcp.json supports ${VAR} and ${VAR:-default} expansion in command, args, env, url, and headers. This is how a team commits a shared .mcp.json while each member's API key stays in their own environment — e.g. "Authorization": "Bearer ${API_KEY}". Never paste real secret values into a committed config.
OAuth: authenticating remote servers via /mcp
Most cloud servers (Notion, Slack, Sentry, Jira) need you to log in before Claude can act on your account. Claude Code supports OAuth 2.0, and the flow is almost entirely hands-off.
- Add the server as usual:
bash
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp - Run
/mcpinside Claude Code. The server shows up flagged as needing authentication (Claude marks a remote server this way when it returns401 Unauthorizedor403 Forbidden). - Complete the login in your browser, then return to the terminal. That's it.
/mcpThe /mcp panel is your control center for connections: it lists every connected server, shows the tool count next to each, lets you authenticate OAuth servers, and offers Clear authentication to revoke access. Tokens are stored securely (system keychain on macOS) and refreshed automatically, so you log in once.
A few real-world wrinkles worth knowing:
- OAuth works on HTTP servers, not stdio (which runs locally and uses its own credentials).
- If the browser redirect fails after you log in, copy the full callback URL from the address bar and paste it into the URL prompt that appears in Claude Code.
- Some servers need a fixed callback port (
--callback-port 8080) or pre-registered credentials (--client-id,--client-secret) when they don't support automatic Dynamic Client Registration. - You can pin OAuth scopes with an
oauth.scopesfield in.mcp.jsonto restrict a server to a security-approved subset (e.g."channels:read chat:write search:read"for Slack).
Tip
claude.ai connectors come along for free
If you logged into Claude Code with a Claude.ai subscription, MCP servers you added at claude.ai/customize/connectors are automatically available — /mcp lists them with a claude.ai indicator. Caveat: they load only when your active auth method is your Claude.ai subscription, not when ANTHROPIC_API_KEY, a gateway token, or Bedrock/Vertex is active. If a connector is missing, run /status to check which auth is live.
Tool search: why 200 MCP tools don't blow up your context
Here is the scaling problem. Every MCP tool has a JSON schema — its name, description, and parameters. A handful of servers can expose hundreds of tools, and if Claude loaded every schema into the context window at startup, your usable context would be gone before you typed a word. MCP definitions are verbose.
Tool search is the fix, and it is on by default. At session start, Claude loads only the tool names and each server's instructions — not the full schemas. When a task actually needs a tool, Claude calls a built-in ToolSearch tool to find the relevant ones, and only those schemas enter context. From your seat nothing changes: MCP tools work exactly as before. Behind the scenes, adding more servers now has minimal impact on your context window.
This is implemented with tool_reference blocks, which require a capable model: Sonnet 4 and later, or Opus 4 and later. Haiku models do not support it. (On Vertex AI, support starts at Sonnet 4.5 / Opus 4.5.)
You control the behavior with the ENABLE_TOOL_SEARCH environment variable:
| Value | Behavior |
|---|---|
| (unset) | Default. All MCP tools deferred, loaded on demand. (Falls back to loading upfront on Vertex AI or a non-first-party ANTHROPIC_BASE_URL.) |
true | Force all tools deferred, even on Vertex AI / through proxies (fails if the model can't do tool_reference). |
auto | Threshold mode: load schemas upfront if they fit within 10% of the context window, defer the overflow. |
auto:N | Threshold mode with a custom percentage, e.g. auto:5 for 5%. |
false | Load every MCP tool upfront, no deferral (the old behavior). |
ENABLE_TOOL_SEARCH=false claude # load all MCP tool schemas at startup
ENABLE_TOOL_SEARCH=auto:5 claude # upfront only if under 5% of contextExempting a server with alwaysLoad. Some tools you need on every turn — paying the search step each time is wasteful. Set "alwaysLoad": true in a server's config and all its tools load at startup regardless of ENABLE_TOOL_SEARCH. Use it sparingly: every always-loaded tool consumes context that could be conversation. A server can also mark individual tools with "anthropic/alwaysLoad": true in their _meta.
{
"mcpServers": {
"core-tools": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"alwaysLoad": true
}
}
}Key insight
Same idea as skills, applied to tools
Tool search is progressive disclosure: keep names cheap and visible, load expensive detail only when relevant. That's exactly how skills work. It's why MCP server instructions matter more with tool search on — a clear, concise instruction (Claude truncates both descriptions and instructions at 2KB) helps Claude know when to search for your server's tools.
Watch out
alwaysLoad blocks startup
Setting alwaysLoad: true makes Claude wait for that server to connect before building the first prompt (capped at the 5-second connect timeout), because the tools must be present. Other servers still connect in the background. Don't alwaysLoad a slow or flaky server.
Resources, prompts, and watching the cost
Beyond tools (actions), MCP servers can expose two more things, plus you'll want to keep an eye on what all this costs in context.
Resources — read data with @ mentions. Just like you reference a file with @path/to/file, you reference an MCP resource with an @ mention. Type @ and connected servers' resources appear in the autocomplete alongside files. The reference format is @server:protocol://resource/path:
Can you analyze @github:issue://123 and suggest a fix?
Please review the API docs at @docs:file://api/authentication
Compare @postgres:schema://users with @docs:file://database/user-modelThe referenced resource is fetched and attached before Claude responds. (You may also see the older @mcp_resource:protocol://path form referenced elsewhere — the current docs use @server:protocol://resource/path.)
Prompts — canned commands as slash commands. Servers can ship reusable prompts that surface as slash commands in the exact format /mcp__<server>__<prompt>. Type / to discover them; pass arguments space-separated:
/mcp__github__list_prs
/mcp__github__pr_review 456
/mcp__jira__create_issue "Bug in login flow" highNames are normalized (spaces become underscores), and the prompt's result is injected straight into the conversation.
Watch per-server cost. MCP tools, results, and definitions all consume context and tokens. Two commands keep you honest:
/mcp— shows the tool count per connected server, so you can spot a server flooding you with tools./usage(aliases/cost,/stats) — session cost plus a breakdown by skill, subagent, plugin, and MCP server. This is where you discover one chatty server is eating your budget.
Skipping MCP entirely with --bare. When you want a fast, minimal session with no MCP discovery at all — plus no hooks, skills, plugins, auto-memory, or CLAUDE.md — launch with --bare. It leaves only Bash/Read/Edit and sets CLAUDE_CODE_SIMPLE. Perfect for a quick one-off where MCP startup would just be overhead.
claude --bare -p "summarize the git log of the last 10 commits"Tip
The --channels preview: servers that push to you
An MCP server can also act as a channel that pushes messages into your session — CI results, monitoring alerts, Telegram/Discord chats — so Claude reacts to external events while you're away. Opt a channel in at startup with the (preview) --channels flag, e.g. claude --channels plugin:notifier@mkt. It needs claude.ai authentication. This is the inverse of normal MCP: events flow toward Claude, not requests away from it.
Watch out
MCP output can be large
Claude Code warns when an MCP tool returns more than 10,000 tokens; the default hard cap is 25,000 (MAX_MCP_OUTPUT_TOKENS to raise it). A database or log-query server can dump enormous results — a reason to prefer read-only, scoped queries and to watch /usage after big tool calls.
Try it: Wire up a server, watch the context, and connect two systems
Goal: connect a real MCP server, prove that tool search keeps your context lean, and run a task that spans two systems.
- Connect a remote server. Pick something you have an account for — GitHub, Sentry, or Notion. Add it (e.g.
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp), then runclaude mcp listandclaude mcp get <name>to confirm it registered. - Authenticate. Start Claude, run
/mcp, and complete the browser OAuth login. Confirm the server shows as connected and note the tool count beside it. - Measure the context cost. Run
/contextand/usage. With tool search on (the default), note that even a tool-heavy server barely dents your context. For contrast, restart withENABLE_TOOL_SEARCH=false claude, re-run/context, and compare — you should see the MCP tools now loaded upfront. - Use a resource and a prompt.
@-mention a resource (e.g.@github:issue://<number>) and watch it get attached. Type/and find an MCP prompt in the/mcp__<server>__<prompt>format; run it with an argument. - Scope it for a team. Re-add one server with
--scope project, open the generated.mcp.json, and confirm it lives at the repo root. Edit it to use${VAR}expansion for any token so no secret is committed. - Reflect. Which server gave you the most tools, and did you set
alwaysLoadon anything? When would--strict-mcp-configor--barehave been the right call for a scripted run? Write three sentences.
Key takeaways
- 1MCP connects Claude Code to external services (Notion, Figma, Slack, Jira, GitHub, databases, Google Drive, custom). Add with `claude mcp add --transport http|stdio`, where remote services use HTTP and local processes use stdio after a `--` separator.
- 2Scope controls reach and sharing: `--scope local` (default, private), `project` (team-shared `.mcp.json` in git), `user` (all your projects). Load servers ad hoc with `--mcp-config`; `--strict-mcp-config` uses only those, ignoring registered servers.
- 3Run `/mcp` to authenticate OAuth servers (flagged on 401/403), check status, see per-server tool counts, and clear auth. Tokens are stored securely and refreshed automatically; OAuth applies to HTTP servers.
- 4Tool search (on by default) defers MCP tool schemas — only names load at startup, schemas load on demand via `ToolSearch` — so dozens of tools barely touch context. It needs Sonnet 4+/Opus 4+ (not Haiku). `ENABLE_TOOL_SEARCH=false` loads everything; `alwaysLoad: true` keeps one server's tools always visible.
- 5Reference MCP resources as `@server:protocol://resource/path` (e.g. `@github:issue://123`); MCP prompts surface as `/mcp__<server>__<prompt>` commands with space-separated arguments.
- 6Audit per-server context cost with `/mcp` and `/usage`; skip MCP discovery (and hooks/skills/plugins/memory) entirely with `--bare`.
Quiz
Lock in what you learned
Check your understanding
0 / 4 answered
1.You run `claude mcp add notion --transport http https://mcp.notion.com/mcp` and it errors. What's wrong?
2.Your team wants everyone on the repo to get the same MCP servers automatically when they clone it. Which scope, and why?
3.You've connected five MCP servers exposing ~200 tools total, but your context usage barely moved at startup. Then you switch to a Haiku model and tools start failing. What's going on?
4.Which of these correctly references an MCP resource and runs an MCP prompt, respectively?
Go deeper
Hand-picked sources to keep learning
The full reference: claude mcp add, transports, scopes, OAuth, resources, prompts, and tool search.
/mcp, /usage, and the /mcp__server__prompt format for MCP prompts.
Browse reviewed MCP connectors you can add with `claude mcp add` — vetted servers reduce prompt-injection risk.
Protocol fundamentals and the build-your-own-server guide for custom MCP servers.
Changelog and issues — track tool-search, --channels, and alwaysLoad behavior as they evolve.