智能体面试准备(四):MCP 协议深入——从架构设计到手写一个 MCP Server

智能体面试准备(四):MCP 协议深入——从架构设计到手写一个 MCP Server

Model Context Protocol(MCP)是 Anthropic 在 2024 年底开源的一套标准协议,目标是解决"每个 AI 应用都要自己接一遍工具"的碎片化问题。你可以把它理解成"AI 工具调用的 USB-C 接口"——定义了一套统一的客户端-服务端协议,任何 MCP Server 暴露的工具/资源,都能被任何 MCP Client(Claude Desktop、Cursor、自研 Agent)直接使用。面试官考 Agent 岗,MCP 已经成了 2026 年的高频题,这篇文章把它的架构、协议、实现拆透,配可运行代码。

一、为什么需要 MCP:M × N 问题

在 MCP 之前,让 AI 应用接入外部工具是 M × N 问题:M 个 AI 应用(Claude、ChatGPT、Cursor、自研 Agent)× N 个工具源(数据库、文件系统、GitHub、Slack),每个组合都要写一遍集成代码。工具一多,集成成本爆炸。

MCP 的解法是引入一个中间层:工具提供方写一个 MCP Server(只写一次),AI 应用实现一个 MCP Client(只写一次),二者通过标准协议通信。M × N 降为 M + N。

| 对比 | 无 MCP | 有 MCP | |---|---|---| | 集成成本 | M × N | M + N | | 工具复用 | 每个应用各接一遍 | 写一次 Server,所有 Client 通用 | | 协议 | 各家私有 | JSON-RPC 2.0 标准 | | 工具发现 | 硬编码 | 运行时动态发现 | | 换模型/换工具 | 重写集成 | 换 Client/Server 即可 |

这个"M × N → M + N"的论点是面试回答 MCP 价值时的核心框架。

二、MCP 的三层架构

MCP 架构分三层:Host(宿主)、Client(客户端)、Server(服务端)。

| 角色 | 职责 | 示例 | |---|---|---| | Host | 运行 LLM、管理对话、决定何时调工具的应用 | Claude Desktop、Cursor、IDE 插件 | | Client | Host 内的协议适配层,与 Server 通信 | Host 内嵌的 MCP Client 实例 | | Server | 工具/资源的提供方,独立进程 | 文件系统 Server、GitHub Server |

一个 Host 可以连多个 Server(每个 Server 一个 Client 实例),每个 Server 暴露若干工具和资源。Host(LLM)看到的是所有已连接 Server 的工具的合集,统一调度。

通信方式有两种:stdio(子进程,本地用,Server 作为 Host 的子进程运行,通过标准输入输出传 JSON-RPC)和SSE/HTTP(远程用,Server 独立部署,通过网络通信)。本地开发多用 stdio,生产部署多用 SSE。

三、MCP 的三大能力原语

MCP Server 通过三种"原语"(primitive)向 Client 暴露能力:

| 原语 | 方向 | 用途 | 类比 | |---|---|---|---| | Tools | Server → Model | 可执行的操作(函数调用) | Function Calling | | Resources | Server → Model | 可读取的数据(文件、数据库记录) | GET 请求 | | Prompts | Server → User | 预定义的提示词模板 | Slash 命令 |

Tools是最常用的原语,等同于 Function Calling 里的函数。Server 声明工具的 Schema,Client(Host)把它发给 LLM,LLM 决定调用时,Client 通过协议把调用请求转发给 Server 执行,结果返回给 LLM。

Resources让 Server 暴露可读数据,比如文件系统 Server 暴露file:///path/to/file资源,Client 可以按需读取。与 Tools 的区别:Resources 是"读取"(幂等、无副作用),Tools 是"执行"(可能有副作用)。

Prompts是预定义的提示词模板,用户在 Host 里选一个 Prompt,Server 填充参数后返回完整提示词给 LLM。适用于标准化工作流。

四、JSON-RPC 2.0:MCP 的传输协议

MCP 用 JSON-RPC 2.0 做消息格式。核心消息类型:

| 消息类型 | 方向 | 用途 | |---|---|---| | Request | 双向 | 请求(带 id,期待响应) | | Response | 双向 | 对 Request 的响应 | | Notification | 双向 | 通知(不带 id,不期待响应) |

一个典型的 MCP 交互流程:

  1. Client → Server:initialize请求(协商协议版本、能力)
  2. Server → Client:initialize响应(声明自己支持的原语)
  3. Client → Server:tools/list请求(获取工具列表)
  4. Server → Client:返回工具 Schema 列表
  5. (LLM 决定调用某工具后)
  6. Client → Server:tools/call请求(带工具名和参数)
  7. Server → Client:返回执行结果

