cd /news/agent-protocols/mcp-transports-that-still-matter-std… · home › topics › agent-protocols › article
[ARTICLE · art-141079] src=dev.to ↗ pub= topic=agent-protocols verified=true sentiment=· neutral

MCP Transports That Still Matter: stdio vs Streamable HTTP (and Why SSE Is a Trap)

A developer writing as 晚安code on Juejin published a version-aware walkthrough of building a minimal MCP server with Python's FastMCP, showing how type annotations and docstrings generate the JSON Schema and tool descriptions models rely on. The post flags HTTP+SSE as deprecated in favor of stdio and Streamable HTTP, and notes the one-line transport switch in the FastMCP run path. It recommends pinning the SDK (around 1.27.x at time of writing) because tutorial snippets in the MCP ecosystem rot quickly.

by read5 min views2 publishedSep 28, 2026

Attributed Chinese → English compile (not original authorship)

Original title: MCP Server 开发入门:手把手写一个能跑的 Server,三种协议怎么选

Author: 晚安code

URL: https://juejin.cn/post/7673880140422955046

Date: 2026-08-15

Why this matters for English readers: Most English MCP intros still paste SSE configs from early 2025. This post is short, version-aware, and mechanism-first: it separates what an MCP Server is from how the bytes move, flags HTTP+SSE as deprecated, and shows the one-line transport switch in the Python FastMCP path. Useful if you are wiring Cursor / Claude Desktop locally and deciding whether a remote Streamable HTTP deploy is worth the auth/ops cost.

If you have ever watched an LLM “want” to check a package status and then fail because it has no eyes and no hands, you already understand why MCP exists. The interesting part is not the metaphor (“USB-C for AI”). The interesting part is the boundary: a small process you control that exposes typed tools and read-only resources, so the model can call out without owning your database credentials or your shell.

This compile re-explains 晚安code’s Juejin walkthrough in English: a minimal FastMCP server, how to debug it, and how to pick a transport without learning an obsolete one first.

MCP (Model Context Protocol) is an open protocol for connecting model hosts to external tools and data. A host (Claude Desktop, Cursor, your own agent runtime) talks to one or more MCP Servers. Each server is ordinary application code—Python or Node—that wraps APIs, files, or internal services behind a stable interface.

Why not let the model call HTTP and SQL directly? Because unconstrained tools are how you get “buy ten refrigerators” demos. The server is the policy layer: allowlists, auth, rate limits, and “this tool may read but not write.” That is the product value, not the JSON-RPC ceremony.

Minimal mental model:

uv (skip the fragile pip folklore) The source uses uv (Astral) as a fast env + dependency manager. On Linux/macOS the install path differs from the Windows PowerShell one-liner in the article; the important sequence is:

uv python install 3.11
uv init . -p 3.11
uv add "mcp[cli]"

mcp[cli] pulls the official Python SDK and the Inspector CLI helpers. Activate the .venv uv creates before you debug in VS Code / Cursor. The article notes SDK ~1.27.x at time of writing—pin what you ship; tutorial snippets rot fast in this ecosystem.

FastMCP (in mcp.server.fastmcp) is the high-level API: decorators instead of hand-rolled JSON-RPC handlers.

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Demo")

@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b

@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
    """Greet someone by name."""
    return f"Hello, {name}!"

if __name__ == "__main__":
    mcp.run()  # default transport: stdio

Three details that burn hours when omitted:

Requirement Why it matters
Type annotations on tool args FastMCP builds JSON Schema ( inputSchema ) from them. No types → weak or empty schemas → worse model calls.
Docstrings Become the tool/resource description the model reads when deciding whether to call. Empty docstring ≈ invisible tool.
if __name__ == "__main__": Import-only modules never start the stdio loop; “nothing happens” is usually this.

Tool vs resource is a semantic contract, not a style preference:

@mcp.tool() @mcp.resource()
Intent Perform an action Expose read-only data
Side effects Expected (write, call API, mutate) Should be none
Discovery tools/list + model-driven call URI / URI template
Examples add , send email, query order greeting://{name} , config blob

Treat tools as verbs and resources as nouns. Mixing “read file” into a write-capable god-tool is how local servers become shell-shaped.

python server.py          # smoke: process starts, no immediate crash
mcp dev server.py         # Inspector UI: list tools, invoke, inspect errors

Version pitfall called out in the source: older blog posts import MCPServer from paths that only exist on pre-alpha SDK v2 sketches. On the stable 1.x line you want from mcp.server.fastmcp import FastMCP. If an import fails on day one, assume tutorial/SDK skew, not “MCP is broken.”

Transport is not a fashion label. It decides whether the server is a local child process or a network service.

command + args from config. This is the right default while you are learning and while tools only need the developer’s laptop.

Switching in FastMCP is intentionally boring:

mcp.run()                                 # local stdio
mcp.run(transport="streamable-http")      # remote Streamable HTTP

Once you leave stdio, you own authentication, rate limits, observability, and deployment. The protocol does not magically make a public URL safe.

SSE-over-HTTP was an early remote option. Per the article’s reading of the ecosystem (including SEP-2596 era deprecation notes around March 2025), new work should not start on SSE. Some TypeScript SDK paths have already removed SSE server support. Old Chinese and English tutorials still teach it; treat those snippets as historical.

Cheat sheet:

Transport Where it runs How it talks Use when Status
stdio Same machine as host stdin/stdout Cursor, Claude Desktop, local CLI Prefer for local
Streamable HTTP Your server / cluster HTTP bidirectional streaming Shared/prod remote tools Prefer for remote
SSE Remote HTTP unidirectional push Legacy clients only Deprecated

Hosts differ in file names, but the stdio shape is the same idea: name → command → args → optional env. Conceptually:

{
  "mcpServers": {
    "demo": {
      "command": "uv",
      "args": ["run", "python", "/absolute/path/to/server.py"]
    }
  }
}

Prefer absolute paths and the same interpreter that has mcp installed. After restart, confirm the host lists add and can call it with integers—not with natural language stuffed into a single string field.

The source’s closing advice is the right engineering posture: the barrier to a first MCP Server is low; the barrier to a correct remote deployment is ops, not decorators. Read the official specification when you outgrow demos—secondary tutorials lag the SDK by months.

Compiler byline: YongBo Yu, Toronto · https://yongbo-yu.vercel.app · https://github.com/YongBoYu1

Attribution: Mechanisms and examples adapted from 晚安code, MCP Server 开发入门:手把手写一个能跑的 Server,三种协议怎么选 (2026-08-15). Re-explained in original English wording; not a verbatim translation.

── more in #agent-protocols 4 stories · sorted by recency
── more on @model context protocol 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/mcp-transports-that-…] indexed:0 read:5min 2026-09-28 · —