本节摘要:理解 MCP 的第一个,也是最容易被误解的一个概念,就是三个角色的职责边界。多数人第一次接触会以为「服务端直接和大模型对话」——这是错的。MCP 用一个清晰的三角划分了职责:宿主(Host) 是用户对话的 LLM 应用(Claude Desktop、Cursor、IDE),客户端(Client) 是宿主内部「说 MCP 的那一半」,服务端(Server) 是你用 SDK 写的、只负责暴露能力的东西。三者之间有一条铁律:服务端从不直接和模型对话。本节讲透这个三角,这是后续所有内容的地基——不先破除误解,后面每一章都会带着错误预期读下去。
先看一张完整的三角图,再逐个拆解:
┌─────────────────────┐ │ 宿主 Host │ │ (Claude / Cursor / │ │ IDE / Agent 运行时)│ │ │ │ ┌─────────────┐ │ │ │ 大模型 LLM │ │ ◄── 真正的推理在这里发生 │ └─────────────┘ │ │ │ │ │ ┌─────────────┐ │ │ │ 客户端 A │──┼──┐ (说 MCP 的那一半) │ │ 客户端 B │──┼──┤ 一个宿主对每个服务端跑一个客户端 │ └─────────────┘ │ │ └─────────────────────┘ │ │ MCP 协议(JSON-RPC) ┌──────────────┼──────────────┐ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ 服务端 A │ │ 服务端 B │ │ 服务端 C │ │(你写的) │ │(别人写的) │ │(你写的) │ │ 工具/资源/ │ │ 工具/资源/ │ │ 工具/资源/ │ │ 提示词 │ │ 提示词 │ │ 提示词 │ └────────────┘ └────────────┘ └────────────┘
逐个看每个角色的职责:
| 角色 | 是什么 | 谁开发 | 关键职责 |
|---|---|---|---|
| 宿主(Host) | 用户对话的 LLM 应用 | 宿主厂商(Claude、Cursor 等) | 跑模型、管对话、决定何时调用工具 |
| 客户端(Client) | 宿主内部「说 MCP 的那一半」 | 宿主厂商(或你用 SDK 自研) | 用 MCP 协议连服务端、转发请求 |
| 服务端(Server) | 你用本 SDK 写的东西 | 你 | 暴露工具/资源/提示词,执行业务逻辑 |
这是本节最重要的一句话:
服务端从不直接和大模型对话。
这句话的反面,就是很多人脑子里的错误模型:「我写的服务端会调用模型 API」。不会。服务端只做一件事:收到客户端发来的请求,执行业务逻辑,返回结果。它既不知道有模型存在,也不关心模型在干什么。
真正的模型推理,只发生在宿主内部。整条数据流是这样的:
模型在宿主里推理 │ ▼ 「我需要调用 add 工具」 宿主把请求交给客户端 │ ▼ 客户端用 MCP 协议发给服务端 服务端执行 add(1, 2) │ ▼ 返回 3 客户端把结果交回宿主 │ ▼ 宿主把结果喂回模型 模型继续推理:「结果是 3,所以……」
服务端在这条链路里,只是一个被动的能力提供者——它被问、它回答,从不主动找模型。
⚠️ 注意:这个边界不是任性设计,而是有深刻理由的。如果服务端能直接调模型,就会出现「每个服务端都要管模型 API Key、管对话历史、管 token 成本」的混乱——而把这些集中在宿主,服务端就能保持「纯粹的业务逻辑单元」。这个分工是 MCP 协议能在跨厂商(不同宿主、不同模型)环境下统一工作的根本前提。
你可能会问:宿主为什么不直接连服务端,中间非要隔一个「客户端」?
答案在于一对多。一个宿主(Claude Desktop)通常会同时连多个服务端——一个查数据库、一个读文件、一个调外部 API。宿主为每个连上的服务端,跑一个独立的客户端实例:
Claude Desktop(宿主) ├── 客户端 A ──► 数据库服务端 ├── 客户端 B ──► 文件系统服务端 └── 客户端 C ──► 外部 API 服务端
每个客户端封装了「连这一个服务端」所需的全部状态:协议版本、能力声明、会话、订阅、错误恢复。这种「一宿主多客户端」的模型,让宿主能用统一的对话界面,编排来自不同服务端的能力。
💡 技巧:这个「多客户端」模型也是第 10 章会讲的会话组(ClientSessionGroup) 的灵感来源——当你自己写 Host 时,会话组正是帮你管理「一堆客户端」的抽象。
理解了三角,你就能理解本 SDK 的定位:它同时给你服务端与客户端两套工具。
| 你想做的事 | 用 SDK 的哪部分 | 导入入口 |
|---|---|---|
| 写一个暴露能力的服务端 | 服务端 API | from mcp.server import MCPServer |
| 写一个消费 MCP 服务的客户端 | 客户端 API | from mcp import Client |
| 写自己的 Host(编排多服务) | 客户端 API + 会话组 | Client + ClientSessionGroup |
注意导入路径的有意区分:服务端多一层 .server,客户端是顶层。这个细节在第 1 章提过,这里再看一眼它的设计意图——让两端的边界在导入时就清晰,避免混用。
本教程的章节安排也对应这个划分:第 38 章讲服务端主线,第 910 章讲客户端主线。两端各自完整,又在架构(本章)、传输(第 8 章)、认证(第 11 章)这些横切章节交汇。
收尾时,把几个最常见的误解列一下,帮你巩固正确模型:
| 误解 | 真相 |
|---|---|
| 「服务端调用模型 API」 | 错。服务端从不和模型对话,模型只在宿主里推理 |
| 「客户端和服务端是同一个进程」 | 不一定。本地(stdio)是两个进程,内存连接才同进程,远程(HTTP)是两台机器 |
| 「一个宿主只连一个服务端」 | 错。一个宿主通常连多个,每个跑一个客户端实例 |
| 「SDK 既能写宿主又能写服务端」 | 部分。SDK 给你客户端与服务端,写宿主还需要你把模型推理拼进来 |
| 「MCP 是模型 API 的替代」 | 错。MCP 是「让模型用你的代码」的协议,模型 API 是另一回事 |
mcp.server vs mcp)。三角边界清楚了,下一节我们看这三者之间传递的「能力」到底分哪几类——用「谁决定调用」这个视角,重新理解工具、资源、提示词。