系列文章:
- 动手做一个AIAgent - 简易框架搭建
- 动手做一个AIAgent - LiteLLM
- 动手做一个AIAgent - 流式输出与视觉支持
- 动手做一个AIAgent - MCP
- 动手做一个AIAgent - SKILLS
- 动手做一个AIAgent - RAG基础
在之前的文章里我们已经知道agent里llm是如何去调用tool的,但我们之前的tool是直接写在python代码里面的。
如何支持用户接入第三方的工具给llm调用呢?这就要用到mcp了。
mcp本质上是一套基于json的发送和接收数据的协议,用于告诉llm有哪些工具可以使用和如何使用:
从上面的图我们可以看到mcp协议里面分为三个角色:
- MCP Server : 业务系统搭建的服务,用于将本业务的功能通过llm可以理解的方式暴露出来
- MCP Host : 发起mcp请求的ai应用,如我们开发的ai agent
- MCP Client : MCP Host内部与MCP Server具体进行通讯的组件
除了三种角色之外,MCP还有三个重要的概念,client可以向server请求下面三种东西:
- Resources : 可以是文件系统、数据库等原本无法从网络中直接获取到的静态的资源,也可以是一些基础的配置信息。它以URI的方式去定义,例如后面定义的
weather://citys这个资源。 - Tools : 提供给ai调用的工具,和我们之前定义的tool函数是一样的概念
- Prompts : 某些场景下的预设提示词,如格式化的输出某些内容,ai在需要的时候可以从mcp server选择Prompts去加载
由于协议本身使用json去通讯,所以可以很方便的使用各种语言去开发,官方也提供了各种语言的sdk,这里我依然使用python去开发。
实际上我们只需要pip去安装mcp的库就可以开始开发了:
1 | pip install mcp |
MCP Server
我们这里先给出一个简单mcp server demo:
1 | # mcp_server.py |
其实代码比较简单,无非是提供了weather://citys这个resource去获取城市列表,和get_weather这个tool去获取指定城市的天气信息。
PS: Prompts这里没有用到,但用法也是一样的直接使用@mcp.prompt()即可
MCP JSON协议
Server的代码写好之后就可以写Client的代码去请求它,但在使用sdk编写client代码去调用之前我想先深入到mcp协议的原理来看看。
之前有说过mcp本质就是用json去发送和接收数据的协议,通讯链路有两种
- 如果server运行在云端则通过http的
sse或者streamable-http去传输数据 - 如果是本地的server则直接将mcp server直接作为子进程启动通过标准输入输出去通讯
整个通讯的生命周期如下:

初始化有下面三步:
- client发送初始化请求将自己的信息告诉server
- server将自己的信息返回给client作为初始化响应
- 客户端发送初始化完成的通知给服务端
例如我们可以直接通过python3 mcp_server.py启动mcp server,然后终端就会阻塞主等待我们的输入
这个时候我们可以手动输入json去模拟client发送初始化请求给server:
1 | {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"ExampleClient","version":"1.0.0","description":"An example MCP client application"}}} |
server会返回初始化响应,打印在终端上:
1 | {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-11-25","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"demo-weather-server","version":"1.26.0"}}} |
然后我们再输入初始化完成的通知:
1 | {"jsonrpc":"2.0","method":"notifications/initialized"} |
虽然server不会有任何的响应,所以终端上看是没有任何的反应,但实际上初始化已经完成了。
我们这个时候可以发送列出支持的tools的请求:
1 | {"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"cursor":"optional-cursor-value"}} |
就能看到定义在server的get_weather了:
1 | {"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"get_weather","description":"Get the current weather for a city. should get support cities from resource weather://citys first","inputSchema":{"properties":{"city":{"title":"City","type":"string"}},"required":["city"],"title":"get_weatherArguments","type":"object"}}]}} |
也可以使用tools/call方法去调用工具:
1 | {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_weather","arguments":{"city":"guangzhou"},"task":{"ttl":60000}}} |
然后回在终端里面看到server返回的结果:
1 | {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\n \"city\": \"guangzhou\",\n \"temp\": 22,\n \"condition\": \"sunny\"\n}"}],"isError":false}} |
MCP Client
从上面的通讯原理我们可以看到,MCP协议其实并不复杂,client的demo我们使用stdio_client去通过标准输入输出调用mcp_server.py:
1 | import asyncio |
在命令行python3 mcp_client.py运行client就能看到server的一些信息了:
1 | Connected to server: demo-weather-server (version 1.26.0) |
Agent中接入
如何将mcp的信息转换成completion的tools参数可以参考我的demo,本质上是一种json格式到另一种json格式的转换
接入完成之后就询问广州天气怎样就可以看到agent先通过weather://citys查询城市列表再通过get_weather获取到天气信息。然后我还加入了智谱的联网搜索api,在配置文件里填上你的key就可以通过网络调用到智谱的api去进行智能搜索: