---
title: "MCP 架构：Host、Client、Server 三角色"
wiki: mcp
category: "协议规范"
slug: mcp-architecture
url: https://learnagent.wiki/mcp/cards/mcp-architecture
tags: ["MCP", "架构", "Host", "Client", "Server"]
last_updated: 2026-04-11
reading_time: 10
---

> MCP 的整体设计是一个**客户端-服务端架构**，但和你平时理解的"浏览器-服务器"不太一样——它中间多了一个"Host"的角色。一共三个角色：**Host（宿主）、Client（客户端）、Server（服务端）**。

# MCP 架构：Host、Client、Server 三角色

## 基础概念

MCP 的整体设计是一个**客户端-服务端架构**，但和你平时理解的"浏览器-服务器"不太一样——它中间多了一个"Host"的角色。一共三个角色：**Host（宿主）、Client（客户端）、Server（服务端）**。

先用一个生活类比帮你建立直觉：

把 MCP 想象成一家餐厅。**Host 是餐厅经理**，负责协调一切；**Client 是服务员**，每个服务员对接一个厨房窗口；**Server 是厨房**，每个厨房专门做一类菜。当客人（用户）说"我想吃牛排"，经理判断该去哪个厨房，派对应的服务员去下单，厨房做好菜之后通过同一个服务员端回来。

三者之间的核心关系：**一个 Host 管多个 Client，一个 Client 对应一个 Server，Client 和 Server 是一对一的专线连接。**

### 核心要素

| 角色 | 是什么 | 职责 |
|------|--------|------|
| **Host** | AI 应用本体（Claude Desktop、Cursor、VS Code 等） | 运行大模型、管理对话、创建和管理所有 Client 实例、决定何时调用工具 |
| **Client** | Host 内部的连接器组件 | 与一个特定的 Server 保持专线连接、转发 Host 的请求、接收 Server 的响应和通知 |
| **Server** | 提供能力的外部程序 | 暴露 Tools / Resources / Prompts 给 Client、处理调用请求、返回结果 |

### 三角色关系图

```mermaid
graph TB
    User([👤 用户])
    
    subgraph Host["🖥️ MCP Host（例：Claude Desktop）"]
        LLM[大模型引擎]
        C1["Client ①"]
        C2["Client ②"]
        C3["Client ③"]
    end
    
    S1["📁 Filesystem Server"]
    S2["🐙 GitHub Server"]
    S3["📊 PostgreSQL Server"]

    User -->|对话| Host
    LLM -.->|需要读文件| C1
    LLM -.->|需要查代码| C2
    LLM -.->|需要查数据| C3
    C1 ---|stdio 专线| S1
    C2 ---|stdio 专线| S2
    C3 ---|stdio 专线| S3
```

**关键理解**：Client 不是一个独立的应用程序，它是 Host 内部的一个组件。当你在 Claude Desktop 配置里写了 3 个 Server，Host 启动时会在内部创建 3 个 Client 实例，分别和对应的 Server 建立连接。用户看到的只有 Host（Claude Desktop 窗口），Client 完全是幕后工作。

### Host 到底干了什么

Host 是整个 MCP 交互的"总指挥"，它的工作比你想象的要多：

1. **创建 Client**：读取配置，知道要连哪些 Server，为每个 Server 创建一个 Client
2. **生命周期管理**：启动时让每个 Client 和 Server 完成初始化握手（能力协商），关闭时通知 Server 断开
3. **工具路由**：大模型决定要调用某个工具时，Host 要知道这个工具属于哪个 Server，交给对应的 Client 去执行
4. **安全管控**：决定是否允许模型调用某个工具、是否需要用户确认，这都是 Host 层面的职责
5. **上下文合并**：从多个 Server 拿回的 Resources 和工具结果，统一整理后送进模型的上下文窗口

### 本地 Server vs 远程 Server

MCP 里的"Server"不一定跑在远方的云服务器上。实际上大多数日常用的 Server 都是**本地进程**：

```mermaid
graph LR
    subgraph Local["🏠 你的电脑"]
        Host["Host（Claude Desktop）"]
        LS1["本地 Server A<br/>（Filesystem）"]
        LS2["本地 Server B<br/>（Git）"]
        Host ---|stdio 管道| LS1
        Host ---|stdio 管道| LS2
    end
    
    subgraph Remote["☁️ 远端"]
        RS1["远程 Server C<br/>（Sentry）"]
    end
    
    Host ---|HTTP| RS1
```

