购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.3
MCP的技术实现:架构、组件与工作原理

本节将深入MCP的技术实现部分,为读者系统解读MCP的底层实现。须知,只有搞懂底层技术原理,方能正确理解、应用、构建MCP相关的工具和产品。本节将从以下几个方面展开介绍:

MCP的整体架构:从宏观视角讲解MCP架构,建立系统性认知。

MCP的核心组件:剖析MCP分层架构设计中的角色分工,掌握Host(主机)、Client(客户端)与Server(服务器)的职责定义和边界。

MCP组件的协同机制:介绍主机、客户端、服务器三个核心组件如何各司其职,协作完成一个完整的流程闭环。

MCP的通信机制:阐释基于JSON-RPC协议的双向实时通信机制的设计考量及其优缺点。

MCP的安全机制:介绍MCP在人与Agent协同时所采用的各种安全机制。

2.3.1 MCP的整体架构

与互联网中许多采用Client(客户端)-Server(服务器)架构的经典协议(HTTP、WebSocket等)不同,MCP采用了主机-客户端-服务器三层架构体系,如图2-3所示。这是非常有独创性的,这种独创性体现在Anthropic的工程师对于AI Agent时代AI系统的独特理解。

在WebSocket协议中,客户端和服务器之间的通信就像两个陌生人对话,每次交流都是独立且没有记忆的,也没有延续性。HTTP更是如此,每一个请求都是一次性的,就像擦肩而过的陌生人。在互联网时代,这种无状态协议的设计是合理的,因为协议的本质只是为了解决资源互联的问题。

但是,大模型为AI Agent应用与互联网应用带来了完全不同的叙事,用户不再局限于一问一答,而是需要具备上下文管理和全局记忆能力的AI Agent。每一轮对话、每一次工具调用的结果都会影响一个Agent任务的走向和推理效果。因此,MCP需要定义一个状态管理角色,来统筹管理Agent的全局上下文和客户端的生命周期,这就是MCP主机。从这个角度讲,MCP不仅是一个通信协议,还是一种全新的AI交互范式。在这个架构中,信息不再是冰冷的数据流,而是充满生命和智慧的动态网络。

图2-3 MCP架构图

MCP规定,客户端由主机统一创建,主机通过客户端与一一对应的服务器进行通信,以获取服务器提供的资源、Prompt模板或者可用的工具清单,以及调用指定的工具等。这些服务可以部署在用户的本地计算机上,也可以部署在远程服务器上。每一个服务器都由企业、开发者或者用户精心构建,开发者通过MCP规定的接口规范,封装了各种各样的能力,譬如查询天气、下个外卖订单、提交一个GitHub的Issue、创建一个本地文档等。

2.3.2 MCP的核心组件

1.MCP主机

相信读者对于大热的AI编程工具Cursor应该有所耳闻。Cursor是一个桌面应用,安装在本地计算机中。在Cursor接入MCP之后,它就是一个实实在在的MCP主机了。在AI系统中,MCP主机承担着协调、管理和保护的多重角色,具体职责如下。

(1)客户端实例管理

主机最基本的职责是创建和管理多个客户端实例。这就像复杂的剧组调度,每一个AI模型、每一个客户端都是这场数字交响乐中的独特乐器。主机精确地控制着它们的生命周期,确保系统的协同与平衡。

(2)执行安全策略和权限控制

在AI Agent应用中,当AI掌握了工具的调用后,授权机制就变得重要起来。人类要将什么样的工具使用权限以什么样的模式交给AI Agent,是非常严肃且重要的问题。稍不小心就有可能带来灾难性的结果。譬如,你告诉AI帮你整理一下混乱不堪的桌面,如果你没有设置安全策略和操作权限,AI很有可能因为模型的执行效果问题而误删很多文件。因此,MCP规定MCP主机必须承担工具执行、安全授权等职责,主要职责如下:

客户端连接权限控制。

工具调用时处理用户授权决策。

执行安全策略和协议许可。

(3)上下文管理

在AI应用中,上下文就是一切,它决定了用户的对话空间。MCP主机负责跨多个客户端进行上下文聚合,就像一个智能的档案管理员,它的职责有以下几种:

追踪和整合分散的对话片段:包括不同客户端的工具调用结果,以及服务器提供的资源等,MCP主机需要将这些信息有序地组装、拼接起来,最终形成一个合适的Prompt文本交给模型去推理。

维护会话的连贯性和深度:AI Agent要解决的通常都是复杂问题,需要很多步的推理和迭代,因此AI Agent的会话历史是否连贯且易于理解,会直接影响大模型的推理效果。因此,MCP主机需要精心管理会话历史,找到推理效果、用户体验、推理成本三者之间的平衡。

管理和过滤上下文信息:在多轮对话中,并不是每次都需要将过程中产生的所有信息拼接到给模型的消息历史中。比如,当模型阅读完一个超长的本地文档并回答问题之后,在后续的子任务中,很可能不需要把这个本地文档的全部内容放在消息历史中。因为有可能文档内容与后面要解决的问题没有关联,也可能是出于对推理Token消耗成本的考量。在实际的AI应用中,管理和过滤上下文的策略非常丰富,也非常个性化。

