在人工智能,特别是大型语言模型(LLM)迅速发展的今天,模型的能力不再仅仅局限于其训练数据所构建的知识边界。为了让AI真正具备感知、理解并影响物理世界和数字世界的能力,它们必须能够与外部数据源、工具和服务进行高效、安全且标准化的交互。模型上下文协议(Model Context Protocol,简称MCP)正是在这一背景下应运而生,旨在为LLM与外部资源之间搭建一座坚实且通用的桥梁。
本章作为MCP协议的基石,将全面剖析其核心概念和底层架构。理解这些基础要素,对于开发者、系统架构师以及任何希望利用MCP构建下一代智能应用的专业人士至关重要。我们将循序渐进,从构成协议的各个实体,到它们之间最基本的通信单元,最终呈现整个系统的协作模型。
MCP协议的设计遵循一种清晰的客户端-服务器(Client-Server)架构模式,这与许多成功的通信协议(如HTTP、LSP等)有着异曲同工之妙。这种架构模式有助于实现关注点分离、提高模块化程度,并为协议的扩展性和安全性奠定基础。在MCP的语境下,这一架构主要由三个核心组件构成:主机(Host)、客户端(Client)和服务器(Server)。
主机是MCP架构中的“发起者”或“使用者”。它代表了需要与外部世界交互的AI应用程序或环境。典型的MCP主机包括但不限于:
大型语言模型应用: 例如,一个基于LLM的聊天机器人、智能助手或内容生成平台。
集成开发环境(IDE): 支持AI辅助编程功能的IDE,可能需要通过MCP调用代码分析工具、版本控制系统或外部API。
数据分析平台: 需要LLM帮助分析、查询或处理来自各种数据库和文件的数据。
企业内部应用: 将LLM能力集成到CRM、ERP或其他业务系统中,以便进行自然语言查询或自动化任务。
主机的核心职责在于:
初始化和管理客户端: 主机负责创建并维护一个或多个MCP客户端实例。
处理用户授权: 当需要访问受限资源时,主机可能需要介入处理用户的身份验证和授权流程。
管理上下文聚合: 主机负责将从不同MCP服务器获取的上下文信息、工具调用结果等整合成LLM可以理解和利用的形式。
协调AI与外部交互: 根据用户的意图或内部逻辑,主机决定何时通过哪个客户端向哪个服务器发起请求,以及如何处理返回的结果。
简单来说,主机是AI能力与用户界面或更广泛应用逻辑的结合点,它通过客户端与MCP服务器进行对话。
客户端是MCP架构中位于主机内部的“通信代理”。它扮演着主机与特定MCP服务器之间的桥梁角色。一个主机应用程序可以包含一个或多个客户端实例,通常每个客户端与一个特定的MCP服务器保持着一对一(1:1)的连接。
客户端的关键功能包括:
连接管理: 负责与MCP服务器建立、维护和终止连接。
消息路由: 将来自主机的请求按照协议规范封装,并通过底层的传输机制发送给服务器;同时接收服务器的响应和通知,并将其转发给主机。
能力管理与协议协商: 在连接建立初期,客户端会与服务器协商双方支持的协议版本和能力集,确保兼容性。
订阅管理: 如果协议支持,客户端可以管理对服务器端特定事件或资源更新的订阅。
客户端的存在使得主机无需直接处理复杂的底层通信细节和协议规范。主机只需要通过客户端提供的抽象接口,发送结构化的请求或处理接收到的数据。这极大地简化了主机应用程序的开发复杂度。
服务器是MCP架构中的“服务提供者”。它封装了特定的外部资源、工具或服务,并通过标准化的MCP接口将其暴露给客户端(进而暴露给主机和LLM)。MCP服务器的设计通常是轻量级的,专注于提供特定的功能集。
MCP服务器可以封装和提供的能力类型广泛,主要包括:
资源(Resources): 提供对静态或半静态数据的访问,如数据库记录、文件内容、文档库、知识图谱片段等。这些资源通常通过URI(Uniform Resource Identifier)来标识。
工具(Tools): 提供可执行的操作或函数,允许LLM触发外部行为,如调用第三方API(天气查询、股票信息、邮件发送)、执行代码片段、进行复杂计算、控制物理设备(物联网设备)等。工具通常通过方法名称来标识。
提示(Prompts): 提供预定义的、结构化的输入模板或交互流程,帮助LLM更有效地与特定服务或数据源交互,或引导LLM生成特定格式的输出。
服务器的核心职责在于:
监听并处理客户端请求: 接收来自客户端的请求(如读取资源、调用工具),执行相应的逻辑。
返回响应或发送通知: 根据请求执行的结果,向客户端返回结构化的响应(成功结果或错误信息),或者在特定事件发生时发送通知。
能力声明: 在连接初始化阶段,向客户端声明自己支持的资源、工具和提示列表及其详细描述。
安全与访问控制: 负责验证客户端的请求是否合法,执行权限检查,确保只有授权的操作才能被执行,并防止敏感信息泄露。
服务器是连接AI与外部世界的关键枢纽。通过实现不同的MCP服务器,开发者可以将各种异构的外部系统(数据库、API、文件系统、IoT平台等)以统一的方式接入AI生态。
下图简要描绘了这三个核心组件及其关系:
图 2.1.1 MCP核心架构组件关系示意图
这种分层和模块化的设计,使得MCP协议能够有效地隔离AI模型的推理逻辑与外部系统的复杂性,提高了整个系统的可维护性和可扩展性。
在核心组件之下,MCP架构还可以进一步细分为协议层和传输层:
协议层(Protocol Layer): 这一层定义了消息的格式、请求与响应的关联方式(通过ID)、以及更高级的通信模式(如能力协商、错误报告等)。MCP的协议层基于成熟的JSON-RPC 2.0标准,这为消息的结构化和双向通信提供了坚实的基础。
传输层(Transport Layer): 这一层负责处理客户端和服务器之间实际的字节流或数据包传输。MCP协议不绑定于特定的传输协议,而是支持多种选项以适应不同的部署环境:
Stdio传输: 使用标准输入/输出流进行通信。这通常用于在同一台机器上运行的进程间通信,例如一个桌面应用(主机/客户端)与一个本地运行的MCP服务器。Stdio传输具有低延迟和高安全性的优点,因为它不需要网络连接。
HTTP与SSE传输: 利用HTTP POST从客户端向服务器发送请求,并利用Server-Sent Events(SSE)实现服务器到客户端的实时消息推送(如通知或流式响应)。这种方式适用于通过网络进行远程通信的场景,SSE特别适合需要服务器主动推送数据的应用。
WebSocket传输: 另一种基于TCP的全双工通信协议,适用于需要客户端和服务器之间频繁、低延迟双向通信的场景。
所有这些传输机制都承载着符合协议层规范的JSON-RPC消息。这种分层设计使得MCP可以在不同的通信环境中灵活部署,同时保持协议逻辑的一致性。
MCP协议的通信基石是其定义的基本消息类型,或者称为交互原语。这些原语构成了客户端和服务器之间所有通信的“词汇表”。如前所述,MCP基于JSON-RPC 2.0,因此其核心交互原语也直接继承了JSON-RPC的规范:请求(Request)、响应(Response)和通知(Notification)。
理解这些原语是理解MCP通信流程的关键。它们都是结构化的JSON对象,通过特定的字段来区分类型、标识操作和携带数据。
请求消息用于调用对方(服务器或客户端)暴露的一个方法或执行一个特定的操作。请求通常由需要服务的一方发送给提供服务的一方。
一个典型的MCP请求消息包含以下核心字段:
jsonrpc: 协议版本,固定为字符串 "2.0"。
id: 请求的唯一标识符。 可以是字符串或数字。接收方在发送对应的响应时,必须使用相同的id来关联请求和响应。这是实现请求-响应模式的关键。对于不需要响应的请求(即通知),此字段不存在。
method: 一个字符串,指定要调用的方法名称。例如,"list_resources"、"execute_query"、"control_device"等。
params: 一个可选的结构化数据(通常是JSON对象或数组),包含调用方法所需的参数。例如,对于"execute_query"方法,params可能包含一个字段如{"query": "SELECT * FROM users WHERE id = 1;"}。
示例(概念性结构,非严格JSON):
{ "jsonrpc": "2.0", "id": "request_abc_123", "method": "get_resource_content", "params": { "uri": "document://report_2023.pdf", "format": "text" } }
请求是MCP通信中最常见的消息类型之一,它驱动了AI模型获取信息或执行外部操作的过程。
响应消息是对接收到的请求的答复。发送响应的一方(通常是服务器,但也可能是处理特定请求的客户端)必须使用与原请求相同的id来标识它是对哪个请求的回复。
一个响应消息可以表示请求成功或失败。它包含以下核心字段:
jsonrpc: 协议版本,固定为字符串 "2.0"。
id: 必须与对应的请求消息中的id完全一致。
result: 如果请求成功执行,此字段存在,包含方法执行的结果。结果可以是任意结构化的JSON数据(对象、数组、基本类型)。
error: 如果请求执行失败,此字段存在,包含错误信息。error字段本身是一个对象,通常包含:
code: 一个数字,表示错误类型(遵循JSON-RPC标准错误码或自定义错误码)。
message: 一个字符串,提供错误的简短描述。
data: 一个可选的结构化数据,提供关于错误的更多详细信息。
一个成功的响应示例:
{ "jsonrpc": "2.0", "id": "request_abc_123", "result": { "content": "这是报告的文本内容...", "pages": 15 } }
一个失败的响应示例:
{ "jsonrpc": "2.0", "id": "request_abc_123", "error": { "code": -32601, "message": "Method not found", "data": {"method_name": "get_resource_content"} } }
响应是请求的必然结果(除非请求是通知),它将外部操作的执行状态和结果反馈给发起请求的一方,使得AI模型能够基于这些信息进行后续的推理或行动。
通知消息是一种单向通信原语。与请求不同,通知不期望接收方返回响应。它通常用于发送方希望向接收方推送信息、报告状态变化或触发某个不要求立即确认的操作。
一个通知消息包含以下核心字段:
jsonrpc: 协议版本,固定为字符串 "2.0"。
method: 一个字符串,指定通知的类型或目的。例如,"resource_changed"、"tool_execution_progress"、"server_status_update"等。
params: 一个可选的结构化数据,包含通知携带的信息。
示例:
{ "jsonrpc": "2.0", "method": "tool_execution_progress", "params": { "tool_id": "query_tool_456", "progress": 0.75, "message": "正在处理数据..." } }
注意,通知消息没有id字段,这是它与请求消息最显著的区别。通知常用于服务器向客户端推送实时更新,或者客户端向服务器报告某些状态,而无需等待确认,从而提高通信效率。
下图展示了这三种消息类型的基本结构和流向:
图 2.2.1 MCP核心交互原语示意图
这些简单的原语通过JSON-RPC 2.0的标准规范组合,构成了MCP协议灵活而强大的通信基础。它们足以支持从简单的请求-响应式数据查询,到复杂的工具调用和实时状态更新等多种交互模式。
在了解了核心组件和交互原语之后,我们可以进一步探讨MCP协议中客户端和服务器是如何协作完成任务的。这种协作遵循一个明确的生命周期和多种交互模式。
一个典型的MCP客户端与服务器连接会经历以下几个阶段:
初始化(Initialization): 这是连接建立的第一个阶段。
客户端向服务器发送一个特殊的initialize请求。这个请求包含了客户端支持的协议版本、客户端的能力(例如,它能处理哪些通知)以及一些初始化选项。
服务器接收到initialize请求后,进行处理并返回一个initialize响应。这个响应包含服务器支持的协议版本、服务器的能力(即它提供的资源、工具和提示列表)以及其他服务器端信息。
如果初始化成功,客户端会向服务器发送一个initialized通知,作为最终的确认。
完成初始化握手后,连接进入正常的消息交换阶段。
消息交换(Message Exchange): 在连接处于活动状态时,客户端和服务器可以自由地交换请求、响应和通知消息。
请求-响应模式: 这是最常见的模式。客户端(或服务器,在某些回调场景下)发送一个请求,包含一个唯一的id。对方处理请求并返回一个带有相同id的响应,其中包含结果或错误。这种模式用于获取数据、调用工具等需要明确结果的操作。
通知模式: 客户端或服务器可以随时发送通知消息,而无需等待对方的回复。这用于事件通知、进度更新等单向通信。
终止(Termination): 连接可以由任何一方主动或被动终止。
干净关闭: 任何一方都可以发送一个shutdown请求(可选)或直接关闭底层传输连接(例如,关闭Stdio流或WebSocket连接)。协议规范通常会定义一个优雅关闭的流程,确保正在处理的消息能够完成。
异常终止: 底层传输错误、协议错误或一方进程崩溃都可能导致连接异常中断。
理解连接生命周期对于正确实现MCP客户端和服务器至关重要,特别是在错误处理和资源清理方面。
基于核心组件、交互原语和连接生命周期,我们可以描绘一个典型的AI驱动的MCP交互流程。假设一个用户在支持MCP的AI应用(主机)中提出了一个需要调用外部工具的问题,例如“请帮我查询明天纽约的天气”。
图 2.3.1 典型AI驱动的MCP交互流程
在这个流程中:
用户意图: 用户通过自然语言向AI应用(主机)表达需求。
主机解析: 主机中的LLM或相关逻辑解析用户的意图,识别出需要执行的特定任务(查询天气)以及所需的参数(纽约,明天)。
工具选择与请求封装: 主机根据解析结果,确定需要调用哪个MCP服务器提供的哪个工具(例如,一个名为get_weather的工具)。主机通过其内置的客户端,按照MCP的请求原语规范,构建一个请求消息,包括工具方法的名称、参数以及一个唯一的请求ID。
客户端转发: 客户端接收到主机构建的请求,通过与天气服务器建立的连接,将请求发送出去。
服务器执行: 天气服务器接收到请求,解析方法名和参数。它调用实际的外部天气API获取数据。
服务器响应: 天气服务器处理完外部API的响应后,将结果封装到MCP的响应原语中,使用与请求相同的ID,并将响应发送回客户端。如果发生错误(例如,找不到城市),则在响应中包含错误信息。
客户端传递: 客户端接收到服务器的响应,将其传递回主机。
主机处理与回复: 主机接收到天气数据,将其反馈给LLM进行处理(例如,整合到对话上下文中)。LLM生成最终的自然语言回复呈现给用户。
在这个过程中,服务器还可能在执行长时间任务时,通过发送通知来报告进度,客户端接收后可以传递给主机,主机可以选择向用户展示这些进度信息。
这种交互模型清晰地划分了责任:
主机: 负责理解用户、协调高级任务、管理LLM上下文以及处理最终结果的呈现。
客户端: 负责可靠地在主机和服务器之间传递消息,处理连接和协议细节。
服务器: 负责封装外部系统的复杂性,以标准化的方式提供资源和工具,并执行请求的实际操作。
这种模型使得AI模型可以专注于其核心的推理和生成能力,而无需关心如何直接与成千上万种不同的API、数据库或设备进行通信。MCP服务器承担了“翻译”和“适配”的角色,将外部世界的复杂性转化为AI模型可以理解和调用的标准化接口。
在初始化阶段,客户端和服务器之间的能力发现与协商是交互模型的关键一环。服务器会声明它提供的所有资源、工具和提示的列表,包括它们的名称、描述、预期的参数、返回值的结构等元数据。客户端(或主机)可以利用这些信息来:
动态发现可用功能: AI模型或主机应用可以了解到当前连接的服务器提供了哪些能力,而无需硬编码这些信息。这支持了插件化和动态扩展。
生成合适的请求: 根据服务器声明的工具描述,主机可以更准确地构建符合要求的请求参数。这与LLM的Function Calling机制高度契合,LLM可以根据服务器提供的工具描述来决定调用哪个工具以及使用哪些参数。
用户界面呈现: 主机应用可以根据服务器提供的资源和工具信息,为用户生成更友好的交互界面或提示。
这种动态的能力发现机制,是MCP协议实现“即插即用”式集成的基础。
在客户端-服务器交互模型中,安全性和访问控制至关重要。MCP协议的设计考虑了以下安全因素:
服务器控制暴露的能力: 服务器只暴露经过精心设计和受控的接口。敏感操作可以被限制或要求额外的授权。
本地运行的优势: 许多MCP服务器可以在本地机器上运行,直接访问本地文件、数据库等,避免敏感数据通过网络传输,提高了安全性。
细粒度权限控制: MCP服务器可以实现细粒度的权限检查,例如,限制特定客户端只能访问某些资源或调用某些工具。
用户确认: 对于可能产生实际影响的工具调用(如发送邮件、进行支付),主机应用通常会在通过MCP发送请求前,要求用户进行二次确认。
传输层安全: 对于远程连接(HTTP/SSE/WebSocket),应使用TLS/SSL等加密手段保护通信内容。
通过将外部资源的访问权封装在受控的MCP服务器中,并将通信标准化,MCP协议有助于降低AI应用直接与外部系统交互带来的安全风险。AI模型本身无需持有敏感凭证(如API密钥),凭证可以安全地存储在服务器端。
本章深入探讨了MCP协议的核心概念与架构,揭示了其构建AI与外部世界连接的基石。我们了解到,MCP采用清晰的客户端-服务器架构,其中主机是AI应用的代表,客户端是内置的通信代理,而服务器则封装并提供标准化的外部资源和工具。协议层基于JSON-RPC 2.0定义了请求、响应和通知这三种核心交互原语,构成了所有通信的基本单元。客户端和服务器之间的交互遵循明确的连接生命周期,并通过请求-响应和通知模式进行消息交换,实现动态的能力发现和受控的安全访问。
这种架构设计不仅实现了AI模型与外部系统之间的解耦,极大地简化了集成复杂性,提高了系统的模块化和可扩展性,同时也为安全性提供了重要的保障。通过理解这些核心概念和架构,我们便能更好地把握MCP协议的工作原理,并为后续章节中更深入地探讨资源、工具、提示等具体功能以及实际应用场景打下坚实的基础。MCP协议的核心架构,正是其成为AI领域“通用适配器”的关键所在。