Table Of Content
前言
本文主打一个快速建立对 MCP 的认知,不讲太多细节。如果你只是想知道 MCP 是什么东西,为什么会需要 MCP,推荐你看完本篇文章。如果你想知道 MCP 的各种技术细节,我推荐你直接去 Model Context Protocol 查看完整的协议内容。
MCP 是什么?
官方文档:https://modelcontextprotocol.io/docs/getting-started/intro
我不会直接说 MCP 是什么,要真正理解,我觉得应该从技术发展遇见了什么问题,从而又提出了什么问题来解决,才能真正理解一项技术。
在 LLM 爆火初期,模型只能进行简单的对话,这个时候模型是没有现在的工具、Skill、MCP 这些扩展能力的,除了聊聊模型训练数据涉及的内容,其他的要么不知道,要么已读乱回。
后来又有了工具调用,其实就是在对话的时候,往 prompt 里面注入一段特定的内容,包括当前有哪些工具可以用,每个工具叫什么名字,适用于什么场景,出入参数说明。最常见的 demo 就是很多教程里面都会写的天气查询。
但是写一次就会发现一个问题,Agent 应用运行时有什么工具通常都是在代码里面写死的,如果我需要加一个新工具,那我需要修改 Agent 应用代码重新上线,这不是很优雅。那么有没有一种办法,当每次发起新的 session 时,只需要从某个地方获取到当前有什么工具可以使用就行了,而不是在代码里面写死固定几个工具的注入,这就又回到了经典的 CS 架构。
MCP 其实就是为了解决这个问题,架构上也分为 3 部分:
- mcp server,提供工具、资源服务。
- mcp client,从 mcp server 获取可用的工具、资源。
- mcp host,其实就是使用 mcp client 的应用自身。
当需要加新的工具时,其实只需要在 mcp server 上增加新的工具即可,mcp server 更新之后,mcp client 能拿到的信息自然就变了,agent 应用也就完成了新的工具注入。
老外造概念真的很烦,就 client、server 就够了,非要再搞个 host。
总之,mcp 主要解决的是工具的动态注入问题。
MCP 的通信协议
Client、Server 之间的通信,主要分了两种场景,三种协议:
Stdio
属于在本地的进程间通信的长场景,直接通过操作系统的标准输入、输出流来做数据传输。
我是做 Java 的,很多日志框架也会把各种日志也通过 Stdio 输出,所以使用这种方式的时候,你的代码本身就别打日志了,因为你的日志输出也会被当成 MCP Server 的响应信息。
SSE(Server Side Event)
适用于远程的通信,当然同一个操作系统实例上的两个进程通过这种方式通信也可以。早期的 LLM 聊天的时候,蹦词器的效果就是通过 SSE 协议实现的,后来 MCP 也使用了这种协议,本质上也是基于 Http 协议,响应是流式的,通过长链接,一直往响应里面塞 chunk。
在 client、server 的交互规范里面,要求 mcp server 提供了两个端点(网络接口):
- /sse,用于 Client 告诉 Server,要开始一个会话了,这里建立的是一个长连接,告诉 Server 以 text/event-stream 响应,也就是流式,同时这次调用首先就会返回另一个端点的地址。
- /mcp/message,一般会是在调用 /sse 端点之后,从返回的第一个 chunk 里面拿到的这个地址,不过后面会跟一个 sessionId 参数,后续对 mcp server 的调用请求都往 /mcp/message?sessionId=xxxx 这个接口发。但是,工具调用的结果不是从这个接口调用中返回的,而是在上次调用 /sse 端点建立的流式响应的长连接里面返回的。
下面是 SSE 协议下的 MCP Client 和 MCP Sevrer 的交互流程:
这个设计其实比较反人类,但是想想其实是有必要的:
比如像调用一个 MCP 爬取页面的场景,如果是直接一个 http 请求,然后保持连接等待响应的话,这个过程所耗费的时间,站在传统应用的视角看,是很长的,这个时候如果让用户傻等着,可能就以为是程序挂了,就直接关闭走人了,而如果是按现在的两个端点协作的方式,那么在 mcp server 端是可以实时往 mcp client 推送进度的,用户也就能看见新的信息出来。
但是 SSE 这种模式是存在问题的:
- 要维护两个连接,因为有两个端点,浪费资源,要是 toC 的应用背后的 MCP Server 和 Client 交互是使用 SSE,这个资源浪费就更加明显了。
- 断点重连,不支持。因为 /sse 这个端口是没带任何标识的,这个长连接一旦断开了,不管是哪一方主动断开,都是没办法重新连接上,然后按照原来的 sessionId 继续交互。
Streamable Http
适用于远程通信,算是在 MCP 场景下,对 SSE 的一种改进:
- Streamable Http 协议下,只需要一个端点进行交互,这个端点会同时支持普通 http 请求和 SSE。
- 支持端点重连。实际情况是,有些 mcp 的开源项目可能已经支持了,有些可能需要自己实现,毕竟断点重连不是 MCP 协议的标准内容,只是具体实现的稳定性考量。
协议具体内容参考:https://modelcontextprotocol.io/specification/2025-11-25/basic/transports
核心内容是:
- mcp server 只需要提供一个端口,同时支持普通 http 和 SSE。
- mcp client 初始化请求的时候,Accept 请求头需要包含 application/json 和 text/event-stream 两种。
- mcp server 任何时刻都可能主动断开 SSE 的连接,但是断开前,需要往客户端推送一条消息,里面会有 retry 相关信息。
- 考虑到可能是网络原因导致的断开连接,mcp server 还需要支持客户端通过 lastest-event-id 恢复意外断开的连接。
今天,MCP 死了吗?
没死。 死的是那些说 MCP 已死的人的脑子。
关于 MCP 还有没有必要存在这个问题,最早应该是在 Skill 开始流行那段时间出现的。但是愚蠢的人类没搞清楚 MCP 和 Skill 分别解决了什么问题,单纯就是觉得 Anthropic 提出的 Skill 这个概念秒天秒地了,MCP 是垃圾。
回到问题本身,前面已经讲过了 MCP 解决的核心问题是什么,在 Skill 盛行的现在,问题依然只能通过 MCP 来解决,Skill 只能给一个行动的大纲,告诉你某一步骤应该调用什么能力,这个被调用的能力,复杂点的说不定就是从 MCP 来的。
有些杠精非要嘴贱说我把这个能力也内化到 Skill 里面,还有 MCP 啥事。 真是单细胞生物。最简单的回答:Skill 里面要写参数校验,LLM 能稳定吗?Skill 里面要写事务,用啥来做数据库事务,一张嘴吗?