表2-1是MCP官方整理的目前市场上支持MCP的主机清单,以及它们支持MCP功能的情况(截至2025年3月29日)。

表2-1 支持MCP的MCP主机一览表

(续)

2.MCP客户端

在MCP架构中,MCP客户端是直接与MCP服务器交互的执行单元,其角色类似“协议适配器”。每个客户端实例由主机创建,专门负责与单个服务器建立通信管道、转换协议格式、执行安全校验等具体事务。它和服务器是一一对应的,且与其他客户端和服务器之间是完全隔离的。客户端和主机的关系可以用一个形象的比喻来阐述:主机是“外交部长”(制定政策、管理多国关系),客户端是“驻外大使”(在特定地区执行具体事务)。MCP客户端的职责主要有以下几种。

(1)会话通道管理

客户端的首要职责是建立并维护与服务器的标准化通信。它实现了基于JSON-RPC 2.0的消息交换机制,管理连接的生命周期,包括初始化有状态的双向连接、保持连接活跃以及优雅地关闭连接。客户端还需要实现能力协商,向服务器明确声明自身支持的功能特性,并提供错误处理和日志记录,确保通信过程的可靠性和可追踪性。一个简单的客户端实现伪代码如下:

1 // 伪代码:客户端连接初始化

2 class McpClient {

3  constructor(transport:Transport){

4    this.connection = new JsonRpcConnection(transport);

5

6    // 维护会话状态机

7    this.connection.on('error',(err)=> {

8      if(err.isRecoverable){

9        this.reconnect();// 自动重连机制

10      } else {

11        this.shutdown(); // 向主机报告致命错误

12      }

13    });

14  }

15 }

(2)根(Root)路径管理与安全

Root作为MCP客户端中的一个安全特性,往往被MCP主机的开发者所忽视,包括Anthropic自己开发的Claude。但这并不意味着这个特性就不重要,MCP的设计者充分考虑了客户端和服务器在协同时可能会带来的各种安全问题。譬如,用户自主添加了一个由社区开发者开源的本地文件操作MCP服务器,在执行用户的Agent任务时,服务器删除了某个未经授权的文件目录,而用户完全不知情。值得注意的是,安全问题的解决在新技术发展中一定是滞后的,现阶段社区还沉浸在AI Agent刚长出了“手脚”、可以连接一切的喜悦中。因此,当前开源社区的各类MCP服务器并未充分考虑操作的安全性,读者在使用时需要格外注意。

Root设计的本质是“资源访问沙盒化”,即由每一个客户端定义服务器可以访问的资源范围。由客户端在初始化时定义对应的服务器可以操控的资源范围,如下代码所示:

1 const client = new Client(

2  {

3    name:"example-client",

4    version:"1.0.0"

5  },

6  {

7    capabilities:{

8      roots:{

9        listChange:true,

10        uris:[{

11          name:'用户目录',

12          uri:'file://~/'

13        }]

14      }

15    }

16  }

17 );

MCP规定,上述代码中的uris的设置需要由主机的开发者自行设计用户界面来进行授权,可以是一个类似目录选择的表单交互,也可以是任何其他形式。这种设计的理念就是将安全交由用户自己掌控,而不是完全交给AI Agent。更细致的组件协同机制和安全设计考量会在后面章节中详细展开。

(3)工具执行网关

MCP规定,工具是由MCP服务器定义和执行的,但MCP客户端却承担了工具执行网关的职责,以确保工具被安全、有效地执行,其中包含了3层校验机制:

协议层校验:模型生成的工具调用结果中参数格式是否符合Tool Schema定义。比如前文的getWeather函数,模型Function Call的输出如果丢失了必填参数city,则是一个不合法的工具调用。

语义层校验:是否在MCP服务器已声明的工具范围内。比如模型因为幻觉问题导致输出的Function Call结果是get_weather,而不是MCP服务器定义的getWeather,这也是需要校验的。

权限层校验:用户是否授权此次工具的调用。

(4)模型采样(Sampling)控制器

Sampling(采样)是MCP定义的一个特殊功能,它允许服务器通过客户端向由主机控制的LLM发起推理请求(如文本、音频、图像等),同时确保客户端保留对模型访问权限和选择策略的完全控制权,并将用户也设计到流程中,让用户负责审计服务器发起的推理请求的内容和结果告知。

这个设计非常特别。虽然当前除了fast-agent外,几乎没有任何主机支持该特性,网络上关于Sampling的应用场景解读也几乎没有,但细想之下,Sampling的设计哲学显然是具备“智能思维”的。

