Hands-On MCP: Decoding the Model Context Protocol with Live JSON-RPC Examples
This article demystifies the Model Context Protocol (MCP) by walking through a practical fetch-mcp example, then detailing the JSON-RPC 2.0 application layer interactions — initialization, tool listing, and tool calling — and comparing STDIO versus Streamable HTTP transport layers for remote MCP server communication.
1. Model and Tools
The most basic LLM product is a Q&A bot that answers from its training data. When extra information is needed, frameworks like LangChain call tools to supplement context. Anthropic's MCP standardizes this context-supplementation interaction, keeping the LLM's "brute-force" approach.
The author first encountered MCP via a Bilibili video showing how to fetch English articles using a fetch-mcp server. The typical flow with Cline (an AI coding assistant) is:
User submits task: "Fetch https://xxoo content, convert to markdown, write to @xxoo.md"
Cline queries fetch-mcp for available tools
Cline sends user task + tool list to LLM
LLM selects needed tool and parameters (e.g., fetch_markdown) and returns to Cline
After user authorization, Cline calls fetch-mcp to execute
Output is fed back to LLM for continuation
Generalized architecture: User → MCP Host (AI App) → MCP Client ↔ MCP Server ↔ LLM.
2. Application Layer Protocol
MCP follows JSON-RPC 2.0 at the application layer. The author uses the official fetch example from the MCP repository to demonstrate live interaction.
2.1 Initialization
Start the fetch MCP server via STDIO transport: uvx mcp-server-fetch Send initialize request:
{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18", "capabilities": { "sampling": {}, "roots": { "listChanged": true } }, "clientInfo": { "name": "haha", "version": "1.0.0" } } }Receive response with server capabilities and version (mcp-fetch 1.12.0). Then send initialized notification (one-way, no reply):
{ "jsonrpc": "2.0", "method": "notifications/initialized" }Initialization lets client and server exchange capabilities.
2.2 Query Tool List
Request:
{ "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": { "_meta": { "progressToken": 1 } } }Response includes tool metadata: name ("fetch"), description (LLM uses this to decide usability), and inputSchema (parameters: url, max_length, start_index, raw).
2.3 Invoke Tool
Request:
{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "fetch", "arguments": { "url": "https://cn.bing.com/robots.txt", "raw": true }, "_meta": { "progressToken": 1 } } }Response returns structured content (type: text, the raw robots.txt) and isError: false. This completes the three-round core protocol interaction.
3. Transport Layer Protocol
The example uses STDIO, which only works locally. For remote execution, HTTP transport is needed. A recent change replaced HTTP+SSE with "Streamable HTTP". The author argues for understanding from first principles: remote services need session management for concurrent sessions and state recovery after interruptions.
Simple connection-based management fails when connections drop (HTTP+SSE's main drawback). Streamable HTTP introduces two key mechanisms:
Initialization phase: "Mcp-Session-Id" header identifies a session
Interruption recovery: "Last-Event-ID" header indicates from which SSE event ID to resume
The interaction flow shows POST for initialization and requests, GET for SSE stream, with session ID propagated across requests.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
