深度解析 MCP:为什么一串 JSON 就能让 Agent 接入外部世界?
在 AI Agent 快速普及的今天,MCP(Model Context Protocol,模型上下文协议)正在成为大模型连接本地工具、云端服务、数据库、文件系统和企业 API 的重要标准。
很多人第一次使用 MCP 时,都会被它的"轻量感"震住:在 Cursor、Qoder、CherryStudio 或其他 Agent Host 里,只要粘贴一段不到10行的JSON代码(手动方式,更方便的是通过token一键同步,但底层都是JSON),Agent 似乎立刻就学会了查天气、调浏览器、连数据库、调 API,它到底是怎么工作的?
本文将以一个"查询城市天气"的例子为主线,拆解 MCP 从配置加载、服务启动、能力发现、工具调用到结果返回的完整链路,并解释为什么一串 JSON 就能成为 Agent 连接外部世界的钥匙。
一、重新认识 MCP JSON:不是代码,而是连接契约
有些人会把 MCP 配置理解成一种"插件导入语句",Agent 读取 JSON 后,就会自动 import 某个 Python 包或 JavaScript 模块,然后把里面的函数注册进大模型,这个理解并不准确。
MCP 配置更像一张“连接说明书”。它告诉 Agent Host:如果你想接入某个能力,应该执行什么命令,传什么参数,连接哪个远程地址。至于这个 MCP Server 到底提供哪些工具、每个工具需要什么参数、返回什么结果,MCP配置JSON通常不涉及,而是由 Server 在启动后通过 MCP 协议动态声明。
用一句话概括:你粘贴的MCP配置文件是一把钥匙,它打开的是 Agent 和外部工具之间的标准化通道。下面我们以具体的案例来进行说明。
二、核心机制拆解:Agent 如何通过 MCP 学会新能力?
假设我们有一个 MCP 服务,叫 mcp-server-weather。它的作用很简单:根据城市名称查询实时天气。
我们给 Agent Host 导入的MCP配置长这样:
{
"mcpServers": {
"weather-helper": {
"command": "uvx",
"args": ["mcp-server-weather", "--api-key", "YOUR_WEATHER_API_KEY"]
}
}
}
当用户添加这个MCP服务时,后台经历了两个大的阶段:连接阶段(让 Agent 知道"有哪些工具可用")和调用阶段(实际执行工具并返回结果)。其中连接阶段又细分为三个步骤,下面逐一拆解。
2.1 连接阶段:让 Agent 知道有哪些工具
2.11 第一阶段:启动 MCP Server,建立传输通道
Agent Host 首先解析 MCP配置JSON,发现这里配置了一个名为 weather-helper 的 MCP Server。它需要执行的命令是 uvx,参数是 mcp-server-weather --api-key YOUR_WEATHER_API_KEY。连起来就是:
uvx mcp-server-weather --api-key YOUR_WEATHER_API_KEY
Claude Code通过命令行添加mcp也是这样执行的:Claude mcp add weather-helper uvx mcp-server-weather --api-key YOUR_WEATHER_API_KEY
于是,Agent Host 会调用操作系统的子进程机制,在后台启动这个服务。用伪代码表示,大致类似这样:
process = subprocess.Popen(
["uvx", "mcp-server-weather", "--api-key", "YOUR_WEATHER_API_KEY"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE
)
这里有一个关键点:对 Agent Host 来说,mcp-server-weather 并不是内置模块,它只是一个可以被操作系统启动的外部进程。Agent Host 不需要理解天气 API,也不需要知道这个包内部怎么实现。它只负责把这个进程拉起来,并和它建立通信。
在本地 stdio 通信模式下,Agent 和 MCP Server 之间通常通过标准输入输出通信。你可以把它理解成一根“传声筒”:Agent 往 Server 的 stdin 写入 JSON-RPC 消息,Server 再从 stdout 写回 JSON-RPC 响应。它们不一定需要开放网络端口,也不一定要走 HTTP,本地进程之间就能完成协议交互。
2.12 第二阶段:握手与能力发现
MCP Server 启动后,Agent Host 并不知道它能做什么。此时,双方先进行一次标准的初始化握手(initialize handshake),确认彼此的身份和能力:
Agent 发送 initialize 请求,声明自己支持的 MCP 协议版本(如 "2024-11-05")和客户端能力(如是否支持 sampling、roots 等特性)。Server 收到后返回自身的协议版本、服务端能力集以及服务名称和版本号。双方确认后,连接状态标记为 initialized。
这一步可以理解为 Agent 和 Server 在正式合作前先"对一下暗号"——确认都说同一种语言、都遵守同一套规则。
握手完成后,Agent 才正式开始探索 Server 提供了哪些工具。它会发送 tools/list 请求:
Agent 问 MCP Server:“你都提供哪些工具?每个工具怎么用?”
MCP Server 返回自己的能力声明。例如,天气服务可能返回如下结构:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "get_weather",
"description": "获取指定城市的实时天气",
"inputSchema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,例如 Beijing"
}
},
"required": ["city"]
}
}
]
}
}
这才是 MCP 的“魔法时刻”。Agent 并不是从配置 JSON 里知道 get_weather 的存在,而是在运行时通过 tools/list 动态发现了这个工具。
通常MCP不只返回一个工具,而是多个工具。这份工具声明里至少包含三类信息:工具名称,例如 get_weather;工具描述,例如“获取指定城市的实时天气”;输入参数的 JSON Schema,例如必须传入一个字符串类型的 city。
这些信息共同构成了 Agent 可理解的工具契约。
2.13 第三阶段:Schema 注入,让大模型知道自己有了工具
发现工具后,MCP SDK 或 Agent Host 会完成一个关键动作:把 MCP Server 返回的工具 Schema 转换成 LLM 原生支持的 Tool Use / Function Calling 格式,然后注入到后续对话上下文中。
为什么需要这一步转换? 因为 LLM 并不理解 MCP 协议——它只认识自己的 Function Calling 接口规范。MCP SDK 在这里扮演了"翻译官"的角色:
// ← MCP tools/list 原始响应(JSON-RPC 格式)
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [{
"name": "get_weather",
"description": "获取指定城市的实时天气",
"inputSchema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "城市名称" }
},
"required": ["city"]
}
}]
}
}
// → 转换后注入 LLM 的 Function Calling 格式
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的实时天气",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "城市名称" }
},
"required": ["city"]
}
}
}
可以看到,inputSchema 被映射为 parameters,其余结构一一对应。MCP 的价值正在于此——不管后端接的是天气 API、数据库还是文件系统,只要 Server 返回标准 Schema,SDK 就能自动完成格式转换,LLM 看到的始终是统一的 Function Calling 接口。
这一步很重要,因为真正做决策的是 LLM。LLM 本身不会直接读操作系统管道,也不会自己启动子进程。它看到的是 Agent Host 提供给它的上下文:当前有哪些工具、工具名称是什么、每个工具适合什么场景、调用时应该传哪些参数。
至此,连接阶段完成,MCP 服务已就绪,等待用户触发调用。
2.2 调用阶段:实际执行工具并返回结果
第四阶段:工具调用闭环,意图变成真实执行
当用户问“北京今天天气怎么样?”时,大模型会判断:这是一个实时信息问题,单靠模型参数无法回答;当前上下文里有一个 get_weather 工具;这个工具需要 city 参数;用户提到的城市是“北京”,对应参数可以是 Beijing 或 北京。
当 LLM 决定调用工具后,Agent Host 会把这个意图封装成 MCP 的 JSON-RPC 请求,发送给 MCP Server。例如:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {
"city": "Beijing"
}
}
}
随后,真正的执行发生在 mcp-server-weather 这个进程里。它会解析参数,拿到 city = Beijing,然后调用真实的天气 API,例如 OpenWeather、和风天气或企业内部气象服务。拿到结果后,MCP Server 再把结构化结果写回通信通道。
Agent Host 接收到结果后,会把结果交回给 LLM。LLM 再根据工具返回内容组织自然语言回答,例如:“北京今天晴,气温约 25℃,适合外出,但早晚温差较大。”
到这里,一个完整闭环就完成了:用户提出需求,LLM 判断是否需要工具,Agent Host 按协议发起调用,MCP Server 执行真实业务逻辑,结果返回给 LLM,最终以自然语言呈现给用户。总体流程如下:
补充:调用失败了怎么办? 以上展示的是成功路径。如果天气 API 超时、鉴权失败或返回异常,MCP Server 并不会直接崩溃,而是通过 JSON-RPC 的
error对象返回结构化错误信息(包含错误码和描述)。Agent Host 再将错误注入 LLM 上下文,LLM 可以据此向用户解释失败原因、建议替代方案、甚至自动重试。这正是"协议"的价值——不仅定义了正常交互,也定义了异常处理的标准路径。
三、MCP 的架构精髓:三层解耦
MCP 之所以能用一串 JSON 撬动庞大的工具生态,核心在于它做到了三层解耦。
3.1 传输解耦
JSON Config 只负责描述如何建立通道,可以是 stdio、本地命令、Docker,也可以是 HTTP/SSE 远程地址。Agent Host 不需要为每个工具写专门适配器。
3.2 能力解耦
Agent 并不需要提前知道工具有哪些,Server 会在运行时通过 tools/list 动态声明自己的能力。工具名称、描述、参数 Schema 都由 Server 提供,再由 Agent Host 转换成 LLM 可理解的工具上下文。
3.3 分发解耦
MCP Server 的代码可以通过 PyPI、npm、GitHub、Docker、企业内网或云服务交付。MCP 协议并不绑定某个包仓库,也不强迫开发者走统一发布平台。
这三层解耦让 Agent 生态从"为每个工具写一套硬编码插件"转向"用统一协议连接任意工具"。开发者只要实现 MCP Server,声明工具 Schema,处理 tools/call,就可以让不同 Agent Host 以相似方式接入自己的能力。
MCP vs 原生 Function Calling:差异到底在哪?
| 维度 | MCP | 原生 Function Calling |
|---|---|---|
| 工具定义 | Server 运行时动态声明 | 开发者硬编码在代码中 |
| 新增工具 | 粘贴 JSON 即可 | 改代码 + 重新部署 |
| 跨 Host 复用 | 同一 Server,多 Host 通用 | 每个 Host 单独写一套 |
| 工具执行 | 独立子进程,隔离运行 | Host 进程内调用 |
| 协议标准 | JSON-RPC 2.0 统一协议 | 各平台私有格式 |