试想,如果没有Sampling机制,在MCP的架构中,MCP服务器只是一个冷冰冰的工具或者执行者,机械地执行着主机的所有指令,那么这套架构就是一套“用户—主机—服务器”的系统,没有考虑到服务器本身同样可以拥有权力和智能。MCP生态中有无数个MCP服务器,在当下看它们或许只是一个操作本地文件的函数、查询天气的API调用,但只要稍微延伸一下,MCP服务器就可以是一个拥有智能的AI Agent。它既可以机械地执行某个动作,也可以为用户的任务目标提供智能的方案贡献。举一个地图的例子:

1 User:帮我规划一个从五道口出发的北京一日自驾游路线

2 Assistant:×××经典的北京一日游通常会去故宫、颐和园和八达岭……我需要先查一下五道口距离这几个景点的距离……

3 Toolcall(MCP工具调用):distance(五道口,[故宫,颐和园,八达岭])

4 toolresult:五道口→故宫的距离是15km,驾车时间为40min……

5 Sampling:故宫周边不好停车,建议第一段行程乘坐地铁出行,申请重新调整计划

6 User:建议得好,可以考虑,你再继续吧

7 Assistant:已根据刚才的建议生成新的计划:×××

8 User:方案很好,感谢你的贡献

上述例子可以看出,地图的MCP服务器不是作为一个工具提供方存在的,而是一个全方位的地图智能体。除了调用工具之外,它还可以根据自身经验参与到用户的问题解决流程中,以便用户获得更优质的结果。此外,这一切都是在User的监控和授权之下进行的。

Sampling机制让MCP服务器既不是纯粹的工具,也不是潜在的反叛者,而是被技术协议赋能的“数字公民”—它们能在规则框架内争取权益、贡献智慧,而人类始终掌握着最终权威。这或许是MCP中最不起眼但最深邃的创新:构筑了一个比传统AI系统更富有生命力的数字文明雏形。

3.MCP服务器

MCP服务器是面向AI应用设计的标准化服务组件,它通过结构化协议为客户端提供上下文数据、工具功能与提示词模板。与传统的API服务不同,MCP服务器专注于满足LLM对动态上下文、领域工具和安全隔离的需求,其核心价值在于将AI能力模块化封装,实现即插即用。MCP服务器的核心职责有以下几种。

(1)为模型推理提供上下文

MCP服务器以资源(Resource)为核心载体,向客户端暴露结构化数据。这些资源可包含代码文件、数据库内容或外部API数据,通过统一的URI标识实现标准化访问。例如,Git集成服务可暴露git://协议资源:

1 server.resource("git-files",

2  new ResourceTemplate("git://{repo}/{path}"),

3  async(uri,params)=>({

4    contents:[{

5      uri:uri.href,

6      text:await readGitFile(params.repo,params.path)

7    }]

8  })

9 );

MCP服务器还支持动态的上下文管理,支持Resource订阅机制,并在Resource内容变更时主动向客户端推送通知。

(2)工具封装与执行

首先通过工具(Tool)定义可执行操作,并将业务逻辑封装为LLM可调用的原子能力,然后执行工具并将工具调用的结果反馈给模型(通过客户端)。每个工具都需声明输入参数Schema,以符合统一的工具定义规范。一个简单的工具封装示例如下:

1 server.tool("calculate-bmi",

2  { weightKg:z.number(),heightM:z.number()},

3  async({ weightKg,heightM })=>({

4    content:[{

5      type:"text",

6      text:String(weightKg /(heightM ** 2))

7    }]

8  })

9 );

(3)提供提示词的最佳实践

MCP为服务器提供了一种向客户端公开提示词模板的标准化方法,允许服务器提供与LLM交互的结构化消息和提示词的最佳实践。这些提示词模板通常由开发MCP服务器的领域专家精心调优后随服务器一并提供,其目的是使用户在使用该服务器提供的工具解决问题时,能够获得更好的效果。比如,当引入一个操作MySQL数据库的MCP服务器时,该服务器的开发者在代码中默认集成了如下Prompt:

1 server.prompt("query-generator",{

2  question:z.string().describe("用自然语言描述数据需求"),

3  safety:z.boolean().default(true).describe("是否启用安全模式")

4 },({ question,safety })=>({

5  messages:[{

6    role:"user",

7    content:{

8      type:"text",

9      text:`将以下需求转换为安全SQL查询:\n"${question}"\n输出格式:{ "sql":"..." }`

10        +(safety ? "\n禁止使用DELETE/UPDATE/DROP语句":"")

11    }

12  }]

13 }));

这个名为query-generator的预定义Prompt模板,可以帮助用户快速地复用该模板,以通过描述生成安全的SQL查询语句。服务器的开发者可以用自己偏好的方式为用户提供这些提示词模板,比如在输入框中输入“/”以触发Prompt模板的选择,如图2-4所示。

图2-4 基于输入框中“/”触发的Prompt模板候选交互

事实上,MCP本身并没有任何关于用户交互的约定或限制,开发者可以完全自定义交互实现。

MCP服务器作为AI原生时代的标准化接口组件,其核心生态价值在于通过协议统一性解决了AI能力集成中的碎片化难题,构建起可自由组合的模块化服务生态。作为即插即用的“能力插座”,它允许开发者将任意领域的专业知识、工具和服务封装为标准服务模块—无论是代码分析工具包、医疗知识图谱查询还是智能家居控制,均可转化为遵循统一协议的MCP服务器。

