The intro explained what MCP is. This article opens the protocol—for engineers building servers or evaluating MCP in architecture.
Protocol Stack
Application Tools / Resources / Prompts (MCP semantics)
─────────────────────────────
Transport stdio (local) | SSE (remote HTTP)
─────────────────────────────
Messages JSON-RPC 2.0
No REST URLs—Host and Server exchange JSON-RPC requests and responses.
Lifecycle
Host Server
│── initialize ──────────────→│
│←─ capabilities ────────────│ (Tools/Resources/Prompts)
│── initialized ─────────────→│
│ │
│── tools/list ───────────────→│
│←─ [tool definitions] ──────│
│ │
│── tools/call ──────────────→│
│←─ result / error ──────────│
Servers are passive—they respond to Host requests. Cursor and Claude Desktop drive the rhythm.
Tool Definitions
tools/list returns schemas like:
{
"name": "query_database",
"description": "Execute a read-only SQL query against the production database",
"inputSchema": {
"type": "object",
"properties": {
"sql": { "type": "string", "description": "SQL query to execute" }
},
"required": ["sql"]
}
}
Descriptions matter—the LLM chooses tools and fills args from them. Good descriptions → accurate agent calls.
Transport Choice
| Transport | When | Notes |
|---|---|---|
| stdio | Local (Claude Desktop, Cursor) | subprocess; stdin/stdout |
| SSE | Remote, multi-client | HTTP Server-Sent Events + auth |
Local dev is almost always stdio. Production shared servers: SSE.
Minimal MCP Server (TypeScript)
Official SDK @modelcontextprotocol/sdk:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "my-server", version: "1.0.0" });
server.tool(
"get_weather",
"Get current weather for a city",
{ city: z.string().describe("City name, e.g. Beijing") },
async ({ city }) => {
const data = { city, temp: "25°C", condition: "Clear" };
return { content: [{ type: "text", text: JSON.stringify(data) }] };
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
Host config:
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["./dist/server.js"]
}
}
}
Resources vs Tools
| Tools | Resources | |
|---|---|---|
| Semantics | ”Do something" | "Give me data” |
| Side effects | allowed | should be read-only |
| LLM usage | function calling | on-demand read (RAG-like) |
| Examples | create PR, run SQL | read file, fetch schema |
Rule of thumb: reads → Resources; writes → Tools.
Production Notes
- Least privilege—expose only needed tools; read-only DB connections
- Validate inputs—
inputSchemaguides the LLM; server must enforce - Timeouts/retries on slow tools
- Audit logs—who did what via MCP
- Version tools—schema changes need compatibility thought
Ecosystem (2026)
- Official servers:
modelcontextprotocol/servers - Community servers growing; quality varies
- Cursor, Claude Desktop, Windsurf, Zed supported
- OpenAI not native MCP—bridges exist
Summary
MCP engineering = JSON-RPC + clear Tool/Resource definitions + right transport. Better descriptions and tight permissions beat feature sprawl.