MCP技术理解 - 第六篇
MCPClient通过AI应用调用Server,LLM仅负责推理输出而非直接调用。使用场景分两种:在现有AI应用中配置JSON文件启动Server,或开发者使用SDK编写Client代码。Client负责连接管理、协议转换及安全拦截,通过初始化、工具发现、指令执行等步骤完成交互。
引言
上一篇文章拆解了一个天气实时预报的MCP Server,帮助大家初步感知MCP的基本形态。本篇将重点探讨MCP客户端在实际使用中遇到的问题及配置方法,同时介绍几个MCP的高级特性。
为保证知识连贯,先简要回顾此前的系列内容:前五篇文章分别从协议基础、工作原理、Server实现等角度进行了梳理。本篇聚焦Client端,解决“如何调用”“如何配置”等落地问题。
如何调用MCP Server
很多人误以为只需让LLM发出指令就能直接调用MCP Server。实际上,LLM不具备外调能力——它无法直接发起HTTP请求或执行系统命令。
调用MCP Server的场景分为两类,但无论哪种情况,真正的调用方都是AI应用通过MCP Client完成的,LLM仅负责推理输入和输出。理解这两类场景后,MCP Client的配置细节将更加清晰。
以下是两个关键认识:
- 安全隔离:可在Client层实现前置拦截。若LLM试图执行
rm -rf /等危险操作,Java或Go代码能在发送给Server前检测并拦截。 - 协议转换:LLM输出文本/Token,MCP Server需要JSON-RPC消息。Client充当适配器(Adapter),完成两种格式的互转。
场景一:作为用户在现有Host中调用
如果只是想用Claude Code、Cursor等现成AI应用调用已有的MCP Server(包括自己编写的),无需编写MCP Client代码,仅修改配置文件即可。此时Host即AI应用本身,已内置MCP Client代码。配置好MCP Server后,它会自动维护连接并完成后续流程。
场景二:作为开发者通过代码调用
若需要在Java、Go或Python程序中调用MCP Server——例如构建一个能调用本地工具的AI Agent——则需自行实现MCP Client。
以Stdio传输方式为例,核心交互流程分为三步:
- 启动进程:程序(Client)启动MCP Server作为子进程,例如执行
npx -y @modelcontextprotocol/server-filesystem。 - 握手(Initialization):发送
initialize请求,协商协议版本和能力。 - 发送指令:发送
tools/list、tools/call、resources/read等JSON-RPC消息。
在此场景下,需要自行编写MCP Client代码,处理初始化链接、协议维护、消息发送和响应处理。
一次MCP调用示例
通过完整调用链演示LLM在整个过程中的角色,各角色职责如下:
- AI应用:负责全局管控,协调请求与负载均衡。
- MCP Client:连接Server、管理进程、维护对话上下文。
- MCP Server:无状态Worker,不知上下文,只负责接收指令 → 执行 → 返回结果。
- LLM:无“手脚”(不能联网、不能读文件),负责分析用户自然语言,对比AI应用提供的Tools List,生成结构化JSON,指示AI应用调用函数。
完整交互流程如下:
- 初始化(Handshake):
- Client启动Server。
- Client询问Server:“你有什么本事?”(Tools List)
- Server回答:“我会读文件(
fs.read),我会查数据库(db.query)。”
- 用户提问:
- 用户对Client说:“帮我查一下
user_id=101的订单。”
- 用户对Client说:“帮我查一下
- Prompt组装(AI应用内部逻辑):
- AI应用将System Prompt + Server提供的工具定义(Schema)+ 用户问题,打包发给LLM。
- LLM决策:
- LLM推理后,发现
db.query工具可解决此问题。 - LLM返回给AI应用(而非直接给Server)结构化指令:
{ "tool_use": "db.query", "params": { "sql": "SELECT * FROM orders WHERE user_id=101" } }
- LLM推理后,发现
- 执行(真正的MCP调用发生在此):
- AI应用解析LLM响应,控制MCP Client发送请求和数据。
- Client通过JSON-RPC(Stdio/SSE)向MCP Server发送
tools/call请求。
- Server响应:
- Server执行SQL,返回JSON数据给Client。
- 回环(Loop Back):
- AI应用接收Client返回的数据,将Server执行结果再次喂给LLM。
- AI应用说:“你让调用的工具,结果是
[Order A, Order B],请生成最终回复。”
- 最终输出:
- LLM生成自然语言回复:“用户你好,ID为101的用户有两笔订单……”
在AI应用里面配置MCP Server
通过上述解析,整体调用过程已清晰。接下来按场景逐步拆解MCP Client配置问题。
在Claude Code等现成AI应用中,只需配置MCP Server,无需编写MCP Client代码。以Claude Code为例,配置信息写在claude_desktop_config.json文件中:
// ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"weather": {
"command": "uv",
"args": ["--directory", "/absolute/path/to/weather", "run", "weather-server"]
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/username/Documents"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_your_token_here"
}
}
}
}
每项配置可理解为在终端执行一个长连接子进程。关键参数拆解如下:
1. mcpServers:根节点,是一个Map。Key(如weather、github)是该Server的唯一标识符,用于AI界面显示与内部隔离。
2. command(可执行文件)
指定Client启动的二进制文件或脚本解释器,入口程序。常见值包括npx(Node.js)、python、uv(Python包管理器),或编译好的Go/Java二进制文件。
3. args(启动参数)
字符串数组,追加在command后,形成完整shell启动命令。
- weather示例:
uv --directory /path/to/weather run weather-server,指示uv切换到特定目录并运行项目。 - filesystem示例:
npx -y @modelcontextprotocol/server-filesystem /Users/username/Documents。-y表示自动确认安装,最后一个参数是传给Server内部的白名单机制,限定允许访问的文件夹。
4. env(环境变量)
子进程启动时注入的环境变量,用于传递敏感信息(API Keys)或配置参数,避免硬编码在代码或命令行中。类似Docker中配置ENV或本地.env文件。GitHub示例中,GITHUB_PERSONAL_ACCESS_TOKEN被注入进程上下文中,Server代码可通过os.Getenv("GITHUB_PERSONAL_ACCESS_TOKEN")(Go)或System.getenv(...)(Java)获取。
通过代码来调用MCP Server
对应场景二——在自己构建的AI应用中,使用官方SDK编写MCP Client代码。
官方提供多种SDK:后端开发常用的Go SDK由Google和Go官方维护,Java SDK由Spring AI官方维护,Python SDK由MCP官方维护。
以下是Python SDK编写MCP Client的示例代码:
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
# 配置服务器参数
server_params = StdioServerParameters(
command="uv",
args=["--directory", "/path/to/weather", "run", "weather-server"],
)
async def main():
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# 初始化连接
await session.initialize()
# 列出可用工具
tools = await session.list_tools()
print("A vailable tools:", tools)
# 调用工具
result = await session.call_tool(
"get_forecast",
arguments={"latitude": 37.7749, "longitude": -122.4194}
)
print("Forecast:", result)
# 列出资源
resources = await session.list_resources()
print("A vailable resources:", resources)
# 读取资源
config = await session.read_resource("weather://config")
print("Config:", config)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
总结
本篇解答了MCP Client的核心问题:Client如何调用Server?在不同场景下如何配置或编写客户端代码?结合前文,大家对MCP的整体理解已更加完整。下一篇将继续深入介绍MCP的高级特性与注意事项,这些实际落地中尤为重要。