这种设计使得跨行业解决方案的构建如同拼装乐高积木:金融风控系统可快速接入法律条文解读服务与财报分析工具链,智能家居中枢能无缝整合环境感知模块与用户习惯模型。开发者无须重复实现基础功能,只需要专注于垂直领域的核心价值创造。协议层的严格安全隔离设计(如上下文沙盒、权限分级控制)确保了模块间的安全互操作,既允许医疗诊断服务器与医学文献服务器协同推理,又能避免敏感数据跨域泄露。

社区驱动的开源MCP服务器模块仓库进一步增强了生态网络效应,开发者贡献的 服务器组件在经历场景验证后,可沉淀为通用能力资产,例如自然语言转SQL查询的服务器可能被电商数据分析、物流仓储优化、科研数据处理等不同场景复用,形成“一次开发,全域赋能”的正向循环。这种标准化、可组合、自进化的生态体系,大幅降低了AI技术落地的长尾成本,使得中小团队能以极低代价接入顶尖技术能力,传统行业专家无须精通算法即可将领域知识转化为智能服务,最终推动AI技术渗透到经济社会的末端场景,实现真正意义上的万物互联与群体智能涌现。

2.3.3 MCP组件的协同机制

在MCP中,对主机、客户端、服务器、资源、提示词、工具、根、采样都有明确的协同机制定义。搞清楚协同机制,有助于进一步了解MCP的底层运作机制,如图2-5所示。

图2-5 MCP中主机、客户端和服务器的协同机制示意图

MCP三大组件之间的协同机制可以分为5个阶段。

1.初始化阶段

初始化阶段首先由主机发起创建和初始化客户端的动作,并建立起与服务器的双向通信连接。然后由客户端发起初始化会话的协商请求,服务器会在响应中告知客户端其支持的功能范围、更新策略以及服务器当前的版本信息。示例代码如下所示:

1 // 第一次握手:客户端发起的初始化请求体

2 {

3  "jsonrpc":"2.0",

4  "id":1,

5  "method":"initialize",

6  "params":{

7    "protocolVersion":"2024-11-05",

8    "capabilities":{

9      "roots":{

10        "listChanged":true

11      },

12      "sampling":{}

13    },

14    "clientInfo":{

15      "name":"ExampleClient",

16      "version":"1.0.0"

17    }

18  }

19 }

20

21 // 第二次握手:服务器发送初始化响应的请求体

22 {

23  "jsonrpc":"2.0",

24  "id":1,

25  "result":{

26    "protocolVersion":"2024-11-05",

27    "capabilities":{

28      "logging":{},

29      "prompts":{

30        "listChanged":true

31      },

32      "resources":{

33        "subscribe":true,

34        "listChanged":true

35      },

36      "tools":{

37        "listChanged":true

38      }

39    },

40    "serverInfo":{

41      "name":"ExampleServer",

42      "version":"1.0.0"

43    }

44  }

45 }

46 // 第三次握手:客户端发送已初始化成功的通知信息

47 {

48  "jsonrpc":"2.0",

49  "method":"notifications/initialized"

50 }

值得注意的是,主机一般会在这个阶段完成后,直接向服务器发起查询请求(例如tools/list),以便第一时间得到服务器支持的工具清单,并对工具清单进行缓存,以降低后续的通信成本。

1 // 客户端的工具清单查询请求体

2 {

3  "jsonrpc":"2.0",

4  "id":1,

5  "method":"tools/list",

6  "params":{

7    "cursor":"optional-cursor-value"

8  }

9 }

10 // 服务器返回的可用工具清单响应体

11 {

12  "jsonrpc":"2.0",

13  "id":1,

14  "result":{

15    "tools":[

16      {

17        "name":"get_weather",

18        "description":"根据目标地址获取天气信息",

19        "inputSchema":{

20          "type":"object",

21          "properties":{

22            "location":{

23              "type":"string",

24              "description":"城市名或者邮编"

25            }

26          },

27          "required":["location"]

28        }

29      }

30    ],

31    "nextCursor":"next-page-cursor"

32  }

33 }

2.客户端请求阶段

该阶段的请求始于用户的交互(可以是一次提示词模板查询,或者是资源获取),或者是模型在回答用户提问时输出的一次工具(Tool)调用。此时会产生一轮客户端和服务器之间的交互,过程通常由主机发起,通过客户端在初始化阶段创建的通信连接发送一次请求(Resource/Prompt/Tool)。服务器收到请求后,经过相应的校验并执行对应的动作,再向客户端发送响应。客户端收到响应后交由主机进行下一步动作(通常是更新主机UI界面,或者组装新的推理消息再交给LLM进行推理)。以执行一次模型驱动的工具调用为例,其协同过程大致如下:

1 // 客户端接收到主机的工具调用命令后,向服务器发起一次查询纽约天气的请求

