AICoding之MCP(格式待调整版)

MCP

什么是mcp

mcp是一个模型上下文协议,定义了大模型与外部工具/服务的“通信方式”
指定了模型该调哪个工具、如何调用工具
基于json-rpc2.0格式通信,默认通过标准输入输出(stdio)传输,也支持httpSSE等方式(下文会讲应用场景)

整体架构

整体架构:宿主+客户端+服务器

  • 宿主:ai应用本地

  • 客户端:宿主内的连接器

  • 服务器:提供具体能力的外部服务,暴露各种工具

所有交互都基于JSON-RPC2.0协议进行双向通信,客户端和服务器都可以主动发起请求或通知。

规范是什么

核心机制主要分为四步

  • initialize:处理化握手信息,初始化协议版本、交换双方能力、声明服务信息

  • tools/list:枚举工具,返回所有可用工具信息(名称、描述、参数规格(JSON))

  • tools/call:执行工具,根据模型传入工具名+参数,执行业务逻辑并返回结果

  • shutdown:关闭通知

完整的生命周期

基础协议规范

1.消息格式基础

所有MCP消息必须严格遵循JSON-RPC2.0规范,共定义3种消息类型,且单条消息独占一行ModelContextProtocol。

请求(Request)

双向调用,必须携带唯一ID,接收方必须返回对应响应。

{ "jsonrpc":"2.0", "id":1, "method":"tools/list", "params":{} } /** jsonrpc:固定为"2.0" id:请求唯一标识(字符串/数字),禁止为null,同一会话内不可重复 method:要调用的方法名 params:方法参数(可选) */
响应(Response)

对请求的回执,与请求ID一一对应,分为成功和错误两种。

//成功响应 { "jsonrpc":"2.0", "id":1, "result":{"tools":[]} } //错误响应 { "jsonrpc":"2.0", "id":1, "error":{ "code":-32601, "message":"Methodnotfound" } }
通知(Notification)

单向消息,无id字段,接收方无需响应,多用于状态同步、事件推送。

{ "jsonrpc":"2.0", "method":"notifications/initialized", "params":{} }
2.标准错误码

MCP沿用JSON-RPC2.0标准错误码,并扩展了业务错误码:

错误码含义
32700解析错误:JSON格式非法
32600请求无效:不符合JSON-RPC规范
32601方法不存在
32602参数无效
32603内部错误
32000~-32099服务器自定义业务错误

传输层规范

MCP协议与传输方式解耦,官方定义了两种主流传输方式:

stdio(标准输入输出)

最常用的本地通信方式,适用于本地进程级别的MCP服务:
客户端启动服务器进程,通过stdin/stdout传输JSON消息
每行一条完整JSON消息,使用UTF-8编码
日志、调试信息必须输出到stderr,禁止写入stdout破坏协议流

HTTPSSE(Server-SentEvents)

适用于远程部署的MCP服务:
客户端通过POST请求发送JSON-RPC消息
服务器通过SSE流返回响应和主动推送通知
支持鉴权(BearerToken等)和会话管理
兼容StreamableHTTP模式(单次请求对应单次响应)

生命周期规范

MCP连接有严格的三阶段生命周期,所有实现必须遵守ModelContextProtocol:

阶段1:初始化(Initialize)

必须是连接后的第一次交互,完成版本协商和能力交换。
客户端发起initialize请求,携带:

  • 自身支持的协议版本

  • 客户端能力声明

  • 客户端名称与版本信息

服务器响应,返回:

  • 协商后的协议版本

  • 服务器能力声明

  • 服务器名称与版本信息

版本协商规则:

  • 客户端发送自身支持的最新版本

  • 服务器若支持该版本则原样返回;否则返回自身支持的最新版本

  • 客户端若不接受服务器返回的版本,应主动断开连接

阶段2:运行期(Operational)

客户端收到初始化响应后,必须发送notifications/initialized通知(无响应),之后进入正常交互阶段,可调用各类能力方法。

阶段3:关闭(Shutdown)

客户端发送shutdown请求,服务器完成清理后响应
客户端收到响应后发送notifications/exit通知,正式结束连接
异常断开时,双方应能正确处理连接中断

完整MCP报文交互实例

以下是基于stdio传输、协议版本2024-11-05的一次完整生命周期交互,C代表客户端(AI宿主,如Claude),S代表MCP服务端。

初始化握手(必选,第一条消息)

C→S:客户端发起初始化请求

{ "jsonrpc":"2.0", "id":1, "method":"initialize", "params":{ "protocolVersion":"2024-11-05", "clientInfo":{ "name":"claude-desktop", "version":"1.10.0" }, "capabilities":{ "sampling":{}, "roots":{} } } } /** *说明:客户端声明自身支持的协议版本、身份信息,以及自身具备的能力(采样、根目录访问等)。 */

