What you will be able to do
- Apply the stdio transport rules for stdout, stderr, and shutdown
- Deploy a Streamable HTTP server securely, and recognise SSE and WebSocket endpoints
- Use the official SDKs to author a server and to connect a client to Claude
- Register stdio, HTTP, SSE, and WebSocket servers in Claude Code
- Configure MCP servers and tool permissions in the Agent SDK, and reason about startup timeouts
1.The stdio transport
With stdio, the client launches the server as a child process. The server reads JSON-RPC messages from stdin and writes them to stdout. Each message is a single request, notification, or response, delimited by a newline, and a message must not contain embedded newlines. The server may write UTF-8 text to stderr for logging. The client may capture, forward, or ignore that output, and should not assume that stderr output means something went wrong.
Shutdown is driven by the client. It closes the server's input stream, waits for the process to exit, and forcibly terminates the process if it does not exit within a reasonable time.
Checkpoint 1 of 8· Put it in order
Put the client's stdio shutdown steps in order
- 1.Wait for the server to exit
- 2.Close the input stream to the child process (the server)
- 3.Forcibly terminate the process if it has not exited within a reasonable time
The client first gives the server a chance to exit cleanly by closing stdin, and only uses the operating system's termination mechanism as a last resort.
“If the server does not exit within a reasonable time, forcibly terminating the process”Source: modelcontextprotocol.io
Sources1
2.Streamable HTTP, sockets, and remote deployment
A remote server uses Streamable HTTP. It exposes a single MCP endpoint that accepts POST, and the client sends every JSON-RPC request or notification as its own POST. The client's Accept header must list both application/json and text/event-stream. The server answers a request either with a single JSON object or with an SSE stream scoped to that request. That stream can carry related notifications, such as progress, before the final response. The server must not send independent requests on the stream. Instead, server-to-client interactions like sampling and elicitation are embedded in results as input requests. If the client posts a notification and the server accepts it, the server returns 202 Accepted with no body. Each POST also carries metadata headers: Mcp-Method on every request, and Mcp-Name on tools/call, resources/read, and prompts/get.
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: get_weatherFor deployment, the sources give three security rules. Servers must validate the Origin header on every incoming connection, to prevent DNS rebinding, and must respond 403 Forbidden when it is present and invalid. A server running locally should bind to 127.0.0.1 rather than 0.0.0.0. And servers should authenticate all connections. Beyond HTTP, Claude Code can also connect to older SSE endpoints (--transport sse) and to WebSocket endpoints ("type":"ws" with a wss:// URL). The provided sources do not go further into hosting or scaling.
Checkpoint 2 of 8· Check yourself
A client POSTs a JSON-RPC notification to a Streamable HTTP server, and the server accepts it. What should the response be?
Notifications get no JSON-RPC result. An accepted notification gets 202 with an empty body, and a rejected one gets an HTTP error status such as 400.
“If the server accepts it, the server MUST return HTTP status code 202 Accepted with no body.”Source: modelcontextprotocol.io
Sources2
3.Authoring with the official SDKs
You rarely write JSON-RPC by hand. In Python, the mcp package provides FastMCP. In a sample server from the MCP tunnels quickstart, a function decorated with @mcp.tool(), with type hints and a docstring, becomes a tool, and mcp.run(transport="streamable-http") serves it over HTTP. The client side looks similar in TypeScript. With @modelcontextprotocol/sdk, you create a transport, create a Client, and connect. Anthropic's TypeScript SDK then provides helpers (mcpTools, mcpMessages, mcpResourceToContent) that convert the server's tools, prompts, and resources into Claude API types.
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const anthropic = new Anthropic();
// Connect to an MCP server
const transport = new StdioClientTransport({ command: "mcp-server", args: [] });
const mcpClient = new Client({ name: "my-client", version: "1.0.0" });
await mcpClient.connect(transport);Checkpoint 3 of 8· Check yourself
In the sample above, what does StdioClientTransport({ command: "mcp-server", args: [] }) do?
A stdio transport launches the server as a child process and exchanges messages over its standard streams. Converting to Claude API types is the job of the separate helpers such as mcpTools.
“The server reads JSON-RPC messages from stdin and writes JSON-RPC messages to stdout.”Source: modelcontextprotocol.io
4.Connecting a server to Claude Code
How you register a server with claude mcp add depends on what you were given. A URL means a remote server, so use --transport http (or sse). A launch command means a local server, so use --transport stdio, put the command after --, and pass any secrets with --env. For anything else, such as a WebSocket endpoint, use claude mcp add-json with a JSON config.
# Real example: Add Airtable server
claude mcp add --env AIRTABLE_API_KEY=YOUR_KEY --transport stdio airtable \
-- npx -y airtable-mcp-serverclaude mcp add-json events-server \
'{"type":"ws","url":"wss://mcp.example.com/socket","headers":{"Authorization":"Bearer YOUR_TOKEN"}}'In JSON, a url entry needs an explicit type of http, sse, or ws. Claude Code treats an entry without a type as a stdio server, so the connection fails. Server names may contain only letters, numbers, hyphens, and underscores. To manage servers, use claude mcp list, claude mcp get <name>, and claude mcp remove <name>; inside a session, /mcp shows server status. Project-scoped servers from .mcp.json show as pending until you approve them in an interactive claude session. Claude Code also warns about hidden whitespace in config values and about the same server name being defined in more than one scope.
Checkpoint 4 of 8· Fill the gap
Which transport value connects Claude Code to Notion's remote endpoint?
claude mcp add --transport ? notion https://mcp.notion.com/mcpA remote MCP URL is added with --transport http. stdio is only for servers launched as local commands.
Source: code.claude.comCheckpoint 5 of 8· Exam question
A platform engineer is integrating a new MCP server into an internal Claude Code agent. The vendor's documentation instructs users to run `npx internal-tools-mcp` as a local process on the same machine that hosts the agent, and gives no network endpoint. Which transport type should the engineer configure for this server?
Correct answer: A — stdio
- A. Correct. When documentation gives a command to run rather than a URL, the server is a local process communicating over stdin/stdout, which is the stdio transport.
- B. Incorrect. SSE is used for cloud-hosted or remote servers reachable by URL, not a local command.
- C. Incorrect. Streamable HTTP also requires a URL endpoint; it does not apply to a locally run command.
- D. Incorrect. WebSocket is not one of the MCP transport types supported by the Agent SDK's mcpServers configuration.
Sources5
5.Connecting a server to the Agent SDK
In the Agent SDK, you pass servers in the mcpServers option (mcp_servers in Python). These use the same shapes as before: type and url for a remote server, or command and args for a stdio server. The same JSON can live in a .mcp.json file. Configuring a server does not by itself give the agent permission to use its tools. You grant that in allowedTools, where each tool has the name mcp__<server>__<tool>, so a wildcard like mcp__claude-code-docs__* allows every tool on that server.
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Use the docs MCP server to explain what hooks are in Claude Code",
options: {
mcpServers: {
"claude-code-docs": {
type: "http",
url: "https://code.claude.com/docs/mcp"
}
},
allowedTools: ["mcp__claude-code-docs__*"]
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}How quickly servers connect at startup affects the first turn. With tool search, which is on by default, the first turn waits only for servers marked alwaysLoad: true, and the others keep connecting in the background.
| Server type | Delays first turn? | Timeout |
|---|---|---|
| stdio server, or HTTP/SSE server without a cached tool list | Yes, until it connects | MCP_TIMEOUT, 30 seconds by default |
| Remote server with a cached tool list | No; cached tools are available from the first turn | None at startup; its deferred connect has its own timeout |
| In-process SDK server | Yes, until it connects and lists its tools | MCP_TIMEOUT per connect attempt |
Checkpoint 6 of 8· Check yourself
An agent configures a server named filesystem but Claude never calls its tools. Which allowedTools entry grants all of that server's tools?
MCP tools are namespaced as mcp__<server>__<tool>, and a trailing wildcard matches every tool on that server.
“allowedTools: ["mcp__filesystem__*"]”Source: code.claude.com
Checkpoint 7 of 8· Exam question
A team wants to connect their agent to a third-party analytics MCP server. The provider's documentation gives a hosted endpoint URL, `https://analytics.example.com/mcp`, rather than a local command to run. Which configuration best matches this integration pattern?
Correct answer: A — Set the server's "type" to "http" and provide the given URL
- A. Correct. A documented URL indicates a remote, cloud-hosted server, so the server should be configured with "type": "http" (or "sse") and the given URL.
- B. Incorrect. stdio is for local processes launched with a command; there is no local binary to run here.
- C. Incorrect. An SDK MCP server is for custom in-process tools you write yourself, not for connecting to an already-hosted third-party server.
- D. Incorrect. "command"/"args" configure a stdio server that spawns a local process; a URL cannot be passed as an argument to achieve HTTP connectivity.
Checkpoint 8 of 8· Exam question
A developer configures an MCP server named `github` that exposes a `list_issues` tool. To auto-approve only this specific tool, without granting access to any other tool on the server, which entry should be added to `allowedTools`?
Correct answer: A — mcp__github__list_issues
- A. Correct. MCP tool names follow the pattern mcp__{server_name}__{tool_name}, so this fully qualified name approves only list_issues from the github server.
- B. Incorrect. This omits the required "mcp__" prefix and does not match the naming convention Claude uses for MCP tools.
- C. Incorrect. This wildcard would approve every tool on the github server, not just list_issues.
- D. Incorrect. The server and tool segments are reversed and missing the "mcp__" prefix, so it will not match the actual tool name.
Sources6
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.A stdio server can print debug output to stdout, and the client will filter it out.Why is that wrong?
stdout may carry only valid MCP messages. Logging belongs on stderr, which clients should not treat as a sign of errors.
Covered in The stdio transport
2.A JSON server entry with just a url is enough, because Claude Code infers HTTP from the URL.Why is that wrong?
An entry without a type is read as stdio, so a url entry must set type to http, sse, or ws.
Covered in Connecting a server to Claude Code
3.A locally running HTTP MCP server should bind to 0.0.0.0 so every client can reach it.Why is that wrong?
Local servers should bind only to localhost, and must validate the Origin header to prevent DNS rebinding.
Practise it for real
Register a local stdio MCP server in Claude Code, check that it connects, and remove it again.
1.Run: claude mcp add --transport stdio myserver -- npx server (replace npx server with the launch command of a server you have)
Why: A launch command means the server runs on your machine, so it is registered as stdio with the command after --.
You should see: Claude Code saves a server named myserver that will run your command.
2.Run: claude mcp list, then claude mcp get myserver
Why: These commands show the configured servers along with their status and any warnings, such as hidden whitespace.
You should see: myserver appears with its command and connection status.
3.Start claude and type /mcp
Why: /mcp shows the status of each server from inside a session.
You should see: myserver shows as connected, along with its tools.
4.Run: claude mcp remove myserver
Why: This cleans up the test registration.
You should see: myserver no longer appears in claude mcp list.
Stuck? Get a nudge
If the server shows as failed, check that the command after -- runs on its own in your shell, and that the server writes nothing but MCP messages to stdout.
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“Messages are delimited by newlines, and MUST NOT contain embedded newlines.”
↩︎ The stdio transport“SHOULD NOT assume stderr output indicates error conditions”
↩︎ The stdio transport“The server MUST NOT write anything to its stdout that is not a valid MCP message.”
↩︎ Exam trap 1“If the server does not exit within a reasonable time, forcibly terminating the process”
↩︎ Checkpoint“The server reads JSON-RPC messages from stdin and writes JSON-RPC messages to stdout.”
↩︎ Checkpoint - 2.https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-httpOfficial docs
“The server exposes a single HTTP endpoint (the MCP endpoint) that accepts POST.”
↩︎ Streamable HTTP, sockets, and remote deployment“Servers MUST validate the Origin header on all incoming connections to prevent DNS rebinding attacks.”
↩︎ Streamable HTTP, sockets, and remote deployment“The server MUST NOT send independent JSON-RPC requests on this stream.”
↩︎ Streamable HTTP, sockets, and remote deployment“When running locally, servers SHOULD bind only to localhost (127.0.0.1) rather than all network interfaces (0.0.0.0).”
↩︎ Exam trap 3“If the server accepts it, the server MUST return HTTP status code 202 Accepted with no body.”
↩︎ Checkpoint - 3.
“from mcp.server.fastmcp import FastMCP”
↩︎ Authoring with the official SDKs - 4.
“These helpers convert MCP types to Claude API types, reducing boilerplate when working with MCP tools, prompts, and resources.”
↩︎ Authoring with the official SDKs - 5.https://code.claude.com/docs/en/mcpOfficial docs
“A launch command such as npx -y @example/mcp-server: the server runs on your machine.”
↩︎ Connecting a server to Claude Code“Run claude interactively to review and approve it.”
↩︎ Connecting a server to Claude Code“Claude Code reads an entry with no type as a stdio server, so a url entry without a type fails.”
↩︎ Exam trap 2 - 6.https://code.claude.com/docs/en/agent-sdk/mcpOfficial docs
“Configure MCP servers to extend your agent with external tools.”
↩︎ Connecting a server to the Agent SDK“make its tools available at their full schemas on the first turn, exempt from tool search deferral”
↩︎ Connecting a server to the Agent SDK“MCP_TIMEOUT, 30 seconds by default; the connection fails at that deadline”
↩︎ Connecting a server to the Agent SDK“allowedTools: ["mcp__filesystem__*"]”
↩︎ Checkpoint