| 对比 | 本地 Server | 远程 Server |
|------|-------------|-------------|
| 运行位置 | 和 Host 在同一台电脑上 | 跑在云端或其他机器上 |
| 传输方式 | stdio（标准输入输出） | Streamable HTTP |
| 启动方式 | Host 自动 spawn 子进程 | Server 已在远端运行，Host 通过 URL 连接 |
| 网络需求 | 无 | 需要网络，可能需要鉴权（OAuth / API Key） |
| 典型例子 | Filesystem、Git、SQLite | Sentry、Cloudflare、Linear |
| 并发 | 通常一个 Client 独占一个 Server 进程 | 一个远程 Server 可以同时服务多个 Client |

### 连接的生命周期

每个 Client-Server 连接都有一个明确的生命周期：

```mermaid
sequenceDiagram
    participant H as Host
    participant C as Client
    participant S as Server

    H->>C: 创建 Client 实例
    H->>S: 启动 Server 进程（本地）或连接 URL（远程）
    C->>S: initialize（版本 + 能力协商）
    S-->>C: initialize 响应（Server 支持的能力）
    C->>S: notifications/initialized（Client 准备好了）
    Note over C,S: 连接就绪，可以开始交互
    
    C->>S: tools/list
    S-->>C: 返回工具列表
    C->>S: tools/call
    S-->>C: 返回调用结果
    
    Note over C,S: ...正常使用中...
    
    H->>C: 关闭连接
    C->>S: 关闭通知
    S-->>C: 确认关闭
```

初始化阶段有一个**能力协商**的步骤：Client 告诉 Server "我支持 Sampling 和 Elicitation"，Server 告诉 Client "我提供 Tools 和 Resources"。协商结果决定了这个连接可以做哪些事。后续的调用不会超出协商范围。

## 基础用法

理解了三角色之后，最直观的实践就是**在 Claude Desktop 里同时配多个 Server**，看它们如何协同工作。

下面是一个配置了 3 个本地 Server 的 `claude_desktop_config.json`：

```json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
      }
    },
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": {
        "DATABASE_URL": "postgresql://user:pass@localhost:5432/mydb"
      }
    }
  }
}
```

启动 Claude Desktop 后，Host 做了这些事情：

1. 读取配置，发现有 3 个 Server
2. 为每个 Server 创建一个 Client 实例
3. 分别 spawn 3 个子进程（`npx @modelcontextprotocol/server-xxx`）
4. 每个 Client 和对应的 Server 完成能力协商
5. 从 3 个 Server 收集所有可用的 Tools，合并成一个统一工具列表
6. 用户提问时，大模型从这个统一列表里选择要调用的工具

当你对 Claude 说"帮我把 GitHub 上 my-repo 的 README 下载到本地 projects 文件夹"，Claude 可能会先调 GitHub Server 的 `get_file_contents` 读取内容，再调 Filesystem Server 的 `write_file` 写入本地，两个 Server 各司其职、互不干扰。

## 同类工具对比

MCP 的 Host-Client-Server 架构和其他"让 AI 用工具"的方案相比，差异在哪？

| 维度 | MCP 三角色架构 | OpenAI Assistants API | LangChain Agent |
|------|---------------|----------------------|-----------------|
| 工具注册方式 | Server 通过 `tools/list` 动态暴露，Host 自动发现 | 创建 Assistant 时在 API 里写死 tools 列表 | 在 Python 代码里硬编码 Tool 对象 |
| 工具跨应用复用 | ✅ 一个 Server 可被任何 Host 使用 | ❌ 绑定 OpenAI 平台 | ❌ 绑定 LangChain |
| 进程隔离 | ✅ 每个 Server 是独立进程，崩溃不影响 Host | ❌ 工具在 API 服务端运行，用户无法控制 | ❌ 工具在同一 Python 进程里 |
| 动态更新工具 | ✅ Server 可发通知让 Client 重新拉取工具列表 | ❌ 需要重新创建 Assistant | ❌ 需要重启进程 |
| 多工具源组合 | ✅ 一个 Host 同时挂多个 Server | 需要自己在 tools 数组里合并 | 需要自己在代码里合并 |

核心区别：

- **MCP**：工具在独立进程里运行，通过标准协议暴露，支持运行时动态发现和更新
- **OpenAI Assistants**：工具定义嵌在 API 调用里，和 OpenAI 平台绑定
- **LangChain**：工具是 Python 对象，和框架代码混在一起，没有进程隔离

## 常见误区