S→C:服务端返回初始化响应

{ "jsonrpc":"2.0", "id":1, "result":{ "protocolVersion":"2024-11-05", "capabilities":{ "tools":{} }, "serverInfo":{ "name":"java-demo-mcp", "version":"1.0.0" } } } /** *说明:服务端确认协议版本,声明自身能力(此处仅声明支持工具调用),返回服务身份信息。 */
初始化完成通知(必选,无响应)

C→S:客户端通知初始化流程完成

{ "jsonrpc":"2.0", "method":"notifications/initialized", "params":{} }

⚠️关键注意:这是通知消息,没有id字段,服务端收到后绝对不能返回响应,否则会破坏协议流。

获取工具列表

C→S:客户端查询所有可用工具

{ "jsonrpc":"2.0", "id":2, "method":"tools/list", "params":{} }

S→C:服务端返回工具元数据

{ "jsonrpc":"2.0", "id":2, "result":{ "tools":[ { "name":"greet", "description":"生成个性化问候语", "inputSchema":{ "type":"object", "properties":{ "name":{ "type":"string", "description":"用户姓名" } }, "required":["name"] } }, { "name":"calculate_add", "description":"计算两个整数的和", "inputSchema":{ "type":"object", "properties":{ "a":{"type":"number","description":"第一个整数"}, "b":{"type":"number","description":"第二个整数"} }, "required":["a","b"] } } ] } }
调用工具(核心交互)

C→S:客户端调用指定工具

{ "jsonrpc":"2.0", "id":3, "method":"tools/call", "params":{ "name":"greet", "arguments":{ "name":"张三" } } }

S→C:服务端返回工具执行结果

{ "jsonrpc":"2.0", "id":3, "result":{ "content":[ { "type":"text", "text":"你好,张三!欢迎使用JavaMCP服务。" } ], "isError":false } }

说明:content是数组,支持多段内容;isError为可选字段,标记执行是否出错。

错误调用示例

C→S:调用不存在的工具

{ "jsonrpc":"2.0", "id":4, "method":"tools/call", "params":{ "name":"not_exist_tool", "arguments":{} } }

S→C:返回标准错误响应

{ "jsonrpc":"2.0", "id":4, "error":{ "code":-32602, "message":"工具不存在:not_exist_tool" } }
关闭流程

C→S:发起关闭请求

{ "jsonrpc":"2.0", "id":5, "method":"shutdown", "params":{} }

S→C:确认关闭

{ "jsonrpc":"2.0", "id":5, "result":{} }

C→S:退出通知(无响应)

{ "jsonrpc":"2.0", "method":"notifications/exit", "params":{} }

收到后服务端可安全退出进程。

如何写

如何加载

添加到对应工具里的配置文件

常见问题说明

服务端和客户端谁先启动?

SSE远程模式:必须服务端先启动并监听端口,客户端启动后才能连接成功,否则会报连接失败
stdio模式:客户端负责启动服务端进程,无需提前启动
Q:配置了远程服务但找不到工具,怎么排查?
A:按优先级排查:是否重启客户端→服务器端口/网络是否通→SSE端点路径是否匹配→鉴权头是否正确→协议版本是否兼容
Q:连接断开后会重新初始化吗?
A:会。网络波动导致SSE断开后,客户端自动重试重连,重连成功后会重新走完整的initialize初始化流程。

ai是如何选择要使用的mcp

AI不会直接“选择哪个MCP服务”,它选择的是具体的工具(Tool);MCP服务是工具的载体,一个MCP服务可以包含多个工具。
整个选择逻辑可以概括为:
客户端提前聚合所有已配置MCP服务的工具列表
把工具的名称、描述、参数规范注入大模型上下文
大模型基于用户问题,通过FunctionCalling能力判断是否需要调用工具、调用哪个工具
客户端根据工具归属,路由到对应的MCP服务执行调用
下面按完整流程拆解细节。

一、完整选择与调用全流程
阶段1:客户端侧的工具聚合(启动时完成)
在用户提问之前,客户端已经完成了所有准备工作,这一步和AI无关,是客户端的协议行为:
客户端启动后,读取所有mcpServers配置,逐个建立连接
对每个MCP服务调用tools/list接口,拉取该服务下全部工具的元数据(名称、描述、inputSchema)
客户端把所有服务的工具汇总成一张全局工具清单,记录每个工具归属的MCP服务
若工具名称重复,客户端通常会按服务名加前缀或直接报错去重
对应之前讲的MCP生命周期:初始化完成后,客户端会立刻拉取工具列表缓存起来,不会每次提问都重新拉取。

