Chapter 3: Minimal MCP (Stdio)
The simplest way to run an MCP server is over Stdio (Standard Input/Output). In this chapter we learn how this communication works and how to start a minimal server.
Why Stdio?
One might think that MCP always has to run over the internet (HTTP/Websockets). But for local applications (e.g., an IDE that accesses files), Stdio is far more efficient:
- No Ports: You don't have to manage TCP ports or configure firewalls.
- Security: Only the process that started the server can talk to it.
- Lifecycle: When the client app (e.g., the
mcp-tester) terminates, the MCP server process dies automatically too.
Streamable HTTP: The Web Standard
For applications that communicate over a network (e.g., a server in the cloud), MCP has used the Streamable HTTP transport since the 2025-03-26 specification (details in Chapter 16). A single HTTP endpoint is started here:
- One Endpoint: All requests run as POST to the same URL; responses are plain JSON answers or - if asynchronous - SSE streams.
- Remote Access: The server can be reachable from anywhere.
- Persistence: It runs independently of the client lifecycle.
- Complexity: Requires port management and authentication (OAuth 2.1).
The Principle: JSON-RPC over Pipes or HTTP
Communication happens over a data stream. The client sends a JSON object to the server's stdin, and the server responds via stdout.
A typical handshake looks like this:
- Client -> Server (
initialize): "Hello, I am client X and support version Y." - Server -> Client: "Hi! I am server Z and have the following capabilities (Tools, Resources)."
- Client -> Server (
initialized): "Okay, let's get started!"
A Minimal Server in Go
Here is the absolute minimum for starting an MCP server:
package main
import (
"github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/modelcontextprotocol/go-sdk/server"
)
func main() {
// 1. Create the server instance
s := server.NewServer("mini-server", "1.0.0")
// 2. Expose it over Stdio
if err := server.ServeStdio(s); err != nil {
panic(err)
}
}Checking the Connection: The Ping Mechanism (New in 2025-11)
In production environments, especially with remote servers (Streamable HTTP), it is important to know whether the counterpart (server or client) is still reachable. For this, MCP offers a Ping mechanism.
The Principle
- Request: The client (or server) sends a
pingrequest. - Response: The recipient immediately answers with an empty result
{}. - Timeout: If no answer arrives within a defined time, the connection is considered "dead" (stale) and should be re-established.
This is especially important for remote connections to avoid "ghost connections", where the client still believes it is connected while the server has already closed the socket.
Testing the Server
With our mcp-tester we can validate this server right away:
# If the binary is called 'mini-server':
./mcp-tester list --command "./mini-server"Even if the server doesn't have any tools yet, the mcp-tester will complete the handshake successfully and return an (empty) list. That way you have built your first working MCP channel!
Beyond Stdio: Remote Servers (Streamable HTTP)
Stdio is perfect for local workflows but has its limits. If your MCP server is supposed to run in a data center or be used by many different clients simultaneously, the Streamable HTTP transport comes into play.
Why an External Server?
- Centralization: A single MCP server can provide tools to the entire staff (e.g., access to the internal wiki).
- Resources: Compute-intensive tools (e.g., video rendering or large database queries) can run on powerful hardware while the client (e.g., a laptop) stays lean.
- Cloud-Native: MCP servers can be operated as containers (Docker) in Kubernetes or as serverless functions.
The mcp-tester is already prepared for this world. Instead of a command, you simply pass it a URL:
./mcp-tester list --url "https://mcp.example.com/mcp"← Chapter 2: How LLMs Communicate | Table of Contents | Next Chapter: Tools →
Copyright Michael Lechner - 2026-02-28