Skip to main content
>_ supraj.dev

Module 5: Multi-Agent & MCP Standards · 3.5h

01 · UNDERSTAND

Day 27 Theory — Build a Local MCP Server with STDIO

Why STDIO is useful

STDIO transport lets a host start a local MCP server process and communicate over standard input/output streams.

This is convenient for local developer tools because no network listener or port is required.

Host process
   │ starts
   ▼
MCP server process
   ▲             │
 stdin/stdout JSON messages

Process boundaries matter

The server is a separate process. It has its own environment, working directory, permissions and lifecycle.

Many “MCP bugs” are actually process-launch problems: missing environment variables, wrong executable path, or unexpected current directory.

Never write logs to protocol stdout

If stdout carries protocol messages, arbitrary debug prints can corrupt the stream.

Use stderr or a configured logging destination for diagnostics.

Capability discovery in the current protocol

In the MCP 2026-07-28 core, the older initialize / initialized handshake is no longer part of the protocol lifecycle. A current client and server exchange supported requests directly according to the capabilities and transport they implement.

Older SDK examples may still show initialization because they target an earlier MCP protocol version. Always match code examples to the negotiated/documented protocol version rather than mixing lifecycle rules from different releases.

After connection, the client can request exposed capabilities such as tools. Tool metadata should be clear enough for both humans and models to understand the contract.

Tool implementation boundaries

The MCP server validates tool input and owns the implementation.

Do not assume that because the caller is “local” it is automatically trusted.

The host and server may have different privilege levels.

Service Desk connection

Today the Service Desk extracts a local knowledge/tool capability behind a real MCP server boundary.

The principle is:

STDIO gives local interoperability without a network port, but the process boundary, protocol stream, protocol-version contract and permission boundary are still real.

02 · APPLY

Lesson goal

Today we build the first concrete MCP capability boundary: a local server process connected over STDIO.

By the end of the lesson you should be able to:

  • explain what STDIO transport does and does not provide,
  • keep protocol stdout clean,
  • expose read-only Service Desk tools with explicit schemas,
  • trace one JSON-RPC request/response at the process boundary,
  • distinguish process-launch failures from protocol failures,
  • apply least privilege even though the server is local,
  • test get_ticket and search_kb through the Day 27 protocol harness.

What changed in the Service Desk today?

On Day 26 we learned the MCP architecture. Today one capability moves out of the host and behind a separate local process.

graph LR
    Host[Service Desk Host] -->|stdin protocol messages| Server[MCP Server Process]
    Server -->|stdout protocol messages| Host
    Server --> Ticket[Ticket Store]
    Server --> KB[Knowledge Base]

The important boundary is not “same laptop.” The server has its own process lifecycle, environment, permissions and protocol stream.

Why STDIO is useful

STDIO lets the host launch a child process and exchange MCP messages using the process's standard input/output streams.

Benefits for local tools include:

  • no listening network port,
  • simple parent/child lifecycle,
  • host can control the executable and environment,
  • useful for local developer integrations.

STDIO is not a security sandbox by itself. A local child process can still have dangerous filesystem/network/credential access if we give it those permissions.

The process boundary

When the host starts the server, several things can fail before MCP logic even runs:

wrong executable path
missing Python/module
wrong working directory
missing environment variable
permission denied
process exits immediately

This is why “MCP server failed” is not specific enough for debugging.

First ask whether the process started successfully. Then ask whether protocol messages are valid.

Keep stdout protocol-clean

For an STDIO protocol server, stdout is not a normal debug console.

Bad:

print("Starting MCP server!!!")

if that text is written into the protocol stdout stream.

Why?

The client expects protocol-framed messages. Arbitrary text can corrupt framing/parsing.

Use stderr or a configured logging destination for diagnostics.

Conceptually:

stdout -> protocol only
stderr -> diagnostic logs

This is one of the most important practical rules of local STDIO integrations.

Expose narrow tools

For this day the Service Desk exposes read-oriented capabilities such as:

get_ticket(ticket_id)
search_kb(query)

A good tool contract has:

  • clear name,
  • clear purpose,
  • explicit input schema,
  • predictable structured output,
  • defined error behavior.

Avoid a generic tool such as:

run_anything(command: str)

That erases the safety benefits of a narrow capability boundary.

Tool schema example

Conceptually, get_ticket expects something like:

{
  "ticket_id": "INC-4821"
}

The server should validate the identifier before querying the backend.

A malformed call should produce a structured protocol/tool error rather than an unrelated traceback dumped onto stdout.

Wire-level mental model

A tool invocation still follows JSON-RPC request/response concepts.

Simplified request:

