MCP 客户端最佳实践
原文:Client 最佳实践
一句话
当 MCP host 应用连接到大量服务器和工具时,使用渐进式工具发现和编程式工具调用两种模式来优化性能和资源使用。
什么时候翻这页
当你的 MCP host 应用需要连接到数十个服务器和数百个工具,导致工具定义占用大量上下文窗口时;当你需要优化工具调用链中的中间结果传递时;当你需要实现更高效、更安全的工具调用机制时。
核心概念
渐进式工具发现 (Progressive Tool Discovery)
一种控制工具定义何时进入模型上下文的模式,避免在对话开始时加载所有工具定义,从而节省 token、降低延迟并提高模型性能。
编程式工具调用 (Programmatic Tool Calling)
也称为"代码模式",允许模型编写代码来调用工具,而不是直接调用工具。代码在沙箱环境中执行,只有最终结果返回给模型,减少中间结果在模型上下文中的传递。
搜索策略 (Search Strategies)
用于工具发现的不同方法:
- 基于关键词:使用关键词匹配(BM25、正则表达式)
- 基于嵌入:在工具描述上进行向量相似性检索
- 基于子代理:使用小型快速模型选择工具
- 混合方法:结合多种策略
沙箱环境 (Sandbox Environment)
隔离的执行环境,用于运行模型生成的代码,通常具有受限的网络访问和资源限制。
怎么做
实现渐进式工具发现
- 延迟工具定义加载:通过
tools/list获取工具定义,但延迟将其注入模型上下文 - 提供搜索工具:向模型提供一个轻量级的
search_tools元工具 - 按需加载完整定义:仅在需要时将完整定义加载到上下文中
三层实现方法
Layer 1: 目录 (Catalog)
- 暴露一组小的元工具用于搜索可用能力
search_tools工具接受自然语言查询并返回匹配的工具名称和简短描述
// 模型调用轻量级搜索工具
search_tools({ query: "update salesforce record" })
// 返回简洁匹配:仅名称和单行描述
→ [
{ name: "salesforce_updateRecord", description: "Update fields on a Salesforce object" },
{ name: "salesforce_upsertRecord", description: "Insert or update based on external ID" }
]
Layer 2: 检查 (Inspect)
- 模型识别候选工具后,仅获取该工具的完整定义
// 模型仅检查其需要的工具
get_tool_details({ name: "salesforce_updateRecord" });
Layer 3: 执行 (Execute)
- 模型仅加载其需要的定义,调用工具
实现动态服务器管理
- 维护可用服务器及其高级描述的注册表
- 仅在模型确定需要服务器功能时连接到服务器
- 断开与当前任务不再相关的服务器连接,释放上下文
实现编程式工具调用
- 从 MCP 模式生成程序化 API
- 读取每个服务器的工具定义
- 基于每个工具的参数和
outputSchema生成类型化函数
// 从日志 MCP 服务器的工具模式自动生成
interface LogEntry {
timestamp: string;
message: string;
level: string;
}
function logging_getLogs(input: {
level: "error" | "warn" | "info";
since: number;
}): Promise<{ entries: LogEntry[] }> {
return mcp.callTool<{ entries: LogEntry[] }>("logging_getLogs", input);
}
-
模型编写代码调用这些 API
- 模型编写单个脚本,而不是在工具调用之间传递完整结果
- 在沙箱中过滤和处理数据,而不是在模型上下文中
-
沙箱执行代码
- 沙箱中的函数调用被拦截并路由回适当的 MCP 服务器
- 仅将
console.log输出和最终结果返回给模型
选择沙箱环境
| 沙箱语言 | 运行时/库 | 主机语言 | 方法 |
|---|---|---|---|
| JavaScript | Deno, isolated-vm | Rust / Node / CLI | 基于 V8 的运行时,具有细粒度权限 |
| Python | Monty (实验性) | Rust | 为 AI 用例构建的最小 Python 解释器 |
| TypeScript | pctx (早期阶段) | Python / Rust | 将代码模式概念作为库集成 |
| 任何语言(通过 Wasm) | Wasmtime | Rust / C / Go | 将任何语言编译为 Wasm 并基于能力安全运行 |
命令 / 配置速查
渐进式工具发现实现指南
| 指导原则 | 理由 |
|---|---|
| 提供多个详细级别 | 让模型选择仅名称、名称和描述或完整模式的响应 |
| 缓存工具定义 | 一旦从服务器获取,就在主机端记忆定义,避免重新注入时需要另一次 tools/list 往返 |
在 list_changed 时刷新 | 当服务器发送 notifications/tools/list_changed 时重新索引搜索目录 |
| 按服务器分组工具 | 按其源服务器组织工具,以便模型能够推理相关能力 |
与提示缓存的交互
- 将新发现的定义附加到缓存断点之后,而不是重新排序
tools数组 - 将服务器断开视为对话边界操作,而不是每轮操作
- 参考提供者的缓存文档
与 Claude Code / Hello-Agents 的联系
在 Claude Code 中,MCP 集成通常直接加载所有可用工具。而 Hello-Agents 第 10 章介绍了基本的 MCP 工具使用。本章节扩展了这些概念,介绍如何处理大量工具和复杂工具调用链,提供了更高级的优化策略,特别是渐进式工具发现和编程式工具调用,这些在处理大量工具时尤为重要。
初学者易错点
- 过早加载所有工具:在连接到大量服务器时,一开始就将所有工具定义加载到模型上下文,导致 token 浪费和性能下降
- 忽视缓存影响:在对话中途添加或删除工具定义会破坏提示缓存,可能导致比删除的定义更多的 token 损失
- 不实现适当的沙箱安全:编程式工具调用需要仔细的沙箱实现,包括网络隔离、凭据保护和资源限制
- 错误处理不当:未正确处理 MCP 工具错误,导致模型无法从错误中恢复或报告已提交的副作用