Skip to main content
AI agents and MCP · Lesson 1 of 4 · 11 min

What Is an MCP Server? How One Tool Call Travels

What an MCP server is, from zero: how one AI agent tool call travels from client to server and back, and when to use stdio or Streamable HTTP transport.

By Matthews WongReviewed on Checked against MCP specification 2026-07-28

Key takeaway

An MCP server is a program or web service that offers tools and data to an AI application over the Model Context Protocol. The model asks for a tool, the host checks the call, its MCP client sends a JSON-RPC tools/call, and the result comes back as text in the model's context. stdio carries local servers; Streamable HTTP carries remote ones.

You will be able to

  • Name the host, the client and the server in an MCP setup, and say which one decides whether a tool runs.
  • Follow one tool call from the model's request to the result in its context, message by message.
  • Choose stdio or Streamable HTTP for a server, and know what each one exposes.
Watch it work

Step through the diagram

Press Play, or step with the arrows. Every step is written out below the diagram.

One MCP tool call, from request to result

The client lists the server's tools, the model asks for one call, the host checks it, the client sends tools/call, and the result returns to the model's context as text.

Reviewed on 17 October 2026 · MCP specification 2026-07-28

Step 1 of 6. The host is the AI app, such as Claude Code. It runs one MCP client per server. The server offers tools; the model sees only their names, schemas and results.

Keys: ← → step · Home / End · K play or pause

  1. The host is the AI app, such as Claude Code. It runs one MCP client per server. The server offers tools; the model sees only their names, schemas and results.

  2. First the client asks the server for tools/list. Each tool comes back with a name, a description and an input schema, which the host shows to the model.

    tools/list → name · description · inputSchema
  3. From the user's request and those descriptions, the model asks for one call: a tool name plus arguments. It cannot run anything itself; it can only ask.

    tool: search_issues · arguments: query=crashPredict the next step The model has asked for a call. What happens before anything reaches the server? (The server runs it at once · The host checks its rules or asks you)
  4. The host checks the call against its permission rules, or asks you. The MCP spec says a human should always be able to deny a tool invocation.

    Claude Code checks deny, then ask, then allow
  5. The client sends a JSON-RPC tools/call. Since spec 2026-07-28 there is no handshake first: every request carries its own protocol version in _meta.

    method: tools/call · _meta: protocolVersion 2026-07-28Predict the next step The server answers. How does the model receive the text in that result? (As checked, trusted fact · As more text in its context · As a command the host must run)
  6. The server returns content, usually text, plus isError if the tool failed. The client hands it to the model as more context; nothing checked that it is true.

    resultType: complete · content: text · isError: false

What is an MCP server, in plain words?

The Model Context Protocol (MCP) is an open standard for connecting AI applications to outside tools and data. An MCP server is the side that offers them: a small program on your machine, or a web service, that lists the tools it has and runs one when asked. Typical servers wrap an issue tracker, a database, a cloud API or a folder of files.

Read more (2)

The specification names three roles. The host is the AI application, such as Claude Code or a desktop chat app. The host creates one client per server, and each client talks to exactly one server. The host keeps the whole conversation and enforces consent; by design a server receives only what it needs and should not be able to see into other servers.

A server can offer three kinds of things. Tools are functions the model may ask to run. Resources are context and data, each addressed by a URI. Prompts are templated messages a user picks. This lesson follows a tool call, because tools are where an agent stops reading and starts acting.

How does one tool call travel from the model to the server and back?

The model never talks to a server. It asks the host for a call by naming a tool and its arguments; the host decides whether that call may run, and only then does its MCP client send a JSON-RPC request to the server. The result travels back the same way and lands in the model's context as text.

Read more (3)

Discovery comes first. The client sends tools/list and receives each tool's name, description and input schema. The description is what the model reads to decide when a tool fits, which is why a vague or hostile description changes what an agent does. The spec adds that clients must treat tool annotations, such as a read-only hint, as untrusted unless they come from a trusted server.

The call itself is a tools/call request with the tool name and arguments. Since the 2026-07-28 revision MCP is stateless: there is no initialize handshake and no protocol session, and every request carries its protocol version and client capabilities in a _meta field. Servers built for 2025 revisions still expect the handshake, and clients detect which kind they are talking to.

The reply is a result with a content array, usually text, and isError set to true when the tool ran but failed, for example on a date in the wrong format. An unknown tool or a malformed request is a protocol error instead. Either way the client passes the outcome to the model, which reads it as input; nothing on the way checked that it is true.

stdio or Streamable HTTP: which transport carries the call?

The transport is how the messages move; the messages themselves are the same. With stdio, the host launches the server as a child process on your machine and they exchange one JSON-RPC message per line over its standard input and output. With Streamable HTTP, the server is an independent service, and each message is an HTTP POST to a single endpoint.

Read more (2)

stdio suits local tools: there is no port and no network listener. The server runs with your user's privileges, so trusting the command you launch matters. The spec says a stdio server must write nothing but MCP messages to stdout, and may log to stderr.

Streamable HTTP suits shared or remote servers. The reply is either one JSON object or a stream of events for that one request. Because the server is reachable over a network, the spec requires it to validate the Origin header, says a local server should bind only to 127.0.0.1, and says every connection should be authenticated. The older HTTP+SSE transport is deprecated.