| 误区 | 准确理解 |
|------|----------|
| 以为 Client 是一个独立的应用程序 | Client 是 Host 内部的组件，用户看不到也操作不了。你打开 Claude Desktop 看到的整个窗口就是 Host，Client 藏在里面 |
| 以为一个 Client 可以连多个 Server | 不行，**严格一对一**。一个 Client 实例只维护和一个 Server 的连接。要连 3 个 Server，Host 就创建 3 个 Client |
| 以为 Server 崩溃会拖垮整个 AI 应用 | 因为进程隔离，一个 Server 进程崩了只影响它对应的那个 Client 连接，其他 Server 照常工作 |
| 以为远程 Server 和本地 Server 的协议不一样 | 数据层（JSON-RPC 消息格式）完全一样，只是传输方式不同（stdio vs HTTP）。Server 代码通常只需换一行启动配置就能从本地切到远程 |
| 以为 Host 只是个"透传器" | Host 的角色远比透传复杂：它负责安全管控（是否允许调用）、上下文管理（哪些信息送进模型）、工具路由（分发到正确的 Server）|

## 优劣势分析

| 优势 | 劣势 |
|------|------|
| **进程隔离**：Server 崩溃不影响 Host，安全边界清晰 | **配置门槛**：新手需要手动编辑 JSON 配置文件，没有图形化的 Server 管理界面（部分客户端已在改善） |
| **一对一专线**：每个 Client-Server 连接独立，互不干扰，方便调试 | **资源开销**：每个本地 Server 都是一个独立进程，挂 10 个 Server 就有 10 个 Node.js 进程在跑 |
| **动态发现**：Server 可以运行时改变工具列表并通知 Client 更新，不需要重启 | **没有内置的 Server 间通信**：两个 Server 之间无法直接交换数据，必须通过 Host 中转 |
| **本地/远程统一抽象**：同一套 JSON-RPC 消息格式，切换部署方式不需要改业务逻辑 | **能力协商是一次性的**：初始化时协商好的能力在连接生命周期内不会重新协商（但工具列表可以动态更新） |

## 思考题

<details>
<summary>初级：为什么 MCP 要把 Host 和 Client 分开，而不是合成一个角色？</summary>

**参考答案：**

分开的核心原因是**职责分离**。Host 的职责是"管理和协调"——管理对话、运行模型、做安全管控；Client 的职责是"维护连接"——和一个特定的 Server 保持通信。

如果合在一起，Host 直接连 10 个 Server 的话，所有连接管理、消息路由、错误处理的逻辑会全部堆在一块，代码会变得又大又脆弱。拆出 Client 后，每个 Client 只关心自己那条连接，Host 只需要管理一组 Client 对象，职责清晰，出问题也好排查。

打个比方：公司 CEO（Host）不会自己跑去和每个供应商谈细节，而是派不同的采购员（Client）分别对接。CEO 只需要知道"采购员 A 负责原材料，B 负责物流"，具体沟通细节交给采购员去做。

</details>

<details>
<summary>中级：如果一个远程 Server 同时被 5 个不同 Host 的 Client 连接，会有什么问题需要考虑？</summary>

**参考答案：**

主要需要考虑三个方面：

1. **状态隔离**：每个 Client 连接应该有独立的会话状态。如果 Server 内部用了全局变量来存状态，5 个 Client 的请求会互相干扰。正确的做法是用 session ID 来隔离每个连接的状态。

2. **并发与资源竞争**：如果 Server 背后操作的是同一份数据（比如同一个数据库），多个 Client 同时调用写操作可能产生冲突。Server 需要有适当的并发控制机制（锁、事务、队列等）。

3. **鉴权与权限**：不同 Host 可能代表不同的用户或组织，远程 Server 需要对每个连接做独立的身份验证和权限检查。Streamable HTTP transport 支持 OAuth 和 Bearer Token 来实现这一点。

本地 Server（stdio）通常不会遇到这些问题，因为它们是 Host spawn 的子进程，天然一对一。多连接并发是远程 Server 的特有挑战。

</details>

## 参考资料

1. 官方架构文档：<https://modelcontextprotocol.io/docs/concepts/architecture>（查询日期 2026-04-11）
2. 官方 Server 概念：<https://modelcontextprotocol.io/docs/concepts/server-concepts>
3. 官方 Client 概念：<https://modelcontextprotocol.io/docs/concepts/client-concepts>
4. MCP 协议规范（生命周期部分）：<https://modelcontextprotocol.io/specification/latest/basic/lifecycle>
5. Anthropic 发布公告：<https://www.anthropic.com/news/model-context-protocol>
6. 官方 Reference Server 仓库：<https://github.com/modelcontextprotocol/servers>

---
*Source: https://learnagent.wiki/mcp/cards/mcp-architecture*
*Markdown mirror of https://learnagent.wiki, served as text/markdown for LLM ingestion.*