2 {

3  "jsonrpc":"2.0",

4  "id":2,

5  "method":"tools/call",

6  "params":{

7    "name":"get_weather",

8    "arguments":{

9      "location":"纽约"

10    }

11  }

12 }

13

14 // 服务器收到请求,在验证了arguments参数的有效性之后,会调用其封装的真实业务逻辑代码,查询得到纽约的天气,并返回给客户端

15 {

16  "jsonrpc":"2.0",

17  "id":2,

18  "result":{

19    "content":[

20      {

21        "type":"text",

22        "text":"纽约现在的天气:温度72°F,部分地区多云"

23      }

24    ],

25    "isError":false

26  }

27 }

28

29 // 客户端收到响应后,会把工具执行结果返回给主机,主机会将工具的调用结果拼接到给模型继续推理的消息队列中:

30 {

31    "messages":[{

32      "role":"user",

33      "content":"查一下纽约的天气"

34    },{

35      "role":"assistant",

36      "content":"",

37      "toolCalls":[{

38        "name":"get_weather",

39        "arguments":{

40          "location":"纽约"

41        }

42      }]

43    },{

44      "role":"tool",

45      "content":"调用get_weather工具的查询结果为:纽约现在的天气:温度72°F,部分地区多云"

46    }]

47 }

3.服务器采样阶段

服务器可以根据自己的设计决定是否要主动向客户端发起采样申请,并以数字公民的身份参与到用户的问题中来。这个申请既可以是建言献策、提供推理建议,也可以是对用户任务的完成结果进行采样监控,以获得知情权。客户端收到采样请求后,会申请用户的授权,以便确认该行为是安全可控的。用户授权通过后,客户端可以继续通过主机向LLM发起推理请求。推理结束后,主机同样会发起用户的授权,只有用户确认结果可以发送给服务器之后,它才会通过客户端把结果发送给服务器。目前支持这个阶段的应用比较少,读者只需要了解其定位和原理即可,如需深入研究可以阅读官方协议文档。

4.消息通知阶段

MCP采用了基于JSON-RPC的全双工通信协议,允许客户端和服务器在建立连接后,根据需要随时发起双向通知,以便同步双方的变更。这些变更可能是:

用户授权的本地Root目录发生变更,客户端需要主动通知服务器。

服务器提供的Resource发生变更,需要告知客户端。

服务器提供的Prompt模板发生变更,需要告知客户端。

譬如,当服务器动态新增了工具时,协议规定,服务器需要主动向客户端发送变更消息:

1 // 服务器发送的变更消息体

2 {"jsonrpc":"2.0","method":"notifications/tools/list_changed"}

客户端收到变更通知后,重新发起工具清单查询请求,以便更新服务器支持的最新工具清单:

1 // 客户端再次发起工具清单查询请求

2 {

3  "jsonrpc":"2.0",

4  "id":1,

5  "method":"tools/list",

6  "params":{

7    "cursor":"optional-cursor-value"

8  }

9 }

10

11 // 服务器返回最新的工具清单,可以看到,和之前相比,get_weather工具增加了一个查询日期的字段

12 {

13  "jsonrpc":"2.0",

14  "id":1,

15  "result":{

16    "tools":[

17      {

18        "name":"get_weather",

19        "description":"根据目标地址获取天气信息",

20        "inputSchema":{

21          "type":"object",

22          "properties":{

23            "location":{

24              "type":"string",

25              "description":"城市或者邮编"

26            },

27            "date":{

28              "type":"string",

29              "description":"查询的日期,格式要求:YYYY-MM-DD"

30            }

31          },

32          "required":["location"]

33        }

34      }

35    ],

36    "nextCursor":"next-page-cursor"

37  }

38 }

5.结束阶段

MCP规定,结束动作可以由客户端和服务器双向发起,并终止在初始化阶段建立起的连接,实现优雅的关闭动作。MCP支持两种标准传输机制:STDIO和HTTP+SSE,分别对应托管在用户本机和远程服务器上的MCP服务器。结束阶段针对这两种连接机制有不同的要求:

STDIO:首先关闭子进程的输入流,然后等待服务器退出。如果服务器在合理时间内没有退出,则发送SIGTERM信号;如果服务器在发送SIGTERM信号之后的合理时间内仍未退出,则发送SIGKILL信号。服务器可以通过关闭其对客户端的输出流以启动关闭动作。

HTTP:只需要关闭连接即可。

2.3.4 MCP的通信机制

我们每个人的数字世界都可以划分成两大领域:一边是你的个人领域,包括本地文件、应用程序和设备;另一边是广阔的社会领域,包含各种远程服务、公共数据和组织资源。MCP作为连接大语言模型应用与外部上下文及工具的开放标准,必须同时兼顾这两个领域。基于此,MCP精心设计了一套全双工的通信机制,使用两种不同的传输标准,搭建了一座连接这两个领域的桥梁,让LLM能够同时与它们优雅地交互。

1.全双工对话机制

