MCP 客户端 SDK 深入:TypeScript 客户端 API、传输状态机与错误处理

系统讲解 MCP 客户端 SDK 的内部机制:TypeScript SDK 的 Client 类与协议状态机、connect/callTool/readResource 全生命周期、streamable 传输的连接管理、请求-响应与通知处理、并发调用与超时控制、错误分类与重试策略,以及 Python SDK 的对应实现。

1. 客户端 SDK 的职责

MCP 客户端 SDK 封装了协议的「对话细节」,让应用只关心「连接 + 调用工具」。它要处理:传输建立、协议握手、请求-响应、通知、错误与重试。

一句话:客户端 SDK 是「协议的死记硬背层」——应用不用懂 JSON-RPC 细节,SDK 负责把它翻译成可靠的调用。

1.1 SDK 的抽象边界

应用层(业务):
  const result = await client.callTool({ name, args })

SDK 层(协议):
  - 建立传输(stdio / HTTP)
  - 初始化握手(initialize / initialized)
  - 发送 JSON-RPC 请求 / 处理响应
  - 处理通知与错误

传输层:
  - stdio 双向管道
  - Streamable HTTP 会话

2. Client 类与连接生命周期

2.1 创建与连接

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

// 1. 创建客户端(声明能力)
const client = new Client({
  name: "my-agent",
  version: "1.0.0",
  capabilities: {
    sampling: {},        // 客户端支持 sampling
    roots: {},           // 客户端支持 roots
  },
});

// 2. 建立传输
const transport = new StdioClientTransport({
  command: "node",
  args: ["server.js"],
});

// 3. 连接(内部完成 initialize 握手)
await client.connect(transport);

2.2 握手状态机

connect()
  → transport.start()       // 建立通道
  → initialize              // 客户端→服务器:能力声明
  → initialized             // 服务器→客户端:确认
  → connected               // 可调用工具/读资源
  → close()                 // 关闭,进入 terminated
状态:disconnected → connecting → connected → terminating → terminated

2.3 能力协商

initialize 时双方交换 capabilities:
  服务器:tools、resources、prompts、logging
  客户端:sampling、roots
  协商结果决定「哪些请求可用」

一句话:connect 不是「连上管道」就完事,而是走完 initialize 握手、完成能力协商后才真正可用——状态机是客户端 SDK 的骨架。

3. 核心 API 调用

3.1 调用工具

const result = await client.callTool({
  name: "search",
  arguments: { query: "MCP 鉴权", limit: 10 },
});

// result 是 JSON-RPC 结果
//   result.isError = false
//   result.content = [ { type: "text", text: "..." } ]

3.2 读取资源

// 读取单个资源
const res = await client.readResource({ uri: "config://app/settings" });

// 列出资源
const list = await client.listResources({ cursor });

// 列出提示词
const prompts = await client.listPrompts();

3.3 获取工具列表

// 拉取服务器全部工具定义(JSON Schema)
const tools = await client.listTools();
// tools.tools: [{ name, description, inputSchema }]

3.4 并行与顺序

// 并行调用(互不依赖)
const [a, b] = await Promise.all([
  client.callTool({ name: "ping", arguments: {} }),
  client.callTool({ name: "status", arguments: {} }),
]);

// 顺序调用(依赖前一步结果)
const r1 = await client.callTool({ name: "query", arguments: { id: 1 } });
const r2 = await client.callTool({ name: "detail", arguments: { id: r1.id } });

一句话:核心 API 就三类——callTool 做动作、readResource 取数据、listTools/listPrompts 摸能力;并行与顺序由 Agent 的决策编排决定。

4. 通知与请求-响应

4.1 三种消息类型

Request     :请求-响应(callTool 等)——需要应答
Notification:单向通知(如 resources/listChanged)——无需应答
Response    :对请求的应答

协议保证:请求按 id 关联,响应带对应 id

4.2 客户端处理通知

// 订阅资源变化 → 服务器发 listChanged 通知 → 重新拉取
client.setRequestHandler(ListChangedNotificationSchema, async () => {
  const res = await client.listResources();
  updateUi(res.resources);
});

4.3 请求 id 与并发

- 每个请求带唯一 id(递增)
- 服务器可乱序返回,客户端按 id 匹配
- 并发请求上限由客户端控制
- 通知不带 id(与请求区分)

一句话:请求/响应/通知三类消息 + id 关联,构成了「并发可乱序、通知独立推」的协议语义——客户端 SDK 按 id 分发,Agent 无需串行化所有调用。

5. 传输层管理

