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

TransportWhenNotes
stdioLocal (Claude Desktop, Cursor)subprocess; stdin/stdout
SSERemote, multi-clientHTTP 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

ToolsResources
Semantics”Do something""Give me data”
Side effectsallowedshould be read-only
LLM usagefunction callingon-demand read (RAG-like)
Examplescreate PR, run SQLread file, fetch schema

Rule of thumb: reads → Resources; writes → Tools.

Production Notes

  1. Least privilege—expose only needed tools; read-only DB connections
  2. Validate inputsinputSchema guides the LLM; server must enforce
  3. Timeouts/retries on slow tools
  4. Audit logs—who did what via MCP
  5. 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.