要理解模型上下文协议(MCP)的架构,需要区分组件扮演的逻辑角色及其物理部署。与传统客户端-服务器REST API中单个客户端通常与一个单体服务器通信不同,MCP采用星形拓扑结构。一个独立的控制应用程序管理着与多个、独立的上下文提供者的连接。这种设计将系统的智能(大型语言模型)与数据源的具体实现细节分离。我们将系统组件分为三个不同的角色:宿主、客户端和服务器。核心组件在MCP生态系统中,职责被分片以确保模块化和安全性。MCP服务器服务器是上下文的基础单元。它是一个独立的进程或网络服务,公开三种特定原语:资源、提示和工具。MCP服务器不包含自己的LLM,也不维护会话历史。其唯一目的是响应标准化的JSON-RPC请求。例如,一个“PostgreSQL服务器”知道如何对数据库执行SQL查询,但它不知道为什么要执行该查询,也不知道是哪个用户请求的。它严格根据协议连接提供的输入进行操作。MCP客户端客户端是协议的实现,负责与服务器保持$1:1$连接。它处理握手、能力协商和消息传输。在大多数实现中,客户端是一个库(如官方的TypeScript或Python SDK),集成到一个更大的应用程序中。宿主应用程序宿主是用户与之交互的应用程序,例如IDE(VS Code)、聊天界面(Claude Desktop)或AI代理运行时。宿主创建LLM运行的环境。重要的是,宿主管理客户端-服务器连接的生命周期。由于客户端和宿主在同一进程中运行,人们常将它们混淆。然而,这种区分有其意义:宿主负责决策(例如,“我应该向用户提供哪些工具?”,“我应该允许此服务器读取文件吗?”)。客户端执行协议机制(例如,“发送tools/list消息”,“解析JSON-RPC响应”)。架构布局标准拓扑包含一个宿主应用程序实例化多个MCP客户端。每个客户端连接到一个不同的MCP服务器。这形成了一个$1:N$的关系,其中一个用户界面汇集了来自多个独立数据源的能力。digraph MCP_Topology { rankdir=TB; node [style=filled, shape=box, fontname="Helvetica", fontsize=10, color="#dee2e6", margin=0.2]; edge [fontname="Helvetica", fontsize=9, color="#868e96", arrowsize=0.6]; subgraph cluster_host { label="宿主应用程序进程"; style=rounded; bgcolor="#f8f9fa"; color="#adb5bd"; fontcolor="#495057"; Orchestrator [label="编排器 / 用户界面", fillcolor="#e9ecef", width=2.5]; subgraph cluster_clients { label=""; style=invis; ClientA [label="MCP 客户端 A", fillcolor="#d0bfff"]; ClientB [label="MCP 客户端 B", fillcolor="#bac8ff"]; ClientC [label="MCP 客户端 C", fillcolor="#a5d8ff"]; } } subgraph cluster_servers { label="上下文提供者 (子进程)"; style=invis; ServerA [label="文件系统服务器", fillcolor="#96f2d7"]; ServerB [label="Git 服务器", fillcolor="#63e6be"]; ServerC [label="Postgres 服务器", fillcolor="#38d9a9"]; } Orchestrator -> ClientA [style=dotted, arrowtail=none]; Orchestrator -> ClientB [style=dotted]; Orchestrator -> ClientC [style=dotted]; ClientA -> ServerA [label="标准IO管道"]; ClientB -> ServerB [label="标准IO管道"]; ClientC -> ServerC [label="标准IO管道"]; }宿主应用程序汇集连接。每个客户端管理与特定服务器的专用管道,从而隔离数据上下文。本地进程拓扑MCP最常见的配置是本地集成模型。在这种场景下,宿主应用程序将MCP服务器作为子进程生成。通信依赖于标准输入/输出(stdio)。宿主启动服务器可执行文件(例如,uvx mcp-server-git),并连接到其stdin和stdout流。宿主将JSON-RPC请求写入服务器的stdin。服务器将其JSON-RPC响应写入stdout。诊断日志(不应作为协议消息解析)写入stderr。这种拓扑提供了实际的安全优势。因为服务器作为用户启动的子进程运行,它继承了用户的本地权限,但在进程树的范围内操作。如果宿主终止,操作系统会确保服务器进程也终止,从而防止孤儿进程。远程拓扑尽管本地子进程能有效处理个人数据,但企业架构通常需要集中式上下文。在远程拓扑中,服务器作为独立的网络服务运行,通常在Docker容器或云函数中。宿主使用服务器发送事件(SSE)作为传输层连接到此远程服务器。客户端到服务器: 客户端向特定端点(例如,/mcp/messages)发送HTTP POST请求。服务器到客户端: 服务器通过开放的SSE连接将JSON-RPC消息推回给客户端。这种配置改变了信任模型。在本地拓扑中,宿主通过执行服务器二进制文件来明确信任它。在远程拓扑中,由于数据通过网络传输,宿主必须验证服务器端点并确保传输已加密(HTTPS)。聚合与隔离MCP拓扑的一个定义性特征是服务器之间不相互通信。它们是完全隔离的。如果用户要求LLM“读取文件data.csv并将其插入SQL数据库”,负责文件系统的服务器不会将数据发送到数据库服务器。相反,数据流通过宿主:工具调用 1: 宿主从文件系统服务器请求文件内容。结果: 文件系统服务器将内容返回给宿主。推断: LLM(通过宿主)分析内容并生成一个SQL查询。工具调用 2: 宿主将SQL查询发送到数据库服务器。结果: 数据库服务器确认执行。这种集中路由确保宿主保持对信息流的控制。它阻止了受损或故障的服务器在没有宿主明确编排的情况下访问另一服务器中的资源。能力协商在连接阶段首次建立拓扑时,客户端和服务器进行握手以声明能力。这使得架构能够版本无关且灵活。服务器发送一个包含capabilities对象的initialize结果。此对象定义服务器支持哪些原语。$$ \text{能力} = { \text{资源?}, \text{提示?}, \text{工具?}, \text{日志?} } $$如果服务器没有声明支持tools,客户端就知道不向该特定连接的LLM呈现任何工具使用能力。这一协商步骤允许宿主连接到旧版服务器而不中断,或者连接到只提供提示但没有可执行工具的专门服务器。MCP的拓扑结构设计上是刚性的(星形拓扑),但在能力上是灵活的(协商)。通过严格定义客户端、宿主和服务器的角色,该协议确保开发人员能够构建与任何符合MCP的宿主应用程序普遍兼容的服务器。