5.1 StdioClientTransport

- 子进程管理(spawn、信号、退出码)
- 双向管道(stdin 写、stdout 读)
- stderr 透传日志
- close → kill 进程

5.2 StreamableClientTransport

- HTTP POST /message(JSON-RPC 体)
- SSE 流式接收
- 会话管理(session id / 无状态模式)
- 心跳 / 超时
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const transport = new StreamableHTTPClientTransport(
  new URL("https://mcp.example.com/mcp"),
  { authProvider }   // 可选:OAuth 令牌提供者
);
await client.connect(transport);

5.3 连接可靠性

- 传输断开 → 监听 onclose → 重连策略
- 指数退避重试(避免打爆服务器)
- 请求超时 → 可重试(幂等操作)
- 会话丢失 → 重新 initialize

一句话:传输层决定了「怎么把 JSON-RPC 送出去」——stdio 管进程、HTTP 管网络;可靠连接靠重连、退避、超时三板斧。

6. 错误分类与重试

6.1 JSON-RPC 错误码

错误码含义处理
-32700解析错误客户端 bug,不重试
-32600无效请求请求格式错误,修请求
-32601方法不存在服务器不支持,报错
-32602无效参数校验参数后重试
-32603内部错误可重试(退避)
-32000+服务器自定义按具体错误处理

6.2 工具执行错误

result.isError = true 是「业务失败」,不是协议错误
  → 向 LLM 返回错误文本,让模型调整后重试
协议错误(抛异常)→ 区分可重试与不可重试

6.3 重试策略

// 幂等操作可安全重试
async function callWithRetry(fn, { retries = 3, base = 200 }) {
  for (let i = 0; i < retries; i++) {
    try {
      return await fn();
    } catch (e) {
      if (isNonRetriable(e)) throw e;
      await sleep(base * 2 ** i);   // 指数退避
    }
  }
  throw new Error("retry exhausted");
}

6.4 超时与取消

// 带超时的调用
const result = await Promise.race([
  client.callTool({ name: "search", arguments: args }),
  timeout(10_000),   // 10s 超时
]);

一句话:错误处理的核心是「区分业务失败与协议失败、可重试与不可重试」——isError 交给 LLM 决策,协议异常走退避重试,超时兜底防挂死。

7. Python SDK 对应实现

from mcp.client.stdio import stdio_client
from mcp.client.session import ClientSession

async def main():
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # 握手
            await session.initialize()
            # 调用工具
            result = await session.call_tool(
                "search", {"query": "MCP", "limit": 5}
            )
            # 读取资源
            content = await session.read_resource("config://app/settings")
Python SDK 对称 API:
  call_tool / read_resource / list_tools / list_resources
  通知处理:session.set_*_notification_handler
  传输:stdio_client / streamable_http_client

一句话:TS 与 Python SDK 是同一协议的两套实现——API 对称、语义一致,选型看应用栈,不必重学协议。

8. 常见陷阱与排障

陷阱症状解决
未初始化就 callTool协议错误connect 后再调用
并发无上限服务器过载客户端并发池
无超时调用挂死统一 timeout
重试非幂等操作副作用重复仅幂等可重试
忽略 onclose断连无感监听重连
未处理通知资源更新丢失注册 notification handler
调试技巧:
  - 开启 SDK 的调试日志(transport 层打印 JSON-RPC)
  - 用 mcp-inspector 单步观察请求/响应
  - 抓异常堆栈区分「协议错误 vs 业务错误」

9. 总结

MCP 客户端 SDK 的要点可以概括为「连得上、调得稳、错得清」:

层面要点
连接initialize 握手 + 能力协商,状态机驱动
调用callTool / readResource / listTools 三类核心
消息请求带 id、响应按 id 匹配、通知独立推
传输stdio 管进程、HTTP 管网络,重连 + 退避
错误isError 交 LLM、协议错分可重试、超时兜底
双语言TS / Python SDK 对称实现

客户端 SDK 是 Agent 与 MCP 服务器之间的「协议翻译官」:把状态机、消息关联、传输、错误处理这些细节全部藏起来,让上层只关心「调哪个工具」。掌握 SDK 内部,才能在遇到诡异的连接问题时,快速定位是协议、传输还是业务问题。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「AI工程」更多文章

  1. MCP 资源模板与订阅:URI 模板、ListChanged 通知与上下文注入
  2. MCP 的 OAuth 鉴权与会话:动态注册、PKCE 与令牌轮换
  3. MCP 服务器测试框架:in-memory 传输、协议断言与端到端测试