MCP Architecture Explained: Host, Client, Server and Transports
To test something well, you must know how its parts talk to each other. MCP architecture has only a few parts, and each has a clear job. In this lesson you will follow one tool call from start to finish, using a small program that you can run yourself.
📖 What You’ll Learn
- What the host, client and server each do
- How messages travel between them
- The difference between stdio and Streamable HTTP
- What changed in the 2026-07-28 protocol
💡 MCP Architecture: The Three Parts
Explain Like I’m 5: Ordering by Phone
Imagine: You want pizza. Your parent holds the phone and speaks to the shop. The shop makes the pizza.
What it really means: The host is the app, the client handles the call and the server does the work.
In testing: An AI assistant asks a browser server to read a page.
| Part | Job | QA example |
|---|---|---|
| Host | The AI application the user works in | An AI coding assistant |
| Client | Keeps the connection to one server | The assistant’s link to the browser server |
| Server | Offers the tools and does the work | Playwright MCP |
One host can have several clients, and each client talks to one server. The model does not connect to the server directly. It asks the host, and the host’s client makes the call.
🔍 How One Tool Call Travels
- The client asks the server for its tools with
tools/list. - The host gives those tool descriptions to the model.
- The model asks to use a tool.
- The host can ask a person to approve it.
- The client sends
tools/callto the server. - The server runs the tool and returns the result.
- The host gives the result back to the model.
Step 4 is your safety point. You met this same idea in the agent loop.
🧩 Two Ways to Connect
Explain Like I’m 5: Talking or Posting
Imagine: You can talk to someone sitting next to you, or post a letter to someone far away.
What it really means: Stdio is for a local helper, and HTTP is for a server elsewhere.
In testing: Run a test helper on your laptop, or share a team server.
| Point | stdio | Streamable HTTP |
|---|---|---|
| Where it runs | On the same machine | On a local or remote server |
| Who starts it | The client starts it as a process | It runs independently |
| How messages move | One JSON message per line | Each message is an HTTP POST |
| Main risk | It runs with your local access | It is reachable over a network |
Never print to stdout in a stdio server
On stdio, the output stream carries only protocol messages. One stray console.log can break the connection. Write your logs to stderr instead.
🛠️ See the Messages
MCP messages use a format called JSON-RPC. This small program has no dependencies. It is a simplified model of a stdio exchange, with text passing in and out one line at a time.
type RpcRequest = {
jsonrpc: '2.0';
id: number;
method: string;
params: {
name?: string;
arguments?: Record<string, string>;
_meta: { 'io.modelcontextprotocol/protocolVersion': string };
};
};
const VERSION = '2026-07-28';
// The server: one line in, one line out.
function server(line: string): string {
const req = JSON.parse(line) as RpcRequest;
const reply = (body: object) => JSON.stringify({ jsonrpc: '2.0', id: req.id, ...body });
if (req.params._meta['io.modelcontextprotocol/protocolVersion'] !== VERSION) {
return reply({ error: { code: -32022, message: 'Unsupported protocol version' } });
}
if (req.method === 'tools/list') {
return reply({ result: { resultType: 'complete', tools: [{ name: 'get_run_status' }] } });
}
if (req.method === 'tools/call' && req.params.name === 'get_run_status') {
const text = `Run ${req.params.arguments?.runId}: 2 failed`;
return reply({ result: { resultType: 'complete', content: [{ type: 'text', text }] } });
}
return reply({ error: { code: -32602, message: 'Unknown tool' } });
}
// The client: sends a request and reads the reply.
let nextId = 1;
function send(method: string, extra: object = {}, version = VERSION) {
const params = { ...extra, _meta: { 'io.modelcontextprotocol/protocolVersion': version } };
return JSON.parse(server(JSON.stringify({ jsonrpc: '2.0', id: nextId++, method, params })));
}
console.log(send('tools/list').result.tools[0].name);
console.log(send('tools/call', { name: 'get_run_status', arguments: { runId: '17' } }).result.content[0].text);
console.log(send('tools/call', { name: 'delete_everything' }).error.code);
console.log(send('tools/list', {}, '2020-01-01').error.code);
get_run_status
Run 17: 2 failed
-32602
-32022
Real responses have a few more required fields, and real projects use an SDK. You will use one in Build an MCP Server for QA.
📌 What Changed in the 2026-07-28 Revision
Explain Like I’m 5: No More Name Badges
Imagine: Instead of checking in at the door once, every note you pass says who you are.
What it really means: Each request now carries its own version and details.
In testing: A server can handle any request without remembering the last one.
| Older revisions | 2026-07-28 revision |
|---|---|
Start with an initialize handshake | No handshake, details travel in _meta |
| Sessions tied to a connection | No protocol-level sessions |
| Server could start requests | Server answers, and asks for input in its results |
Many servers and clients still use the older style. Modern and older software do not always work together, so check the versions when a connection fails.
🌍 Real-Life Example: The Silent Server
A tester adds a debug message to a local MCP server. The server starts, but the assistant reports a connection error. The stray message went to stdout and broke the protocol stream. Moving the message to stderr fixes it immediately.
🧪 Try It Yourself
- Run the example with
npx tsx messages.ts. - Add a second tool to the server.
- Print the raw JSON line for one request.
- Find the
idin the request and the matching response. - Change the version and read the error code.
📋 Ready-to-Copy Prompt
You are an MCP expert teaching a software tester.
Here is a JSON-RPC message from an MCP connection:
[paste a message with no secrets]
Explain in simple words:
1. Who sent it: the client or the server
2. What it is asking for or returning
3. Which fields a tester should check
4. What a bad version of this message might look like
Check your answer against the 2026-07-28 revision. Tell me if you are unsure.
⚠️ Common Mistakes
- Thinking that the model connects directly to the server
- Mixing up the host and the client
- Writing logs to stdout on a stdio server
- Exposing an HTTP server without authentication
- Debugging with a guide written for an older revision
🎯 Key Takeaways
- The host is the app, and the client talks to one server.
- Tools are discovered with
tools/listand run withtools/call. - Use stdio locally and Streamable HTTP for shared servers.
- The newest revision carries its details in every request.
🎤 Interview Questions
1. Explain the host, client and server in MCP.
Model answer: The host is the AI application. It creates a client for each server, and the client manages that connection. The server exposes the tools and carries out the work.
2. What are the MCP transports?
Model answer: The standard transports are stdio and Streamable HTTP. Stdio suits a local process started by the client. Streamable HTTP suits a server that runs independently and may be shared.
3. Why must a stdio server not write logs to stdout?
Model answer: Stdout is reserved for protocol messages, so anything else can corrupt the stream. Logs should go to stderr.
4. What changed in the 2026-07-28 revision?
Model answer: The handshake and protocol-level sessions were removed. Each request carries its own version and capability details in metadata, which makes servers stateless at the protocol level.
5. How would you debug a failed MCP connection?
Model answer: I would check the launch command, the protocol versions and the server’s stderr output. I would also look for stray stdout output, then call tools/list from a test client.
📝 Assignment
Draw the path of one tool call for an AI assistant using a test-report server. Label the host, client, server, transport and human approval point. Then list one failure that could happen at each arrow.
❓ FAQ
Do I need to write JSON-RPC by hand?
No. SDKs handle it for you. Reading the messages is still useful when you are debugging.
Can one AI application use several MCP servers?
Yes. It creates one client for each server. Remember that each extra server adds more tools and more risk.
🚀 Next Steps
Well done on finishing this lesson. Complete the assignment, then continue with Playwright MCP Workflow for Testers: Explore Safely, Then Automate.