{"slug": "mcp-transports-that-still-matter-stdio-vs-streamable-http-and-why-sse-is-a-trap", "title": "MCP Transports That Still Matter: stdio vs Streamable HTTP (and Why SSE Is a Trap)", "summary": "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.", "body_md": "**Attributed Chinese → English compile (not original authorship)**\n\n**Original title:** MCP Server 开发入门：手把手写一个能跑的 Server，三种协议怎么选\n\n**Author:** 晚安code\n\n**URL:** [https://juejin.cn/post/7673880140422955046](https://juejin.cn/post/7673880140422955046)\n\n**Date:** 2026-08-15\n\n**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.\n\nIf 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.\n\nThis 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.\n\n**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.\n\nWhy 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.\n\nMinimal mental model:\n\n`uv` (skip the fragile pip folklore)\nThe 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:\n\n```\n# install uv from https://docs.astral.sh/uv/ if needed\nuv python install 3.11\nuv init . -p 3.11\nuv add \"mcp[cli]\"\n```\n\n`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.\n\nFastMCP (in `mcp.server.fastmcp`) is the high-level API: decorators instead of hand-rolled JSON-RPC handlers.\n\n``` python\nfrom mcp.server.fastmcp import FastMCP\n\nmcp = FastMCP(\"Demo\")\n\n@mcp.tool()\ndef add(a: int, b: int) -> int:\n    \"\"\"Add two numbers.\"\"\"\n    return a + b\n\n@mcp.resource(\"greeting://{name}\")\ndef greeting(name: str) -> str:\n    \"\"\"Greet someone by name.\"\"\"\n    return f\"Hello, {name}!\"\n\nif __name__ == \"__main__\":\n    mcp.run()  # default transport: stdio\n```\n\nThree details that burn hours when omitted:\n\n| Requirement | Why it matters | \n|---|---|\n| Type annotations on tool args | FastMCP builds JSON Schema ( `inputSchema` ) from them. No types → weak or empty schemas → worse model calls. | \n| Docstrings | Become the tool/resource description the model reads when deciding whether to call. Empty docstring ≈ invisible tool. | \n| `if __name__ == \"__main__\":` | Import-only modules never start the stdio loop; “nothing happens” is usually this. | \n\n**Tool vs resource** is a semantic contract, not a style preference:\n\n|  | `@mcp.tool()` | `@mcp.resource()` | \n|---|---|---|\n| Intent | Perform an action | Expose read-only data | \n| Side effects | Expected (write, call API, mutate) | Should be none | \n| Discovery | `tools/list` + model-driven call | URI / URI template | \n| Examples | `add` , send email, query order | `greeting://{name}` , config blob | \n\nTreat tools as verbs and resources as nouns. Mixing “read file” into a write-capable god-tool is how local servers become shell-shaped.\n\n```\npython server.py          # smoke: process starts, no immediate crash\nmcp dev server.py         # Inspector UI: list tools, invoke, inspect errors\n```\n\nVersion 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.”\n\nTransport is not a fashion label. It decides whether the server is a **local child process** or a **network service**.\n\n`command` + `args` from config.\nThis is the right default while you are learning and while tools only need the developer’s laptop.\n\nSwitching in FastMCP is intentionally boring:\n\n```\nmcp.run()                                 # local stdio\nmcp.run(transport=\"streamable-http\")      # remote Streamable HTTP\n```\n\nOnce you leave stdio, **you** own authentication, rate limits, observability, and deployment. The protocol does not magically make a public URL safe.\n\nSSE-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.\n\nCheat sheet:\n\n| Transport | Where it runs | How it talks | Use when | Status | \n|---|---|---|---|---|\n| stdio | Same machine as host | stdin/stdout | Cursor, Claude Desktop, local CLI | Prefer for local | \n| Streamable HTTP | Your server / cluster | HTTP bidirectional streaming | Shared/prod remote tools | Prefer for remote | \n| SSE | Remote | HTTP unidirectional push | Legacy clients only | Deprecated | \n\nHosts differ in file names, but the stdio shape is the same idea: name → command → args → optional env. Conceptually:\n\n```\n{\n  \"mcpServers\": {\n    \"demo\": {\n      \"command\": \"uv\",\n      \"args\": [\"run\", \"python\", \"/absolute/path/to/server.py\"]\n    }\n  }\n}\n```\n\nPrefer 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.\n\nThe 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.\n\n**Compiler byline:** YongBo Yu, Toronto · [https://yongbo-yu.vercel.app](https://yongbo-yu.vercel.app) · [https://github.com/YongBoYu1](https://github.com/YongBoYu1)\n\n**Attribution:** Mechanisms and examples adapted from 晚安code, [MCP Server 开发入门：手把手写一个能跑的 Server，三种协议怎么选](https://juejin.cn/post/7673880140422955046) (2026-08-15). Re-explained in original English wording; not a verbatim translation.", "url": "https://wpnews.pro/news/mcp-transports-that-still-matter-stdio-vs-streamable-http-and-why-sse-is-a-trap", "canonical_source": "https://dev.to/yong_yu_f98e15562e9b120a0/mcp-transports-that-still-matter-stdio-vs-streamable-http-and-why-sse-is-a-trap-1ji", "published_at": "2026-09-28 15:05:56+00:00", "updated_at": "2026-09-28 15:21:07.210347+00:00", "lang": "en", "topics": ["agent-protocols", "ai-agents", "developer-tools", "ai-tools"], "entities": ["Model Context Protocol", "FastMCP", "Juejin", "晚安code", "uv", "Astral", "Claude Desktop", "Cursor"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/mcp-transports-that-still-matter-stdio-vs-streamable-http-and-why-sse-is-a-trap", "markdown": "https://wpnews.pro/news/mcp-transports-that-still-matter-stdio-vs-streamable-http-and-why-sse-is-a-trap.md", "text": "https://wpnews.pro/news/mcp-transports-that-still-matter-stdio-vs-streamable-http-and-why-sse-is-a-trap.txt", "jsonld": "https://wpnews.pro/news/mcp-transports-that-still-matter-stdio-vs-streamable-http-and-why-sse-is-a-trap.jsonld"}}