Skip to lesson
supraj.dev THE ENGINEERING HANDBOOKS
LEARN / BUILD / VERIFY2026 edition · checked 06 Oct

CHAPTER 05 / 30 · Understand the contract

stdio is a transport, not a sandbox

Keep protocol output clean and reason about the privileges of a local server process.

4 min read + practiceWorked exerciseInterview practice

The mechanism

A stdio client launches or connects to a child process and exchanges newline-delimited UTF-8 JSON messages over standard input and output. Standard output belongs to the protocol. Diagnostic text belongs on standard error. A harmless-looking startup banner on stdout can corrupt the first exchange.

The child process still has operating-system permissions. MCP does not automatically isolate its filesystem, network, environment or subprocesses. A package launched for convenience may read everything the launching account can read unless another boundary restricts it.

For the handbook, keep the server read-only and in-memory. Review its source and dependencies before enabling it in a host. Do not pass a broad home directory, Docker socket or inherited secret collection merely because the command is labelled local.

Host process
stdin: requests
Child server
stdout: protocol only

Worked example

This Python fragment demonstrates stream discipline only, not a complete MCP server. The application logger writes to stderr. JSON serialization escapes embedded newlines so each message occupies one physical line. The SDK should normally own framing and protocol handling in the finished implementation.

import json
import sys

def diagnostic(message):
    print(message, file=sys.stderr, flush=True)

def emit_fixture(message):
    sys.stdout.write(json.dumps(message, separators=(",", ":")) + "\n")
    sys.stdout.flush()

# Do not call emit_fixture alongside a running SDK transport.
diagnostic("Synthetic fixture loaded; no external data accessed")

Practice: predict, inspect, explain

Offline exercise. Inspect the code rather than launching an installed MCP host. Predict the destination of the diagnostic. Create a string containing a newline, serialize it with json.dumps, and count the physical lines in the serialized output. Then inventory the environment variables and directories your proposed server actually needs, without reading their values.

Expected observation: message framing can be correct while the process remains overprivileged. Write separate acceptance criteria for protocol cleanliness and runtime permissions. Both must pass before a useful local integration is considered ready.

Troubleshooting and trade-offs

A hanging startup may be a buffered stdout write, a child waiting for input or a host that expects a different era. A parse error often points to a print statement or dependency banner. When shutting down, use the documented transport lifecycle and bounded termination behavior. Closing stdin supports graceful completion; killing a process cannot reverse an already committed external effect.

Interview practice

Why is stderr allowed for logs?

It is outside the JSON message stream, so diagnostic output does not corrupt protocol framing. Logs still require redaction because hosts may collect or display them.

What does local execution authorize?

Only the explicitly intended operation. The process may technically inherit wider permissions, so constrain its runtime and capability surface independently of the transport.

Completion check

Explain two independent failures: a clean protocol with excessive privileges, and a narrow process with invalid stdout framing.

Sources and version notes

This edition targets MCP 2026-07-28, checked 6 October 2026. SDK examples are version-sensitive and labelled when not executed. Synthetic fixtures are learning material, not protocol conformance evidence.

YOUR NEXT STEP

Make the understanding yours.

Use the completion check above. Mark this chapter when you can explain the mechanism and its limits.

Self-assessed reading progress. This does not certify that a lab ran or a system is secure.