下面是一个tools/list请求的 JSON-RPC 消息示例:

{ "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} }

对应的响应:

{ "jsonrpc": "2.0", "id": 1, "result": { "tools": [ { "name": "get_weather", "description": "查询指定城市天气", "inputSchema": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名"} }, "required": ["city"] } } ] } }

五、手写一个 MCP Server

下面用 Python 官方 SDK(mcp包)实现一个最小 MCP Server,暴露两个工具:查天气和算数学。这段代码可以直接跑。

""" 最小 MCP Server 示例。 安装: pip install mcp 运行: python mcp_server_demo.py(作为子进程被 Client 启动) """ from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent import mcp.server.stdio import json import asyncio app = Server("demo-tools-server") # ---- 定义工具 ---- @app.list_tools() async def list_tools() -> list[Tool]: """声明 Server 提供的所有工具(等价于 tools/list 响应)。""" return [ Tool( name="get_weather", description="查询指定城市当天天气。当用户问天气、气温时调用。", inputSchema={ "type": "object", "properties": { "city": { "type": "string", "description": "城市名,如'北京'" } }, "required": ["city"] } ), Tool( name="calculate", description="执行数学计算表达式。", inputSchema={ "type": "object", "properties": { "expression": { "type": "string", "description": "数学表达式,如'3*8+2'" } }, "required": ["expression"] } ) ] @app.call_tool() async def call_tool(name: str, arguments: dict) -> list[TextContent]: """执行被调用的工具(等价于 tools/call 响应)。""" if name == "get_weather": city = arguments.get("city", "") # 模拟天气数据(实际接 API) mock = {"北京": "晴 25°C", "上海": "多云 28°C", "深圳": "雷阵雨 30°C"} result = mock.get(city, f"未找到{city}的天气") return [TextContent(type="text", text=f"{city}今天天气: {result}")] elif name == "calculate": expr = arguments.get("expression", "") try: # 注意:生产环境不要用 eval,这里仅演示 result = eval(expr, {"__builtins__": {}}, {}) return [TextContent(type="text", text=f"{expr} = {result}")] except Exception as e: return [TextContent(type="text", text=f"计算失败: {e}")] else: return [TextContent(type="text", text=f"未知工具: {name}")] async def main(): """通过 stdio 启动 Server。""" async with stdio_server() as (read_stream, write_stream): await app.run(read_stream, write_stream, app.create_initialization_options()) if __name__ == "__main__": asyncio.run(main())

代码要点解读:

  1. @app.list_tools()装饰器注册工具声明回调,Client 发tools/list时触发,返回工具 Schema 列表。
  2. @app.call_tool()装饰器注册工具执行回调,Client 发tools/call时触发,按 name 路由到对应逻辑。
  3. stdio_server()用标准输入输出做传输,Host 以子进程方式启动这个 Server,通过管道收发 JSON-RPC 消息。
  4. 返回值是TextContent列表——MCP 工具结果支持多种内容类型(text、image、resource),最常用的是 text。

六、MCP Client 侧:Host 怎么用

Host 侧(Client)的工作是:启动/连接 Server → 获取工具列表 → 把工具发给 LLM → LLM 决定调用时转发给 Server → 回填结果。下面是 Client 侧的核心逻辑(伪代码示意流程):

""" MCP Client 侧流程示意(伪代码,展示协议交互)。 """ import asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def run_client(): # 1. 定义如何启动 Server(stdio 模式) server_params = StdioServerParameters( command="python", args=["mcp_server_demo.py"], ) # 2. 启动 Server 子进程并建立会话 async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: # 3. 初始化握手 await session.initialize() # 4. 获取工具列表 tools_result = await session.list_tools() print("可用工具:", [t.name for t in tools_result.tools]) # 5. 把工具 Schema 转成 LLM 的 tools 格式 llm_tools = [] for tool in tools_result.tools: llm_tools.append({ "type": "function", "function": { "name": tool.name, "description": tool.description, "parameters": tool.inputSchema, } }) # 6. 调用 LLM(此处用伪代码) # user_msg = "北京天气怎么样?" # llm_response = llm.chat(messages=[...], tools=llm_tools) # if llm_response.tool_calls: # for tc in llm_response.tool_calls: # 7. 转发工具调用给 MCP Server result = await session.call_tool("get_weather", {"city": "北京"}) print("工具结果:", result.content[0].text) # 8. 把 result 回填给 LLM,LLM 生成最终回答 if __name__ == "__main__": asyncio.run(run_client())

这段伪代码展示了 MCP 的完整数据流:Client 拿到 Server 的工具列表后,转成 LLM 能理解的格式发给模型;模型决定调用工具时,Client 通过session.call_tool转发给 Server;Server 执行后结果回到 Client,再回填给 LLM。MCP 在这里扮演的就是"标准化中间层"。

七、MCP vs Function Calling vs LangChain Tools

面试常考三者的定位差异:

| 维度 | Function Calling | LangChain Tools | MCP | |---|---|---|---| | 本质 | 模型 API 能力 | 框架封装 | 开放协议标准 | | 工具定义 | 各家 API 私有格式 | Python 类装饰器 | JSON-RPC + JSON Schema | | 运行位置 | 与 LLM 同进程 | 与 Agent 同进程 | 独立进程/远程 | | 跨语言 | 否(绑定特定 API) | 否(绑定 Python) | 是(协议级跨语言) | | 工具复用 | 同 API 内复用 | 同项目内复用 | 全生态复用(一次编写处处可用) | | 动态发现 | 不支持 | 不支持 | 支持(运行时 list_tools) | | 适用阶段 | 单模型快速接入 | LangChain 生态内 | 多工具、多客户端、跨平台 |

一句话总结:Function Calling 是模型层的能力,LangChain Tools 是框架层的封装,MCP 是协议层的标准。三者不互斥——MCP Server 暴露的工具,最终在 Host 里还是通过 Function Calling 让 LLM 调用的,MCP 只是标准化了"工具怎么被发现和传输"。

八、面试高频追问与答题模板

追问1:MCP 解决了什么问题?答:M × N 集成碎片化问题。M 个 AI 应用 × N 个工具源,每个组合都要写集成代码。MCP 引入标准协议中间层,工具方写一次 Server、应用方写一次 Client,通过 JSON-RPC 通信,成本降为 M + N。类比 USB-C 统一接口。

追问2:MCP 的 Tools 和 Resources 有什么区别?答:Tools 是"可执行操作",有副作用(写文件、发消息、调 API),等价于 POST 请求;Resources 是"可读数据",幂等无副作用(读文件、查数据库),等价于 GET 请求。LLM 通过 Tools 做事,通过 Resources 获取上下文。

追问3:MCP Server 怎么被发现的?答:两种模式。一是静态配置(Host 配置文件里写明 Server 启动命令或 URL);二是动态发现(SSE 模式下 Server 注册到注册中心,Client 查询发现)。目前主流是静态配置,动态发现仍在发展中。

追问4:MCP 的安全性怎么保证?答:三层。传输层:stdio 本地隔离或 SSE 加 TLS;权限层:Server 可声明工具需要的权限,Client 授权后才调用;执行层:Server 在自己进程内执行,可加沙箱。敏感操作(删文件、转账)应加用户确认环节。MCP 规范本身没有定义完整的权限模型,目前靠实现方自律。

追问5:为什么不用 OpenAPI/Swagger 而要搞 MCP?答:OpenAPI 是给"人调 API"设计的,描述 HTTP 端点;MCP 是给"LLM 调工具"设计的,描述工具语义、支持动态发现和双向通信(Server 可以主动推送资源更新)。OpenAPI 缺少 LLM 需要的工具语义描述(什么时候该用这个工具),MCP 的 description 字段正是为此设计。

九、MCP 生态现状与工程建议

截至 2026 年,MCP 生态快速扩张。Anthropic 官方维护了一批参考 Server(filesystem、github、slack、postgres 等),社区贡献了数百个第三方 Server。Claude Desktop、Cursor、Zed 等已原生支持 MCP Client。

工程落地建议:

  1. 工具少(<5个)、单应用:直接 Function Calling 够用,上 MCP 是过度设计。
  2. 工具多、要多应用复用:上 MCP,写一次 Server 多个 Host 通用。
  3. 跨语言团队:MCP 协议级跨语言,Python Server 配 TS Client 没问题。
  4. 远程部署:用 SSE 模式,Server 独立部署,多 Client 共享。
  5. 安全:生产环境务必加权限校验和操作确认,MCP 规范不强制安全模型。

MCP 的价值在于"标准化"——它不引入新的模型能力,而是让工具生态从"私有花园"走向"开放市场"。对 Agent 工程师来说,理解 MCP 的协议设计和实现方式,是 2026 年面试的必备项。下一篇我们进入 Agent 的规划与任务分解,讲 Plan-Execute 范式。