{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tools/call",
  "params": {
    "name": "get_ticket",
    "arguments": {
      "ticket_id": "INC-4821"
    }
  }
}

Simplified lifecycle:

Host serializes request
        ↓
request bytes written to server stdin
        ↓
server parses + validates
        ↓
tool implementation executes
        ↓
server serializes result
        ↓
response bytes written to stdout
        ↓
host correlates response ID

Use the current MCP SDK/protocol implementation for actual framing. The wire example is here so you understand what the SDK is doing for you.

Do not reintroduce the old handshake model

Day 26 established that the 2026-07-28 MCP core no longer requires the old initialize / initialized protocol handshake.

Do not teach Day 27 as:

STDIO means initialize() -> session -> list tools

The transport is STDIO; the current protocol lifecycle remains the 2026-07-28 model taught yesterday.

If an SDK surface still contains compatibility APIs for older protocol generations, identify the version explicitly rather than blending them into the current lesson.

Server permissions

A local MCP server should receive only the access it needs.

For the read-only Day 27 server:

Allowed:
read approved ticket records
read approved KB records

Not automatically allowed:
reset passwords
modify tickets
read arbitrary home directory files
use every host credential

The fact that the host launched the process does not mean the process should inherit all host privileges.

Environment hygiene

When launching a child process, be deliberate about environment inheritance.

Weak pattern:

pass the entire developer shell environment

Better question:

Which environment variables does this server actually require?

A local process can accidentally inherit cloud keys, tokens, database credentials or debugging secrets unrelated to its job.

Error categories

Process launch error

Example:

python: module service_desk.mcp_server not found

Fix process configuration, not tool schema.

Protocol parse/framing error

Inspect stdout contamination, malformed messages or incompatible protocol assumptions.

Unknown tool

Return a structured error. Do not silently route to a “closest” tool.

Invalid arguments

Reject before executing backend logic.

Backend/domain failure

Represent the capability failure clearly without crashing the server loop when recovery is possible.

Practical lab

The authoritative Day 27 outline defines the focused verification suite as:

pytest service-desk-day-27/tests/test_stdio_mcp.py -v

Task A — inspect tools.py

Find the implementations for:

  • get_ticket,
  • search_kb.

For each tool, write down:

input contract
output contract
external data touched
whether it mutates state

Task B — inspect the server entry point

Identify:

  • how the MCP server starts,
  • how tools are registered,
  • how logs are routed,
  • what environment/config it receives.

Task C — protect stdout

Add or run a test proving diagnostic logging does not inject arbitrary text into the protocol output stream.

Task D — trace one call

For get_ticket("INC-4821"), trace:

client request
-> protocol method/name
-> argument validation
-> tool function
-> structured result
-> client response

Task E — test failures

Exercise at least:

  • unknown ticket,
  • invalid argument shape,
  • unknown tool,
  • server process exits unexpectedly.

Task F — run the suite

PYTHONPATH=. pytest service-desk-day-27/tests/test_stdio_mcp.py -v

Explain which failures are transport/process failures and which are MCP/tool-contract failures.

Knowledge check

1. Why is STDIO attractive for a local MCP server?

It gives a direct parent/child process transport without opening a network listener.

2. Why must stdout remain clean?

Because it carries protocol messages; arbitrary logs can corrupt the message stream.

3. Does local STDIO make a server trusted?

No. Process permissions, environment, tool scope and application authorization still matter.

4. What is the difference between a process-start failure and a tool-call failure?

A process-start failure happens before the protocol server can operate; a tool-call failure happens after protocol communication reaches a registered capability.

5. Should a read-only knowledge server inherit production write credentials?

No. Grant only the credentials and privileges required by its capability contract.

Scenario

Your STDIO MCP client reports malformed JSON responses. The server's tool logic passes all unit tests. When you run the server manually, you notice startup logs printed with print().

What is the first thing to investigate?

Answer: Whether those prints are being written to protocol stdout and corrupting the STDIO message stream. Move diagnostics to stderr/logging and keep stdout protocol-only.

Key takeaways

  • STDIO is a transport between processes, not a security sandbox.
  • Process launch/configuration is a separate failure layer from MCP method handling.
  • Keep protocol stdout clean; send diagnostics elsewhere.
  • Expose narrow, typed tools instead of arbitrary command execution.
  • Validate arguments and return structured failures.
  • Apply least privilege to the local server process and its environment.
  • Stay consistent with the current MCP protocol generation taught on Day 26.

Official references

03 · EXPLAIN

Interview checkpoint

Explain one design decision from this lesson, the alternative you rejected, and the failure mode or evidence that justified your choice.