一句话总结:Function Calling 是把工具"写死在代码里",MCP 是把工具变成"可插拔的标准化服务"。 这也是为什么你在 Cursor 里配好的 MCP Server,换到 Claude Code 里粘贴同一段 JSON 照样能跑——工具逻辑和 Agent Host 彻底解耦了。
四、MCP 服务的四种分发与运行方式
理解了前面的流程,再回头看“一串 JSON 接入万物”,就会发现它并不神秘。
JSON 的作用不是承载能力本身,而是告诉 Agent Host 如何找到 MCP Server,并建立与它的通信通道。至于 MCP Server 的代码从哪里来、运行在哪里、如何分发,则有不同形态。
4.1 场景一:PyPI / npm 分发,本地 stdio 运行
最常见的配置是这样的:
{
"command": "uvx",
"args": ["mcp-server-weather"]
}
或者 Node.js 生态中常见的:
{
"command": "npx",
"args": ["mcp-server-weather"]
}
uvx 和 npx 都是包运行工具。它们的作用是根据包名去 PyPI 或 npm 等包仓库下载代码,准备依赖环境,然后把对应程序作为本地进程启动。
这类方式对使用者很友好,因为很多时候不需要手动安装包,也不需要关心代码放在哪里。只要本机有对应运行环境,Agent Host 执行这条命令,就能把 MCP Server 拉起来。
但要注意,PyPI 或 npm 只是代码分发渠道,不是 MCP 协议的注册中心。开发者把 MCP Server 发布到包平台,是为了方便别人下载和运行;MCP 协议本身并不要求所有服务都注册到某个官方中心。
这种模式适合需要访问本地资源的工具,例如本地文件系统、本地数据库、浏览器自动化、命令行工具、开发环境操作等。因为 MCP Server 就运行在用户机器上,所以它天然更容易接触本地上下文。
4.2 场景二:GitHub 或本地脚本,直接启动
开发调试或企业内部工具中,也常见这样的配置:
{
"command": "python",
"args": ["/Users/username/github/my-mcp-weather/server.py"]
}
这种方式完全不需要把代码发布到 PyPI 或 npm。开发者可以把代码放在 GitHub,使用者 git clone 到本地后,把脚本路径写进 JSON;也可以干脆是一个从未公开的企业内部脚本。
Agent Host 不关心这个脚本来自哪里。它只负责执行 python /path/to/server.py,然后按 MCP 协议和这个进程通信。只要这个脚本实现了 MCP Server 所需的协议响应,它就能被 Agent 当作工具服务使用。
这种方式适合个人原型、内部系统集成、私有 API 封装和快速调试。它的优点是灵活,不需要发布流程;缺点是使用者必须自己准备代码、依赖和本地路径。
4.3 场景三:远程 HTTP/SSE 服务,连接云端
有些 MCP Server 并不运行在用户电脑上,而是部署在云端。这时配置里可能没有 command 和 args,而是一个远程地址:
{
"mcpServers": {
"cloud-weather": {
"url": "https://api.example.com/mcp/weather"
}
}
}
在这种模式下,Agent Host 不再启动本地子进程,而是通过 HTTP、SSE(Server-Sent Events)或其他 MCP 支持的远程传输方式连接云端服务。Server 运行在开发者或企业的服务器上,Agent 通过网络与它完成初始化、工具发现和工具调用。
这种方式对非技术用户更友好,因为用户本地不需要 Python、Node.js、Docker 等环境,也不用下载代码。它也适合 SaaS 产品和企业共享服务,因为服务提供方可以集中维护能力、统一鉴权、统一升级。
但它也带来新的要求:服务端需要处理认证授权、访问控制、限流、日志、安全审计和可用性。尤其当工具涉及用户隐私数据或企业内部系统时,远程模式必须认真设计 OAuth2、API Key、权限隔离和数据边界。
4.4 场景四:Docker 容器化分发,隔离复杂环境
还有一种常见方式是通过 Docker 启动 MCP Server:
{
"command": "docker",
"args": ["run", "-i", "--rm", "my-weather-mcp:latest"]
}
这种方式的本质仍然是本地 stdio 模式,只不过 MCP Server 被封装在容器里运行。Agent Host 执行 Docker 命令,Docker 拉起容器,容器内的进程通过标准输入输出与 Agent Host 通信。
Docker 模式适合依赖复杂系统库、需要环境隔离或希望降低本机污染的场景。例如某个 MCP Server 依赖特定版本的浏览器、数据库客户端、图像处理库或系统二进制工具,直接要求用户本地安装会很麻烦;用容器封装后,运行环境就更可控。
不过,Docker 并不会自动消除安全问题。容器权限、挂载目录、网络访问和密钥传入方式都需要谨慎配置。MCP 提供的是通信标准,不是万能安全沙箱。
3.5 四类方式的核心差异对比
从上面几个场景可以看出,MCP 的分发与运行方式并不只有一种。它可以通过 PyPI、npm、GitHub、本地脚本、远程服务或 Docker 镜像交付。

