Module 11 — MCP: connect Claude Code to your tools and your data
Claude Code, straight out of the terminal, knows how to read your files, write code, and run shell commands. It knows nothing about anything else: your GitHub repo, your staging Postgres database, your ticket tracker, your S3 bucket, your observability system. MCP, the Model Context Protocol, is the standardized mechanism through which Claude Code talks to those services without you writing the integration code yourself.
The principle: one protocol, several transports
MCP is an open protocol. The service ("MCP server") exposes tools — atomic actions such as read_file, search_issues, run_query — and Claude Code ("MCP client") calls them as if they were internal tools. The output vocabulary is identical on Claude's side: it does not matter whether the tool lives in-process or on the other side of the world.
What varies is the transport. Four transports are documented:
| Transport | Use case | Notes |
|---|---|---|
http (streamable-http) | Remote server, cloud service | Recommended transport for cloud services, supports OAuth |
sse | Legacy, a few services still SSE-only | Marked deprecated, prefer HTTP |
stdio | Local process, homegrown script, CLI tool | Ideal for direct system access or a local database |
ws (WebSocket) | Remote servers that push events | Does not support OAuth or --transport ws in the CLI; configure through JSON |
The type field in a configuration file also accepts streamable-http as an alias for http — that is the official MCP name, useful when you paste a configuration copied from another client's docs.
Three scopes for three uses
An MCP server is declared in one of three scopes, each with a destination file and a different visibility.
| Scope | Loads in | Shared with the team | Stored in |
|---|---|---|---|
| Local | This project only | No | ~/.claude.json |
| Project | This project only | Yes, versioned | .mcp.json at the project root |
| User | All your projects | No | ~/.claude.json |
The scope is chosen on the command line with --scope local|project|user (default: local). If names collide between scopes, precedence is local > project > user > plugin > claude.ai connector: Claude Code uses the entire entry from the priority scope, no field-by-field merge.
Beware a security trap: servers declared in a versioned .mcp.json require manual approval the first time the project opens. In a claude -p session (headless) or in the SDK, there is no prompt: Claude Code loads the servers without asking, unless you add --strict-mcp-config or list the server in disabledMcpjsonServers. A freshly cloned repo whose workspace trust dialog you have not yet accepted leaves every server at status ⏸ Pending approval.
Installing a server: the four paths
Remote HTTP server
The recommended option to connect a cloud service. Syntax:
claude mcp add --transport http <name> <url>
claude mcp add --transport http notion https://mcp.notion.com/mcp
claude mcp add --transport http secure-api https://api.example.com/mcp \
--header "Authorization: Bearer <token>"
Remote SSE server
Reserve for services that only expose an SSE endpoint. Equivalent syntax with --transport sse. Feature deprecated, migrate to HTTP as soon as the service allows.
Local stdio server
A stdio server is a process launched by Claude Code on your machine, exchanging over standard descriptors. The separation between claude mcp add options and the command to run is done with --:
claude mcp add [options] <name> -- <command> [args...]
Claude Code automatically injects CLAUDE_PROJECT_DIR into the server process environment, pointing at the project root. It is stable for the entire session, independent of the current directory. Variables are passed through --env KEY=value, placed before the -- and not right after if the server name follows:
claude mcp add --env AIRTABLE_API_KEY=<key> --transport stdio airtable \
-- npx -y airtable-mcp-server
Remote WebSocket server
Reserved for servers that push events unsolicited. --transport does not accept ws in the CLI; go through claude mcp add-json or edit .mcp.json directly:
claude mcp add-json events '{"type":"ws","url":"wss://mcp.example.com/socket","headers":{"Authorization":"Bearer <token>"}}'
Anatomy of .mcp.json
The standard, versionable, shared format looks like this:
{
"mcpServers": {
"shared-server": {
"type": "http",
"url": "https://example.com/mcp"
}
}
}
Two key points. An entry with url but no type is an error: Claude Code then reads the server as stdio, fails, and displays MCP server "<name>" has a "url" but no "type". Add "type": "http" (or "sse" / "ws") explicitly. ${VAR} expansion is accepted in command, args, env, url, and headers, with two forms: ${VAR} (silently fails with a warning if missing) and ${VAR:-default}. This lets you commit a .mcp.json in plain text without exposing secrets — each developer sets the value in their environment.
Managing servers
A set of CLI commands covers the lifecycle.
| Command | Role |
|---|---|
claude mcp add [options] <name> ... | Add a server (default: local scope) |
claude mcp add-json <name> <json> | Add from a JSON string |
claude mcp list | List all configured servers, with health status |
claude mcp get <name> | Detail of one server, resolved endpoint, status |
claude mcp remove <name> | Remove a server (also wipes OAuth tokens) |
claude mcp login <name> | Start the OAuth flow from the shell |
claude mcp logout <name> | Revoke stored authentication |
claude mcp reset-project-choices | Reset .mcp.json approvals |
In an interactive session, the /mcp command opens a panel that lists servers, their status (Connected, Needs authentication, Failed to connect, Pending approval, cached), their tool count, and lets you disable a server without removing it, reconnect it, or clean up its authentication.
OAuth authentication
A remote server that returns 401 or 403 is marked "needs authentication." Open /mcp, select Sign in, a browser opens for the OAuth flow. Alternative on the command line since v2.1.186: claude mcp login <name>.
Two operational subtleties. On an SSH session with no local browser, the command prints the authorization URL to open manually then waits for the pasted redirect URL — connect with ssh -t so the terminal is interactive. And if you configure an Authorization header yourself (through --header or a headersHelper), a 401 will not trigger the OAuth flow: Claude Code assumes the credential comes from you and simply reports the connection as failed.
Kiosque: a complete .mcp.json
The Kiosque team wants two servers: GitHub to read issues, open PRs, read automated reviews (remote HTTP, PAT authentication); Postgres in read-only to query the staging database (local stdio, DSN in environment variable).
Nadia creates the file at the root and commits it. Secrets stay outside, in each local .env:
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer ${GITHUB_MCP_TOKEN}"
}
},
"postgres": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@bytebase/dbhub",
"--dsn",
"${KIOSQUE_DB_DSN:-postgresql://readonly:local@localhost:5432/kiosque}"
]
}
}
}
The @bytebase/dbhub package is cited as it appears in the official documentation (practical example from mcp.md); it exposes Claude to a relational database through a DSN, with a read-only user to prevent any write. The default DSN points at a local database: each developer switches to their own through KIOSQUE_DB_DSN in their environment.
On first opening, Karim sees a workspace trust dialog, accepts, then each server passes through ⏸ Pending approval — he approves. For the GitHub HTTP, he then runs claude mcp login github and completes the OAuth flow (or provides his PAT if the organization requires it). For Postgres, Claude Code launches npx -y @bytebase/dbhub ... on demand at first use.
What Claude can do, once connected
On the GitHub channel, Karim can ask: "Summarize the last 20 open issues tagged bug, group them by module." Claude Code calls search_issues, aggregates, summarizes — without Karim writing any curl. On the Postgres channel: "What is the average basket for orders delivered this month?" — Claude Code composes a read-only SQL query, runs it, returns the result in natural language.
Three precautions worth repeating.
- The Postgres user must be read-only at the database level. A
PreToolUsehook onmcp__postgres__*tools can reinforce, but the database is the source of truth. - The GitHub PAT must be fine-grained and limited to the specific repositories Claude needs. A too-broad token is a dangerous entry point.
- The versioned
.mcp.jsonmust contain no secret, only${VAR}with generic defaults. Real secrets live in local.envfiles or in a manager.
The SDK and custom tools
If no MCP server meets the need, you can write one. The server side follows the official MCP specification and can be coded in any language. On Claude Code's side, a lighter approach is to define a custom tool through the Agent SDK: the agent-sdk__custom-tools.md doc describes a mechanism for exposing a local function as an in-process MCP tool, without running an external server. This is especially useful for Kiosque business tools — for example, calling the internal commission calculation API — when you do not want to publish them as a reusable MCP server.
/mcp in session: the control view
Once configured, /mcp gives the picture at a glance. Each server is displayed with:
- Its transport and endpoint (with
${VAR}unexpanded on display — never a secret on screen). - Its status:
Connected,Needs authentication,Failed to connect,⏸ Pending approval,⊘ Disabled for this project,cached(tools loaded from a discovery cache). - Its tool count.
- A per-server menu:
Sign in,Re-authenticate,Clear authentication,Reconnect, per-project disable.
On failure, the Issue: line gives the returned HTTP code (401, 403, 500…) and the server's error text, never including the full URL — any secrets that might live there are protected.
A server in Failed to connect with a 401: the token is likely wrong or expired. In Failed to connect with a network code: dig or curl the URL by hand to check connectivity. In persistent Pending approval: the workspace is not trusted; type claude in the directory, accept the dialog.
Summary
- MCP standardizes Claude Code's access to external services; four transports:
http(recommended),sse(deprecated),stdio(local),ws(push). - Three scopes: local (
~/.claude.json, private), project (.mcp.json, versioned, requires approval), user (~/.claude.json, all your projects). - CLI:
claude mcp add,add-json,list,get,remove,login,logout,reset-project-choices. In session:/mcpopens the control panel. .mcp.json:{"mcpServers": {...}}format,${VAR:-default}expansion incommand,args,env,url,headers;typemandatory for remote servers.- Authentication: automatic OAuth on remote servers;
claude mcp login <name>without going through/mcp; anAuthorizationheader you provide short-circuits OAuth. - For Kiosque, a two-entry
.mcp.jsonis enough: GitHub HTTP authenticated by PAT; Postgres stdio in read-only through a DSN passed as${VAR:-default}.
Next module: Plugins and marketplaces: package and share your tooling — how to bundle your skills, subagents, hooks, and MCP servers into a single plugin and distribute it to the entire team.