How this site does it

The real config behind this lesson

Excerpts from the repository that builds and deploys this website, quoted as they are.

app/components/blogs/KubernetesMcpServerReadOnlyToolsets.tsx:327-332
const config = process.argv[2];
const server = spawn(
  "npx",
  ["-y", "kubernetes-mcp-server@0.0.67", "--config", config],
  { stdio: ["pipe", "pipe", "inherit"] },
);

The audit script printed in this site's Kubernetes MCP article is a tiny MCP client, and this is the stdio transport in six lines: the client launches the server as a child process and owns its stdin and stdout. stderr is inherited, so the server's logs reach the terminal without mixing into the message stream. The version is pinned, because a server you launch is code you run.

app/components/blogs/KubernetesMcpServerReadOnlyToolsets.tsx:351-356
send({ jsonrpc: "2.0", id: 1, method: "initialize",
  params: { protocolVersion: "2025-06-18", capabilities: {}, clientInfo: { name: "audit", version: "1" } } });
setTimeout(() => {
  send({ jsonrpc: "2.0", method: "notifications/initialized" });
  send({ jsonrpc: "2.0", id: 2, method: "tools/list" });
}, 3000);

The same script's messages speak the 2025-era protocol: an initialize request, the initialized notification, then tools/list, which suited the server version the article tested. Under the 2026-07-28 revision the handshake is gone. A client can send tools/list straight away with its version in _meta, and a client that also supports older servers should probe with server/discover first.

What breaks

Mistakes that cost real time

  • A stdio server that prints to stdout

    A stray console.log in a stdio server lands in the message stream, and the client fails to parse it as JSON-RPC. The spec says a stdio server must write only valid MCP messages to stdout. Send every log line to stderr.

  • Trusting a tool description you never read

    The model picks tools from their descriptions. A description that claims more than the tool does, or tells the model what to do next, steers the agent. Read a server's tools/list output before you connect it to anything real.

  • Mixing protocol eras

    A 2025 tutorial opens every connection with initialize and keeps an Mcp-Session-Id header. The 2026-07-28 revision removed both, so old snippets in a new SDK, or the reverse, fail in confusing ways. Check which revision your client and server speak first.

  • A local HTTP server open to the network

    A Streamable HTTP server bound to 0.0.0.0 with no authentication answers other machines too, and without Origin checks a web page can reach it through DNS rebinding. Bind to 127.0.0.1 and require a token.

The two standard MCP transports
stdioA child process the host launches on your machineOne JSON-RPC message per line on stdin and stdoutThe command you launch runs with your privileges
Streamable HTTPAn independent service, local or remoteOne HTTP POST per message; the reply is JSON or an event streamAnyone who can reach the endpoint: check Origin, require auth
Try it yourself

Do it on your own machine

You need a terminal and Node.js 22.19 or newer, which the MCP Inspector requires. No account and no API key. The Inspector is the protocol's reference debugging client, so you watch real messages with no AI model in the loop.

  1. Make a small practice folder for the filesystem reference server to serve.

    mkdir -p ~/mcp-practice && echo hello > ~/mcp-practice/note.txt
  2. Ask the server for its tool list over stdio. The Inspector launches the server as a child process, exactly as a host would.

    npx -y @modelcontextprotocol/inspector --cli npx @modelcontextprotocol/server-filesystem ~/mcp-practice --method tools/list
  3. Find list_directory in the output and read its description and input schema. That text is all a model would know about the tool.

  4. Call the tool. The content in the result is the text an agent would read back into its context.

    npx -y @modelcontextprotocol/inspector --cli npx @modelcontextprotocol/server-filesystem ~/mcp-practice --method tools/call --tool-name list_directory --tool-arg path=$HOME/mcp-practice
  5. Open the web Inspector on the same server to see the exchange as JSON-RPC messages, one request and one response at a time.

    npx -y @modelcontextprotocol/inspector npx @modelcontextprotocol/server-filesystem ~/mcp-practice
Quick quiz

Three questions before you move on

Pick an answer to see why it is right or wrong. Nothing is graded, so guess freely; a miss is kept in this browser so the course can ask it again later.

  1. 1.In an MCP setup, which part decides whether a tool call is allowed to run?
  2. 2.Which transport launches the server as a child process on your machine?
  3. 3.Under the 2026-07-28 spec, what must a client send before its first tools/call?
Think it through

Answer in your head, then check

  1. 1.Why can a badly written tool description make an agent misbehave even when the tool's code is fine?Show the answer

    Answer:The model chooses tools and arguments only from their names, descriptions and schemas. A description that overstates what a tool does, or carries instructions, steers that choice before any code runs.

  2. 2.A tool ran, but the date argument was in the wrong format. Does the client get a JSON-RPC error?Show the answer

    Answer:No. That is a tool execution error: a normal result with isError set to true and a message in content, which the model can read and correct. JSON-RPC errors are for protocol problems, such as an unknown tool or a malformed request.

  3. 3.Why does a stdio server write its logs to stderr and not stdout?Show the answer

    Answer:stdout carries the JSON-RPC messages, one per line, and the spec says a stdio server must write nothing else there. Anything extra corrupts the stream; stderr is free for logs.

Go deeper

Related deep dives on this site