MCP选择了基于JSON-RPC2.0的全双工通信架构,通信双方分别是MCP客户端和MCP服务器,如图2-6所示。所谓全双工通信,就是客户端和服务器都可以随时向对方发送消息。全双工通信的价值在于,它允许模型和环境之间进行自然的、非阻塞的交互。就像我们在对话中可以随时插话或补充信息,MCP中的参与者也可以在任何时候发起请求或通知。

图2-6 MCP基于JSON-RPC 2.0的全双工通信架构示意图

MCP定义了两种标准传输机制—STDIO和HTTP+SSE(新版本升级为Streamable HTTP)来实现个人领域与社会领域的MCP服务器的连接。

2.STDIO传输

STDIO(标准输入输出)传输机制是一种基于进程间通信的轻量化数据传输方案,特别适用于本地计算机环境中的进程协作。其核心原理是通过操作系统的标准输入流(stdin)和标准输出流(stdout)在进程间建立双向通信通道。MCP使用STDIO传输的基本模式是:

客户端以子进程方式启动MCP服务器。

服务器从标准输入(stdin)中读取消息,向标准输出(stdout)中写入消息。

消息以换行符分隔,不能包含嵌入换行符。

服务器可以使用标准错误(stderr)输出日志信息。

想象你正在使用一个像Cursor一样能安装在本地计算机上的AI应用。在这种情况下,Cursor可以直接通过标准输入输出流与运行在本地计算机上的MCP服务器进行通信,如图2-7所示。

STDIO传输的美妙之处在于它的即时性、直接性和简单性。没有网络延迟,没有连接问题,客户端和服务器可以像两个紧密协作的同事一样高效工作。对于那些注重隐私的本地任务,比如处理敏感文档或分析个人数据,这种本地通信方式提供了额外的安全保障。STDIO相比其他网络传输机制存在以下四大优势:

低延迟与高效性:STDIO绕过了网络协议栈,数据直接在进程内存间传输,避免了网络通信的序列化/反序列化开销,响应速度可达微秒级。例如,在本地文件处理场景中,客户端与服务器通过管道传递数据,无须经过网络接口卡,吞吐量显著高于远程通信。

简化部署与安全性:本地环境下无须配置IP地址、端口或SSL证书,降低了部署复杂度。同时,由于通信仅存在于同一台机器的不同进程间,因此天然规避了网络监听、中间人攻击等安全风险。

资源隔离与稳定性:客户端与服务器以父子进程关系运行,操作系统自动管理资源分配和生命周期。例如,当服务器崩溃时,客户端可通过进程信号快速感知并重启,避免因网络超时机制导致的长时间阻塞。

开发调试友好性:开发者可直接在终端观察原始数据流,通过重定向(如 >或<)将输入输出保存为日志文件,便于问题复现和分析。

图2-7 STDIO传输机制示意图

以下是STDIO通信的简化代码示例:

1 // 客户端代码示例

2 import { spawn } from "child_process";

3

4 // 启动MCP服务器作为子进程

5 const serverProcess = spawn("mcp-server");

6

7 // 发送请求

8 const request = {

9  jsonrpc:"2.0",

10  method:"initialize",

11  params:{

12    capabilities:{

13      /* 客户端能力 */

14    },

15  },

16  id:1,

17 };

18

19 // 向stdin写入请求

20 serverProcess.stdin.write(JSON.stringify(request)+"\n");

21

22 // 从stdout中读取响应

23 serverProcess.stdout.on("data",(data)=> {

24  const responses = data

25    .toString()

26    .split("\n")

27    .filter((line)=> line.trim());

28  for(const responseText of responses){

29    try {

30      const response = JSON.parse(responseText);

31      // 处理响应……

32    } catch(e){

33      console.error("解析响应失败:",e);

34    }

35  }

36 });

37

38 // 处理日志

39 serverProcess.stderr.on("data",(data)=> {

40  console.log("服务器日志:",data.toString());

41 });

3.HTTP+SSE传输

随着视野从个人计算机扩展至整个互联网,MCP需要一种能够跨越网络边界、连接远程服务的通信机制。在MCP的第一版规范中,使用了HTTP+SSE的传输协议规范来实现全双工通信,它是MCP为连接社会领域而设计的关键通信机制。要系统理解该传输机制,首先需要了解一下什么是SSE。

SSE(Server-Sent Events)是一种基于HTTP的服务器推送技术,其底层机制围绕单向通信、轻量化设计和高效连接管理展开。其基本原理是通过使用HTTP/1.1+支持的持久连接,实现服务器持续地向客户端单向发送数据。

与WebSocket需要独立协议栈不同,SSE复用HTTP的协议栈,请求头仅需要标准字段,如Content-Type:text/event-stream和Connection:keep-alive。这种设计使得SSE的建连成本比WebSocket低很多,因此被广泛应用于需要建立持久连接且拥有高并发的场景中。

