MCP Client 集成
本节定位
前面我们已经从 server 视角看了 MCP。
这一节换个方向,从 client 视角来看:
客户端怎样发现、选择并调用 MCP Server 的能力?
这一步很关键,因为真正用工具的往往不是 server,而是 client。
学习目标
- 理解 MCP Client 的核心职责
- 学会把“发现工具”和“调用工具”分成两步看
- 看 懂一个最小 MCP Client 调用流程
- 理解 client 侧为什么仍然需要选择策略、失败处理和缓存
一、Client 和 Server 的职责到底怎么分?
1.1 Server 提供能力
Server 更像“工具仓库管理员”,它负责:
- 列出工具
- 暴露能力
- 执行调用
1.2 Client 负责消费能力
Client 更像“真正来办事的人”,它负责:
- 发现工具
- 决定调用哪个
- 组织参数
- 接收结果
所以非常重要的一点是:
MCP Client 不是被动转发器,它通常仍然有自己的调用决策逻辑。
二、Client 最先要学会什么?先发现工具
2.1 为什么不能直接写死?
如果 client 一开始就把工具全写死:
- server 工具一变就要改代码
- 换一个 server 也要重写
这和 MCP 想解决的问题正好反着来。
2.2 一个最小发现示例
class MockMCPServer:
def list_tools(self):
return [
{"name": "search_docs", "description": "搜索课程文档"},
{"name": "get_weather", "description": "查询天气"}
]
server = MockMCPServer()
tools = server.list_tools()
for tool in tools:
print(tool)
2.3 这一步在教你什么?
它在教你:
client 先要知道“能用什么”,再谈“怎么用”。
这就是发现阶段的价值。
三、发现完以后,client 还要做什么?
3.1 选择工具
不是所有工具都要调。
客户端通常要先判断:
- 当前问题需不需要工具
- 如果需要,调哪个
3.2 组织参数
就算选对工具,也还要正确组织参数。
3.3 处理错误
如果:
- server 超时
- 工具不存在
- 参数校验失败
client 不能只崩掉,还要决定:
- 要不要重试
- 要不要降级
- 要不要换工具
四、一个最小 Client 示例
4.1 可运行代码
class MockMCPServer:
def list_tools(self):
return [
{"name": "search_docs", "description": "搜索课程文档"},
{"name": "get_weather", "description": "查询天气"}
]
def call_tool(self, name, arguments):
if name == "search_docs":
return {"result": f"检索结果: {arguments['query']}"}
if name == "get_weather":
return {"result": f"{arguments['city']} 当前晴天 22 度"}
return {"error": "unknown_tool"}
class MockMCPClient:
def __init__(self, server):
self.server = server
self.tools = []
def discover(self):
self.tools = self.server.list_tools()
return self.tools
def call(self, name, arguments):
return self.server.call_tool(name, arguments)
server = MockMCPServer()
client = MockMCPClient(server)
print(client.discover())
print(client.call("search_docs", {"query": "退款政策"}))
4.2 这段代码已经在说明什么?
它已经体现了 client 的两大主功能:
- 发现
- 调用
这就是 MCP Client 的最小闭环。
五、Client 其实还有“策略层”
5.1 为什么说 client 不只是协议调用器?
因为真实系统里,client 往往还要决定:
- 当前问题需不需要走 MCP
- 如果走,优先哪个 server / 哪个工具
- 失败后如何回退