MCP什么是mcpmcp是一个模型上下文协议定义了大模型与外部工具/服务的“通信方式”指定了模型该调哪个工具、如何调用工具基于json-rpc2.0格式通信默认通过标准输入输出stdio传输也支持httpSSE等方式下文会讲应用场景整体架构整体架构宿主客户端服务器宿主ai应用本地客户端宿主内的连接器服务器提供具体能力的外部服务暴露各种工具所有交互都基于JSON-RPC2.0协议进行双向通信客户端和服务器都可以主动发起请求或通知。规范是什么核心机制主要分为四步initialize处理化握手信息初始化协议版本、交换双方能力、声明服务信息tools/list枚举工具返回所有可用工具信息名称、描述、参数规格JSONtools/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破坏协议流HTTPSSEServer-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宿主如ClaudeS代表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是如何选择要使用的mcpAI不会直接“选择哪个MCP服务”它选择的是具体的工具ToolMCP服务是工具的载体一个MCP服务可以包含多个工具。整个选择逻辑可以概括为客户端提前聚合所有已配置MCP服务的工具列表把工具的名称、描述、参数规范注入大模型上下文大模型基于用户问题通过FunctionCalling能力判断是否需要调用工具、调用哪个工具客户端根据工具归属路由到对应的MCP服务执行调用下面按完整流程拆解细节。一、完整选择与调用全流程阶段1客户端侧的工具聚合启动时完成在用户提问之前客户端已经完成了所有准备工作这一步和AI无关是客户端的协议行为客户端启动后读取所有mcpServers配置逐个建立连接对每个MCP服务调用tools/list接口拉取该服务下全部工具的元数据名称、描述、inputSchema客户端把所有服务的工具汇总成一张全局工具清单记录每个工具归属的MCP服务若工具名称重复客户端通常会按服务名加前缀或直接报错去重对应之前讲的MCP生命周期初始化完成后客户端会立刻拉取工具列表缓存起来不会每次提问都重新拉取。阶段2提问前的预筛选可选客户端实现当MCP工具数量很多几十上百个时全部塞进上下文会占用大量Token、干扰模型判断因此主流客户端会做前置筛选只把最相关的工具交给模型场景过滤比如代码编辑器里只展示开发类工具文档场景只展示文档类工具语义召回对用户问题做向量匹配从全量工具中召回TopN个最相关的再交给模型权限过滤根据当前用户/工作区权限隐藏未授权的工具用户手动开关客户端通常提供工具启用/禁用开关被禁用的工具不会进入模型视野阶段3模型侧的决策核心环节筛选后的工具列表会以系统提示词/函数定义的形式注入到本次对话的上下文中大模型基于用户输入自主决策。本质就是标准的FunctionCalling逻辑模型会做三层判断要不要调用工具判断用户问题仅凭模型自身知识能否回答是否需要外部能力调用哪个工具匹配工具描述与用户意图选择最契合的一个或多个工具填充什么参数从用户问题中提取参数按JSONSchema格式生成入参模型决策的内部逻辑模型会把每个工具的namedescription当作“能力标签”和用户的意图做语义匹配描述越精准、和用户问题重合度越高被选中的概率越大若多个工具都能满足需求模型会优先选择描述更匹配、参数更简单的那个复杂问题下模型可能选择多工具串行调用先调用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服务可以按以下原则优化提升工具被正确调用的概率工具名直白用动宾结构见名知意描述写场景不要只写“做什么”还要写“适合什么时候用”参数讲清楚每个参数的含义、格式、取值范围都写完整单一职责一个工具只做一件事不要做全能工具否则描述会模糊匹配度下降避免重名和常见工具比如文件读写、搜索区分开防止被系统工具覆盖