MCP通过HTTP+SSE的传输方案实现了客户端和服务器之间的“逻辑全双工”通信,该方案融合了SSE的实时推送特性与HTTP POST的请求响应机制,构建出兼具高实时性与可靠性的通信框架。MCP的通信架构由两条独立通道构成:SSE长连接通道(服务器→客户端)与HTTP POST短连接通道(客户端→服务器)。这种分离式设计既遵循HTTP规范,又通过资源隔离降低了传统长连接双向通信的复杂度。

(1)SSE长连接通道的建立

客户端通过HTTP GET请求访问/sse端点,服务器返回text/event-stream响应头,建立持久化连接。该连接在初始化时会推送endpoint事件,其中携带包含会话ID(如/messages?session_id=xxx)的专用URI。此URI将成为后续双向通信的枢纽,其会话ID机制既实现了多客户端隔离,也为断线重连提供了状态追踪锚点。

(2)HTTP POST请求通道

客户端获取到专用URI后,所有请求均通过HTTP POST发送至服务器。这种设计将请求处理与事件推送解耦:POST请求处理常规业务逻辑,SSE通道专注于实时消息推送。服务器对POST请求返回标准HTTP状态码(如202 Accepted),实际响应数据则通过SSE通道异步返回。虽然SSE本身是单向协议,但MCP通过双通道协同工作实现了逻辑上的双向通信。

1)请求-响应映射。每个POST请求携带唯一ID(JSON-RPC协议中的id字段),服务器处理完成后通过SSE通道推送包含相同ID的响应消息。客户端通过ID匹配机制将异步响应与原始请求相关联,为用户提供类似同步调用的编程体验。

2)双通道响应机制。在复杂交互场景中,服务器可以同时返回两种响应:

即时响应:POST请求返回HTTP 202确认接收。

流式响应:通过SSE分多次推送处理进度或中间结果。

这种机制特别适用于大模型推理等长时任务,既能快速确认请求已被接收,又能实时反馈生成过程。

为了实现全双工通信这个设计目标,MCP选择了HTTP+SSE的传输架构,这种设计带来了几个非常关键的优势:

兼容标准HTTP基础设施,不需要特殊网关或代理。

浏览器原生支持EventSource API,降低客户端实现成本。

单向流设计减少服务器资源消耗,单节点可支撑10万级并发连接。

这种创新性的双通道设计,在HTTP的协议栈上构建出高效的准实时通信系统,为后续升级到Streamable HTTP方案奠定了技术基础。其核心价值在于通过标准化协议降低系统耦合度,使AI应用能够以统一方式接入异构数据源,这一设计思想深刻影响了后续AI通信协议的发展方向。

4.Streamable HTTP传输

Streamable HTTP是MCP在2025-03-26版本中引入的改进传输机制,最终的目标是取代HTTP+SSE传输机制,成为MCP面向互联网远程服务连接的唯一传输机制。在MCP的初版设计中,HTTP+SSE传输机制通过两个独立通道实现通信:客户端通过HTTP POST发送请求,服务器通过SSE长连接推送事件。这种架构在早期验证阶段表现出简单易用的特性,但随着生产环境复杂度的提升,其缺陷逐渐暴露:

连接脆弱性:SSE长连接对网络抖动极其敏感,断开后无法自动恢复会话状态。例如,在无服务器(Serverless)环境中,Lambda函数的执行时间限制会导致SSE连接频繁中断,用户需要重新初始化整个会话。

服务器资源瓶颈:每个SSE连接都需要占用独立线程/进程资源,当并发用户的数量超过1万时,服务器内存消耗将激增300%。这在电商大促等高并发场景下极易引发雪崩效应。

协议单向性缺陷:SSE仅支持服务器到客户端的单向通信,客户端需通过额外的HTTP通道发送数据,导致双通道同步难题。例如,AI推理进度推送与用户终止指令可能产生时序冲突。