阶段2:提问前的预筛选(可选,客户端实现)
当MCP工具数量很多(几十上百个)时,全部塞进上下文会占用大量Token、干扰模型判断,因此主流客户端会做前置筛选,只把最相关的工具交给模型:
场景过滤:比如代码编辑器里只展示开发类工具,文档场景只展示文档类工具
语义召回:对用户问题做向量匹配,从全量工具中召回TopN个最相关的,再交给模型
权限过滤:根据当前用户/工作区权限,隐藏未授权的工具
用户手动开关:客户端通常提供工具启用/禁用开关,被禁用的工具不会进入模型视野

阶段3:模型侧的决策(核心环节)
筛选后的工具列表会以系统提示词/函数定义的形式注入到本次对话的上下文中,大模型基于用户输入自主决策。
本质就是标准的FunctionCalling逻辑,模型会做三层判断:

  • 要不要调用工具:判断用户问题仅凭模型自身知识能否回答,是否需要外部能力

  • 调用哪个工具:匹配工具描述与用户意图,选择最契合的一个或多个工具

  • 填充什么参数:从用户问题中提取参数,按JSONSchema格式生成入参

模型决策的内部逻辑:
模型会把每个工具的name+description当作“能力标签”,和用户的意图做语义匹配
描述越精准、和用户问题重合度越高,被选中的概率越大
若多个工具都能满足需求,模型会优先选择描述更匹配、参数更简单的那个
复杂问题下,模型可能选择多工具串行调用:先调用A工具拿到结果,再基于结果调用B工具

阶段4:客户端路由执行
模型输出工具调用指令后,客户端执行最终路由:
解析模型返回的工具名和参数
根据全局工具清单,查到该工具属于哪个MCP服务
向对应MCP服务发起tools/call请求
拿到执行结果后,把结果塞回上下文,让模型基于结果继续生成回答

影响AI选择的关键因素

AI选工具本质是语义匹配,你的MCP工具能不能被选中、会不会被误选,核心取决于4个要素:
1.工具描述(description)
这是影响选择的第一要素。
❌反面例子:“description”:“处理数据”(太模糊,模型无法判断适用场景)
✅正面例子:“description”:“查询指定服务器的CPU、内存、磁盘实时使用率,输入服务器IP地址”(场景、能力、入参都讲清楚)
描述越具体、越贴近真实使用场景,模型越容易在对应问题下选中它。
2.工具名称(name)
名称是语义匹配的强信号,模型会优先通过名称做初步匹配。
建议使用「动词+名词」的命名方式,比如query_server_metrics、generate_excel_report
避免使用缩写、黑话、无意义的命名
3.参数Schema与参数描述
inputSchema里每个字段的description也会影响模型判断,同时决定了模型能不能正确提取参数。
每个参数都要写清楚含义、格式要求(比如“格式为YYYY-MM-DD”、“单位为秒”)
必填项和可选项要明确区分
枚举值尽量列全,减少模型参数填充错误
4.工具排序与上下文位置
客户端传给模型的工具列表是有顺序的,排在前面的工具被选中的概率会略高。
多数客户端按MCP配置顺序+工具在tools/list中的返回顺序排列
做过语义预筛选的客户端,会把匹配度高的工具排在前面

常见的补充机制

1.用户手动干预
几乎所有MCP客户端都支持人工干预选择:
用户可以手动禁用某个MCP服务或单个工具,禁用后模型完全看不到
部分客户端支持「@工具」手动指定调用,强制模型使用某个工具
高危工具调用前会弹窗二次确认,用户同意后才会真正执行
2.多轮对话中的工具复用
在同一轮对话中,模型会记住已经调用过的工具,后续相关问题会优先复用已经用过的工具,而不是重新选一个新的。
3.错误重试与工具替换
如果调用某个工具失败、返回结果不符合预期,模型可能会:
调整参数重试同一个工具
换另一个功能相似的工具尝试
放弃调用工具,直接基于自身知识回答
四、优化建议:让你的MCP工具更容易被选中
如果你在开发MCP服务,可以按以下原则优化,提升工具被正确调用的概率:
工具名直白:用动宾结构,见名知意
描述写场景:不要只写“做什么”,还要写“适合什么时候用”
参数讲清楚:每个参数的含义、格式、取值范围都写完整
单一职责:一个工具只做一件事,不要做全能工具,否则描述会模糊,匹配度下降
避免重名:和常见工具(比如文件读写、搜索)区分开,防止被系统工具覆盖