这些方式的差异主要体现在三个问题上:代码从哪里来,Server 在哪里运行,以及 Agent 如何建立通信。
如果使用 uvx 或 npx,代码来自包管理器,Server 通常在本地作为子进程运行,适合社区工具和本地能力扩展。如果使用本地脚本,代码来自用户文件系统,适合私有工具和调试。如果使用远程 URL,代码运行在云端,适合 SaaS 和企业共享服务。如果使用 Docker,代码来自镜像仓库,运行在容器中,适合依赖复杂或需要隔离的工具。
但无论哪种方式,有一点是不变的:MCP 协议本身不负责分发代码,也不要求服务注册到某个中心。它只规定 Agent 和 Server 之间如何说话。
4.6 MCP 服务目录网站:给人看的"应用商店"
既然 MCP 不要求官方注册中心,为什么网上还有很多 MCP 服务目录网站?比如:
是目前全球最大的 MCP 服务市场,收录超过 2 万个 MCP 服务器、客户端和集成,支持按类别浏览和搜索,是发现和挑选 MCP 工具的首选一站式目录
lobehub
- 通过活跃度、稳定性、社区反馈三个维度对 MCP 服务器进行评分,帮你筛选可信赖的服务,支持中英文

这些网站更像“给人看的应用商店”,而不是 Agent 必须访问的注册中心。它们通常承担三类作用:帮助开发者展示自己的 MCP Server,帮助用户发现可用工具,提供一键复制配置或托管部署能力。
但 Agent 在运行时并不会自动去这些网站搜索工具。它只认当前 Host 配置里写明的 MCP Server。你粘贴了某段 JSON,它就按那段 JSON 去启动进程或连接 URL;你没有配置的服务,它通常不会凭空知道。
这也是 MCP 去中心化设计的一部分。MCP 规范关注的是协议互通,而不是把所有工具纳入一个中心化市场。
五、安全边界:MCP 不是魔法,也不是免审计通道
MCP 让工具接入变得简单,但简单不等于没有风险。
6.1 本地 stdio 模式的风险
在本地 stdio 模式下,MCP Server 运行在用户电脑上,理论上可以访问它被授予的本地资源。因此,用户在粘贴配置时,本质上是在允许 Agent Host 启动某个外部程序。这个程序来自哪里、是否可信、是否会读取敏感文件、是否会把数据发往外部网络,都需要审慎判断。
6.2 远程 HTTP/SSE 模式的风险
在远程 HTTP/SSE 模式下,数据会通过网络传给云端 MCP Server。此时更要关注鉴权、隐私、日志留存和权限边界。对于企业场景,最好使用最小权限原则,明确哪些工具可以访问哪些系统、哪些数据可以被返回给模型、哪些操作需要用户确认。
因此,MCP 的安全性来自协议设计、Host 权限控制、Server 实现质量和用户授权机制的共同作用,而不是只靠“用了 MCP”这件事本身。
六、结语:一串 JSON 背后的真实含义
当你下一次在 Agent 里粘贴一段 MCP 配置时,可以把它理解成一张“工具接入说明书”。它没有把代码塞进 Agent,也没有让大模型瞬间学会某个 API 的内部实现;它只是告诉 Agent:去哪里找到一个 MCP Server,如何和它建立连接。
真正让 Agent 获得能力的,是连接建立之后的一系列标准化交互:Server 通过 tools/list 声明工具,Agent Host 把工具 Schema 注入 LLM 上下文,LLM 根据用户意图选择工具,Host 再通过 tools/call 请求 Server 执行业务逻辑,最后把结果交给 LLM 组织成自然语言。
这就是 MCP 的关键价值:它把 AI 能力扩展从“硬编码集成”变成了“协议化连接”,把工具生态从“每个 Agent 各写一套插件”推向“一个 Server 可被多个 Host 理解”。
所以,一串 JSON 并没有施魔法。它只是打开了一扇门。门后,是 MCP 用标准协议搭建起来的外部世界。
更多推荐



所有评论(0)