开源社区为此展开了激烈的讨论,在MCP官方的GitHub仓库的一个名为Pull Request的帖子(https://github.com/modelcontextprotocol/specification/pull/206)中,大家的争论集中在以下几个维度:

协议选择之争:激进派主张采用WebSocket实现真正的双向通信,但实测显示WebSocket的握手耗时比SSE多200ms,且无法在浏览器端附加Authorization头。保守派则认为应保持HTTP协议栈的纯粹性。

状态管理范式:关于会话ID的生成机制,社区产生较大分歧。部分开发者主张客户端生成UUID(类似JWT),但安全专家指出这会导致会话劫持风险;最终方案确立为在服务端加密生成会话ID并进行签名验证。

无状态化路径:部分人认为强制无状态设计会丧失MCP的上下文优势,但AWS Lambda团队提供的测试数据显示,通过将会话状态存储在Redis集群中,无状态服务器的请求处理吞吐量提升了4.2倍。

经过长时间的讨论和A/B测试,Anthropic技术委员会最终选择了Streamable HTTP方案,其核心设计考量包括:

渐进式升级策略:保留SSE作为流式载体,但解除端点绑定。旧版 /sse端点迁移为 /message,通过Content-Type:text/event-stream头动态升级,实现向后兼容。

资源解耦设计:引入Mcp-Session-Id头部,实现会话状态与连接的解耦。服务器可选择将会话状态存储在外部数据库中,从而使单个请求的平均处理时间从120ms缩短至45ms。

混合传输模式:支持3种响应类型—即时JSON响应、分块SSE流、持久SSE长连接。例如,在AI代码补全场景中,首条补全建议通过JSON返回,后续优化建议通过SSE流推送。

Streamable HTTP传输机制与HTTP+SSE传输机制的具体差异见表2-2。

表2-2 Streamable HTTP传输机制与HTTP+SSE传输机制的具体差异

Streamable HTTP的成功印证了“简单即复杂”的技术哲学。其设计启示在于:优秀协议不是创造新标准,而是在既有生态中寻找最优解。据Anthropic的技术路线图披露,2026年团队将实现对HTTP/3 QUIC协议的支持,届时流式传输延迟有望被进一步压缩至30ms以内。

随着AI技术的发展和应用场景的扩展,MCP通信机制也将继续演进。未来,我们可能会看到更加智能的会话管理、更精细的权限控制,以及更流畅的多模态交互支持。但无论如何变化,标准化、安全性、灵活性和高效性这4个核心原则将继续指导MCP的发展方向。对于开发者而言,理解MCP的通信机制不仅有助于正确实现协议,还能启发更好的AI应用架构设计。就像理解人类对话的规则有助于我们成为更好的沟通者一样,理解MCP的通信机制有助于我们创造更自然、更有用的AI应用。在AI Agent时代,MCP正逐步成为连接大语言模型与现实世界的标准桥梁。通过这座桥梁,个人领域与社会领域得以连接,本地资源与远程服务得以整合,AI助手的能力得以充分发挥。

2.3.5 MCP的安全机制

现代人工智能系统面临着复杂的安全挑战:既要实现跨应用的能力整合,又要防范未授权的访问;既要保持交互的灵活性,又要维护明确的控制边界。MCP通过创新的安全架构设计,在人类用户、AI Agent和外部服务之间构建起可信的协作网络。这个系统的最精妙之处在于,它让安全机制像呼吸般自然地融入日常交互中,而非生硬的技术枷锁。

1.安全架构

MCP的安全架构设计建立在3个核心原则上,如同三足鼎立般支撑整个体系,如图2-8所示。

图2-8 MCP安全架构的核心原则

这种设计使得安全控制既不会阻碍正常业务流程,又能精准防范越权风险。例如,当代码审查服务器需要访问项目文件时,它不会直接获取整个代码库,而是通过声明式接口申请特定目录的读取权限。

2.会话生命周期的安全机制

每个MCP会话都像精心编排的安全“芭蕾”,在初始化阶段就确立了明确的权限边界。

1 // 会话初始化示例

2 interface CapabilityDeclaration {

3  prompts?:{ listChanged:boolean };

4  resources?:{ subscribe:boolean };

5  sampling?:{};

6 }

7

8 function initializeSession(serverCapabilities:CapabilityDeclaration){

9  const clientCapabilities = {

10    resources:{ listChanged:true },

11    sampling:{}

12  };

13

14  // 动态协商公共能力集

15  return negotiateCapabilities(serverCapabilities,clientCapabilities);

16 }

这个过程类似餐厅点餐时的需求沟通—服务员(客户端)告知厨房(服务器)所需的菜品(服务),厨房回应实际能完成的菜品,最终达成双方都确认的菜单。

3.数据流动的安全机制

资源访问控制是MCP最值得称道的设计之一,它通过多层级过滤机制确保数据安全,如图2-9所示。

图2-9 MCP中数据流动的安全机制

上述流程中蕴含3个关键控制点:用户显式设置根目录、运行时动态权限审批、服务端最小化数据请求。这就像银行金库的三重门禁,每道门都只对必要人员临时开放。

MCP安全机制的设计灵感来源于其设计者对现实世界协作模式的深刻理解。传统安全模型往往建立在对参与方的静态信任基础上,而MCP采用了动态的、上下文感知的信任建立机制。

渐进式暴露:如同陌生人之间间逐步建立信任一样,服务能力按需逐步开放。

环境感知:根据当前对话的上下文动态调整权限范围。

沙盒化运行:每个服务实例都在隔离环境中运行,如同科研实验室的独立操作间。

这种设计使系统既能保持扩展性(支持不断接入新服务),又能维持安全基线。就像现代城市既需要四通八达的交通网络,又需要消防隔离带防止火灾蔓延。

在数字技术日新月异的今天,MCP展现了一种全新的安全范式—它不追求绝对的控制,而是通过精巧的机制设计,让安全成为能力进化的助推器而非绊脚石。这或许正是未来智能系统安全架构的演进方向:在开放与管控之间找到动态平衡,让技术创新与安全保障和谐共生。 s1hjo9Lo3SWDC867YxKckLQtJHIcTGtIqGA3ExKLcjRzuLCOiTFursZ01wimBrUE

点击中间区域
呼出菜单
上一章
目录
下一章
×