MCP 协议深度解析:从原理到 TypeScript 实战
Model Context Protocol(MCP)是 Anthropic 提出的"AI 应用的 USB-C 接口"。本文讲清 MCP 的 host/client/server 三角模型、resources/tools/prompts 三类能力、JSON-RPC 通信机制,并用 TypeScript 从零实现一个可被 Claude Code 调用的 MCP Server。
本文要点
- 主题聚焦:AI Agent、MCP
- 适合人群:独立开发者、全栈工程师、AI 应用开发者
- 核心价值:快速理解背景、实践路径、常见误区和工程建议
如果你做过 AI 应用,多半踩过这个坑:今天给模型接 GitHub,明天接数据库,后天接企业内部 API——每接一个工具,就要为它写一份适配、鉴权、错误处理,还要想办法把 schema 塞进上下文。工具一多,集成代码比业务代码还长。
Model Context Protocol(MCP) 就是来解决这件事的。它把"模型 ↔ 外部能力"的连接方式标准化了:能力提供方实现一个 MCP Server,任何 MCP Client(Claude Code、Cursor、Continue、Cline……)都能即插即用地调用它。一句话概括——MCP 是 AI 应用的 USB-C 接口。
一、MCP 的三角模型
MCP 规范里有三个角色:
- Host:承载模型、面向用户的程序(如 Claude Code、Cursor IDE)。
- Client:Host 内部和 Server 通信的组件,一个 Host 可以连多个 Server。
- Server:暴露具体能力的独立进程,可以是本地的(stdio),也可以是远程的(HTTP/SSE)。
关键点是解耦:能力方只关心"怎么把我的数据/操作按 MCP 规范暴露出去",Host 只关心"怎么发现并调用这些能力",中间用一份统一协议对接。这和 LSP(语言服务器协议)的思路一脉相承——LSP 让编辑器和语言分析解耦,MCP 让模型和外部能力解耦。
二、三类能力:Resources / Tools / Prompts
一个 Server 可以暴露三类东西:
| 类型 | 语义 | 由谁触发 | 典型例子 |
|---|---|---|---|
| Resources | 只读数据源(类似 GET) | Host/用户主动选择 | 日志文件、数据库表 schema、文档 |
| Tools | 可执行操作(类似 POST) | 模型决定调用 | 发邮件、建 issue、跑 SQL 查询 |
| Prompts | 可复用提示模板 | 用户从菜单选 | /summarize-pr、/explain-error |
最常用、最有"Agent 感"的是 Tools。模型在推理时决定要不要调一个 tool、传什么参数,Server 执行后把结果回灌给模型——这就是 function calling 的工程化升级版。
三、通信机制:JSON-RPC 2.0
MCP 用 JSON-RPC 2.0 做消息层,传输方式目前两种:
- stdio:Server 是本地子进程,Client 通过标准输入输出收发。适合本地工具,零网络开销、天然隔离。
- HTTP + SSE(或 Streamable HTTP):Server 是远程服务,适合共享、多客户端。
一次典型交互(初始化 → 列工具 → 调用):
// Client → Server: 初始化握手
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"demo","version":"0.1"}}}
// Server → Client: 声明能力
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","capabilities":{"tools":{}},"serverInfo":{"name":"time-server","version":"0.1"}}}
// Client → Server: 列出工具
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
// Client → Server: 调用工具
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"now","arguments":{"tz":"Asia/Shanghai"}}}
四、TypeScript 实战:一个时间/时区 MCP Server
用官方 SDK(@modelcontextprotocol/sdk)几十行就能跑起来。下面是一个可被 Claude Code 直接调用的 Server:
// server.ts
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: "time-server", version: "0.1.0" });
// 注册一个 tool:按时区返回当前时间
server.tool(
"now",
"Return the current time for an IANA timezone, e.g. Asia/Shanghai",
{ tz: z.string().default("UTC").describe("IANA timezone") },
async ({ tz }) => {
try {
const now = new Date().toLocaleString("sv-SE", { timeZone: tz, hour12: false });
return { content: [{ type: "text", text: `${tz}: ${now}` }] };
} catch {
return { content: [{ type: "text", text: `invalid timezone: ${tz}` }], isError: true };
}
},
);
await server.connect(new StdioServerTransport());
跑起来:
npx tsx server.ts
然后把它注册到客户端。Claude Code 的 ~/.claude.json / 项目 .mcp.json:
{
"mcpServers": {
"time": { "command": "npx", "args": ["tsx", "/abs/path/server.ts"] }
}
}
重启客户端后,问"现在东京几点",模型会自动调用 now(tz:"Asia/Tokyo") 并用结果回答。
五、工程实践要点
- Tool 描述就是 prompt 的一部分。 模型完全靠
name和description判断该不该调用,描述要精确、给例子、写清边界。模糊的描述是 Agent 跑偏的头号原因。 - 参数用 schema 约束。 Zod schema 既校验又自动生成模型可见的 JSON Schema,省心且安全。
- 错误要可恢复。 返回
isError: true和可读原因,让模型有机会换参数重试或改方案,别直接抛异常中断。 - 远程 Server 务必鉴权。 Streamable HTTP 模式下,用 token / OAuth 给
tools/call加权限,并按客户端限流——Server 执行的是真实操作。 - 进度与日志用通知。 长任务通过
notifications/progress推进度,logging通道写调试日志,别污染结果通道。
六、MCP 改变了什么
没有 MCP 之前,"给 Agent 加一个能力"是 N × M 的集成地狱(N 个模型 × M 个工具)。MCP 把它变成 N + M——工具方写一次 Server,所有兼容的 Client 都能用。这正是它半年内就被 Cursor、Cline、Continue、Zed、Replit 等广泛采用的原因。
对开发者而言,MCP 还意味着一种新机会:把你的产品/数据封装成 MCP Server,就能直接进入所有主流 AI 编程工具的能力池,而不必等它们逐个集成你。
MCP 的本质不是某个新算法,而是一份让 AI 应用生态得以规模化的协议契约。理解了三角模型、三类能力、JSON-RPC 这三件事,你就能写出生产可用的 Server。下一篇我们会用 MCP 搭一个更完整的知识库检索 Server,把 RAG 装进 Claude Code。
Related