老张开了一家“AI餐厅”,迎来的顾客都是大模型——个个聪明绝顶,可点餐时却总是鸡同鸭讲。有的顾客想看菜单(读数据文件),有的要用厨房的烤箱(调API),有的想让老张推荐招牌菜(提示词模板)。问题是:每位顾客都有自己的“暗号”,厨房里每台设备也各有脾气,老张每天忙得焦头烂额。直到他雇了一位“全能服务生”——MCP。这位服务生通晓一套标准话术:顾客只需用统一的方式说出“我要什么”,服务生就用标准化指令跑向后厨,把菜端回来,管他后面是燃气灶还是电磁炉。MCP(Model Context Protocol,模型上下文协议)就是这样一套让大模型与外部世界对话的通用语言。它定义了资源、工具、提示词三大原语,通过统一的通信规则连接文件、数据库和API,让Agent既能“看得见”数据,又能“使唤”工具,还能“记住”套路。这篇文章,我们就从老张的餐厅出发,把MCP的架构、通信、传输与开发讲透。

岗位关联

掌握MCP是进阶Agent开发者的关键一步:它让零散的Function Calling整合成可复用的开放协议,让你的智能体从“单个应用会调工具”升级为“任何应用都能安全连接外部世界”。

目录
  1. 可视化演示
  2. 协议定位:AI世界的万能插座
  3. 三层架构:餐厅里的铁三角
  4. 三大原语:菜单、厨具与招牌推荐
  5. 通信机制:服务生点单的规则书
  6. 传输层选型:面对面还是电话下单
  7. 对比Function Calling:专属服务生与行业标准
  8. 开发MCP Server:开一家新餐厅
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示Model Context Protocol的Client-Server架构,动态演示资源/工具/提示词三种原语的数据流转,交互切换模式理解MCP协议通信机制。

本节配有交互动效(进入视口后自动加载,约 3KB)。

资源原语:文件/数据源访问 紫色为Client(LLM Host) 粉色为Server(资源提供方) 浅紫为MCP协议层 Protocol: JSON-RPC 2.0 over stdio/SSE MCP定义资源原语让Agent安全访问文件系统和数据源,通过JSON-RPC协议通信

协议定位:AI世界的万能插座

老张的餐厅最初一团乱麻:厨房里的冰箱、烤箱、收银机各有各的“脾气”,新来的顾客(也就是大模型)想取食材,得挨个学设备暗号。更麻烦的是,今天来个GPT顾客,明天来个Claude顾客,每个顾客的“方言”还不一样。老张算了一笔账:店里添置一台新设备,就得多教N个老顾客学会用;来一位新顾客,又得多学M台设备。很快他就发现,这种“谁都要适配谁”的打法根本撑不住。

他琢磨出的解法,是立一条《标准设备接口》:设备统一换成“国标插头”,顾客只需认准插座。这就是MCP的核心理念——把“模型与外部世界”的连接,从两两直连变成统一的一层中介。MCP全称Model Context Protocol(模型上下文协议),由Anthropic主导开源,任何一个模型、工具只要实现这套协议,就能自动互认。

具体来说,MCP定义了资源、工具、提示词三种原语,以及JSON-RPC消息通信规则。对模型而言,它看到的不是五花八门的REST端点,而是统一格式的工具清单;对工具方而言,写一次Server,就能被所有主流AI客户端自动发现和调用。于是集成题目从“每个模型对接每套工具”变成了“大家各自对接协议”,这就是乘法与加法的差别。

老张后来在餐厅门口挂了一块牌子:“本店支持标准插头。”从此购置新烤箱,再也不用拉着顾客逐个培训;来了新顾客,也只需认准插座。你如果做过Agent开发,一定体会过那种“换个模型就要重写一遍工具层”的痛——MCP把这层痛,变成了装一次插座的事。

Costbefore=O(N×M)⟶Costafter=O(N+M)\displaystyle \text{Cost}_{\text{before}} = O(N \times M) \quad \longrightarrow \quad \text{Cost}_{\text{after}} = O(N + M)
N代表模型数量,M代表工具数量。直连时代每加一个模型或工具,工作量都要翻好几倍;有了MCP这个“中转插座”,两边各接一次即可。
  1. N 的含义N表示需要接入的大模型数量,这是集成成本的一个维度。
    N=模型数量\displaystyle N = \text{模型数量}
  2. M 的含义M表示需要连接的外部工具或数据源数量,是另一个维度。
    M=工具数量\displaystyle M = \text{工具数量}
  3. 直连成本每个模型对接每个工具都要单独写适配,总量是笛卡尔积。
    O(N×M)\displaystyle O(N \times M)
  4. 协议中介后模型侧只需连一次MCP,工具侧也只需实现一次协议,成本变成相加。
    O(N+M)\displaystyle O(N + M)

三层架构:餐厅里的铁三角

老张的餐厅装了标准点餐系统后,整个运营由三部分组成。顾客坐着的大堂叫Host——它是顾客(AI)所处的整个应用环境;在大堂里穿梭、负责传话的服务员叫Client;后厨里把所有设备管起来的服务台叫Server。服务员听不懂设备暗号没关系,服务台听得懂就好,三方各管一摊,缺一不可。

Host是承载用户交互的应用本体,比如Claude Desktop桌面客户端、Cursor之类的IDE、或者你正在开发的Agent框架。Host负责管理多个Client的创建与销毁,决定什么时候启动一个Server会话。Client是SDK里的一个模块,它负责与Server建立连接、完成初始化握手,并把用户意图翻译成协议消息。Server则是一个独立的进程或服务,真正去访问文件、数据库和API,把结果加工成模型能懂的格式。

三者的职责边界可以这样记:Host管“什么时候连、连几个”;Client管“怎么把话说标准”;Server管“话到了以后干什么”。一次连接的完整生命周期包括四步:initialize(双方自我介绍、协商协议版本)、capability exchange(宣布各自支持的能力)、operation(列表、调用、读取等操作)、shutdown(优雅关闭)。

大堂、服务员、服务台各司其职,老张的餐厅终于有条不紊。你再看任何一套MCP实现,脑子里自然分成这三层:谁在接待用户(Host),谁在传话(Client),谁在管理后厨设备(Server)。分清这三个角色,读任何MCP项目源码都不会迷路。

Tsession=Tinit+K×(Tcall+Ttransport)\displaystyle T_{\text{session}} = T_{\text{init}} + K \times (T_{\text{call}} + T_{\text{transport}})
一次MCP会话的总耗时,等于握手时间加上K次工具调用的时间与传输时间之和。日常开发中K往往远大于1,所以减少无效轮询比优化握手更有意义。
  1. T\_init 的含义初始化握手耗时,包含双方交换协议版本和能力的开销。
    Tinit=Thandshake+Tnegotiation\displaystyle T_{\text{init}} = T_{\text{handshake}} + T_{\text{negotiation}}
  2. K 的含义一次会话中调用工具的总次数。
    K=工具调用次数\displaystyle K = \text{工具调用次数}
  3. T\_call 的含义单次请求与响应的处理时间。
    Tcall=单次调用处理时间\displaystyle T_{\text{call}} = \text{单次调用处理时间}
  4. T\_transport 的含义消息在传输层上花费的时间。
    Ttransport=传输时延\displaystyle T_{\text{transport}} = \text{传输时延}

三大原语:菜单、厨具与招牌推荐

服务台正式开业后,老张给它定了三样“库存”。第一样是菜单——餐厅里有哪些食材和存货,对应的就是MCP里的Resources(资源)。第二样是厨具——烤箱、炸锅、搅拌机这些能干活的设备,对应Tools(工具)。第三样是招牌菜推荐卡——顾客一犯选择困难,服务员就递上一张“就点这些”的模板,对应Prompts(提示词)。

Resources让模型“看得见”数据。每个资源有一个URI作为唯一地址,例如file:///docs/notes.txt,再用MIME类型标明它是文本还是JSON。模型可以列表、按需读取,甚至订阅更新,但不能直接改写,它是只读快照。Tools让模型“使得动”能力。一个get_weather工具,模型传入city参数,Server执行后返回结构化JSON,这是有副作用的操作。Prompts则是一套“协作套腔板”,告诉模型遇到某一类任务时按固定结构组织输出,帮开发者少写重复提示词。

这三种原语搭配起来,覆盖了Agent的绝大多数需求:读资料靠Resources、动世界靠Tools、定套路靠Prompts。除了语义本身,协议还约定了错误处理:成功返回result,失败返回error对象(含code和message),让客户端能区分“参数错了”和“服务崩了”。

老张从此不看设备、只看标签:菜单上写着“今日有鲜鱼”(Resource),厨具架上贴着“烤箱可烤鱼”(Tool),服务员手里有一叠“烤鱼推荐卡”(Prompt)。原语就是标签,把千奇百怪的外部世界,翻译成AI能理解的三种类型。你写的MCP Server,本质上就是给真实世界贴这三类标签。

Resource=⟨URI,MimeType,Content⟩\displaystyle \text{Resource} = \langle \text{URI}, \text{MimeType}, \text{Content} \rangle
资源原语本质是一个三元组:地址、格式、内容。URI决定了“去哪找”,MIME决定了“怎么读”,Content是真正返回给模型的数据。
  1. URI 的含义资源在协议世界中的唯一地址,形如 scheme://path。
    URI=scheme://path\displaystyle \text{URI} = \text{scheme}://\text{path}
  2. MimeType 的含义描述资源内容格式,帮助客户端正确解析。
    MimeType∈{text/plain,application/json,…}\displaystyle \text{MimeType} \in \{ \text{text/plain}, \text{application/json}, \ldots \}
  3. Content 的含义资源实际携带的数据内容。
    Content=data stream\displaystyle \text{Content} = \text{data stream}

通信机制:服务生点单的规则书

有了原语还缺一本“话术手册”。老张规定:服务员和顾客之间必须用统一格式对话。顾客说“请帮我看下A套餐还有吗”,必须报上自己的桌号;服务员回话时也要核对同一桌号。这样一来,哪怕餐厅嘈杂、几十桌同时点单,服务员也绝不会把A套餐端到B桌。这个“桌号”,就是JSON-RPC消息里的id。

JSON-RPC 2.0是MCP的底层消息协议。每条消息都是合法的JSON对象:jsonrpc固定为"2.0",method是方法名,params是参数对象,id是请求的唯一标识。服务端处理完后,响应里带着相同的id,让客户端知道“这盘菜是哪桌点的”。MCP规定了一组固定的方法名:initialize、tools/list、tools/call、resources/read、prompts/get等,整个交互就是围绕这些方法展开的。

通信中还有两类特殊消息:notification(通知)没有id,发送方不期待任何回复,常用于日志、进度更新;request必须有id,必须等到响应。这种“有号必回、无号不回”的规则看似简单,却保证了复杂并发场景下的消息可追踪性。

话术手册一统一,新来的服务员培训三天就能上岗。JSON-RPC就像手册里最基础的“通用句式”——不管点菜还是退菜,格式都是:桌号(id)+动词(method)+内容(params)。以后你在SDK里看到请求日志,能一眼认出这个句式,就掌握了MCP的沟通密码。

Message={jsonrpc:2.0,  id:N∪{∅},  method:String,  params:Object}\displaystyle \text{Message} = \{ \text{jsonrpc}: 2.0, \; \text{id}: \mathbb{N} \cup \{\varnothing\}, \; \text{method}: \text{String}, \; \text{params}: \text{Object} \}
JSON-RPC消息是一个四字段对象。id是自然数或空;当id为空时,这条消息就是“发出去不用等回复”的通知,这个设计让协议同时支持同步调用和异步广播。
  1. jsonrpc 字段固定为2.0,标明遵循的消息协议版本。
    jsonrpc:2.0\displaystyle \text{jsonrpc}: 2.0
  2. id 字段请求的唯一编号,用于把响应与请求配对。
    id=request 唯一标识\displaystyle \text{id} = \text{request 唯一标识}
  3. method 字段要调用的协议方法名,如 tools/call。
    method∈{tools/list,tools/call,…}\displaystyle \text{method} \in \{ \text{tools/list}, \text{tools/call}, \ldots \}
  4. params 字段方法所需参数对象,如工具名与参数值。
    params:{name,arguments}\displaystyle \text{params}: \{ \text{name}, \text{arguments} \}

传输层选型:面对面还是电话下单

话术定了,话怎么说出去?老张店里有两种方式:大堂内的传达室,服务员和顾客同处一屋,喊一声就能听到(本地进程通信);或者通过电话和外卖平台,顾客身处千里之外也能远程下单(网络通信)。两种方式各有舞台:堂食靠面对面,外送靠电话。

MCP最初支持的传输方式是stdio:Client在本地启动一个Server子进程,双方通过标准输入输出流传递JSON消息。这种方式零网络开销、部署简单,适合本地文件系统工具、命令行工具封装,Claude Desktop与本地脚本的通信就常走这条路。后来协议扩展了Streamable HTTP:Server暴露为一个HTTP Endpoint,Client通过POST发消息,用GET/SSE接收流式响应,适合访问云端数据库和第三方API。

Streamable HTTP比早期SSE方案更进一步,支持双向流:客户端可按需创建、更新或关闭流,服务端还能通过SSE持续推送进度。选型时主要看三点:进程隔离需求(需要沙箱隔离就选stdio);部署距离(跨机器、跨机房必须HTTP);任务时长(长耗时任务配HTTP+SSE流式,避免一个请求挂半天)。

老张最后给店里配了“双通道”:大堂直接喊(stdio),外卖平台走电话(HTTP)。MCP的传输层也类似——本地用管道,跨网用协议,选型从来不是谁更好,而是谁更合适。你接手一个MCP项目时,先问一句“Server跑在哪”,答案基本就定了传输方式。

Tstdio=Tproc+Tio,Thttp=Tdns+Tconn+2R×Tlatency+Tio\displaystyle T_{\text{stdio}} = T_{\text{proc}} + T_{\text{io}}, \qquad T_{\text{http}} = T_{\text{dns}} + T_{\text{conn}} + 2R \times T_{\text{latency}} + T_{\text{io}}
stdio的耗时只有进程启动加本地IO;HTTP则要叠加DNS解析、连接建立和网络往返。R是往返次数,流式响应能把R从“轮询N次”降到“一次长连接”,这就是它适合长任务的原因。
  1. T\_proc 的含义本地启动子进程的耗时,通常几十毫秒。
    Tproc=进程启动耗时\displaystyle T_{\text{proc}} = \text{进程启动耗时}
  2. T\_conn 的含义建立HTTP连接(含DNS与握手)的耗时。
    Tconn=Tdns+Thandshake\displaystyle T_{\text{conn}} = T_{\text{dns}} + T_{\text{handshake}}
  3. R 的含义一次响应所需的网络往返次数。
    R=网络往返次数\displaystyle R = \text{网络往返次数}
  4. 流式减少 RSSE流式让R趋近于1,远小于轮询的R值。
    Rstream≪Rpolling\displaystyle R_{\text{stream}} \ll R_{\text{polling}}

对比Function Calling:专属服务生与行业标准

老张的餐厅火了之后,隔壁的“AI快餐店”也学他搞标准话术。可快餐店是小本经营,只培训自家服务员认自家菜单,顾客换家店就得重新学。老张则用的是行业统一的“厨师证照”,服务员在哪家店都能照常点单。这个故事里的快餐店,就是Function Calling。

Function Calling是OpenAI等模型厂商在模型层内置的工具调用能力:模型输出一段结构化JSON,声明“我要调用函数X,参数是Y”,然后由应用代码执行它。它很强大,但有个棘手的问题:它是“私有话术”。你在OpenAI上写的工具函数迁移到Claude时要重写,换个应用又要重新接一遍,每个工具都要根据模型厂商的规范单独适配。

MCP则把工具调用从“模型层”解耦到“应用层”。MCP不依赖某个模型厂商支持特定格式,它只是Client与Server之间的一套标准对话。模型看到Tools列表,只是把它当作上下文的一部分;真正触达外部世界的是MCP Client。于是同一个MCP Server可以被Claude用,也可以被Cursor用,甚至被你自己的Python脚本用。两者其实是互补关系:模型层仍需Function Calling来产出结构化输出,而MCP负责把上层应用和下层工具统一接起来。

快餐店的服务员在自己店里很熟练,但老张的服务生走到哪都能上岗。这就是协议标准的魅力——MCP不是要取代Function Calling,而是把“点单语言”从各家方言统一成普通话。面试时被问到二者关系,记住一个词:互补,而非替代。

NFC=M,NMCP=1\displaystyle N_{\text{FC}} = M \quad \text{,} \quad N_{\text{MCP}} = 1
用纯Function Calling接M个工具,就攒M套私有接入逻辑;用MCP接M个工具,只实现一次协议。前者是“各写各的”,后者是“一次注册、到处复用”。
  1. FC 集成数每接一个工具,都要按模型厂商的私有规范写一套适配。
    NFC=M\displaystyle N_{\text{FC}} = M
  2. MCP 集成数所有工具通过统一协议接入,客户端只需实现一次。
    NMCP=1\displaystyle N_{\text{MCP}} = 1

开发MCP Server:开一家新餐厅

明白了原理,老张决定开一家新餐厅——开发自己的MCP Server。开餐厅第一步是装修厨房、备齐厨具,对应到MCP就是搭好开发环境、装好SDK。Python下最省事的方案是官方SDK中的FastMCP,几行代码就能起一个Server。

工具注册是Server开发的重头戏。用装饰器声明一个函数后,SDK会自动采集函数名、docstring和参数类型,生成JSON Schema形式的工具元数据,客户端通过tools/list就能发现它。这就像给厨房每道菜贴标签:菜名、做法、需要哪些食材,全写在牌子上,顾客看牌子就能点。

暴露资源则把数据文件挂到URI上,让模型能按地址读取。权限设计同样关键:敏感操作要加确认机制,参数要做白名单校验,防止模型在用户未授权时写入文件或调用危险API。SDK默认支持stdio和Streamable HTTP两种传输,写完业务逻辑后调用mcp.run()即可启动。开发一个Server的核心工作,就是把已有的业务能力(读数据库、调第三方API、操作文件)原语化“翻译”出去。

再开一家餐厅,按统一标准装修、挂好菜单、培训服务生,立刻就能接入老张的AI生态。MCP Server开发,本质就是一次“标准话术翻译”——把内部能力翻译成协议语言,剩下的交给Client去发现和调用。动手写第一个Server时,从注册一个天气工具开始,你会立刻感受到“被任何客户端发现”的爽快。

Tcall=Tparse+Tdispatch+Tverify+Texec\displaystyle T_{\text{call}} = T_{\text{parse}} + T_{\text{dispatch}} + T_{\text{verify}} + T_{\text{exec}}
一次工具调用的总时间,由解析、路由、校验、执行四段组成。业务代码只占T\_exec,剩下三段是协议层与安全层开销——这提醒你:Server写得好不好,校验部分的比重是关键。
  1. T\_parse 的含义解析JSON-RPC消息与参数的时间。
    Tparse=消息解析耗时\displaystyle T_{\text{parse}} = \text{消息解析耗时}
  2. T\_dispatch 的含义根据method名路由到对应工具函数的时间。
    Tdispatch=路由查找耗时\displaystyle T_{\text{dispatch}} = \text{路由查找耗时}
  3. T\_verify 的含义执行权限与参数合法性校验的时间。
    Tverify=安全校验耗时\displaystyle T_{\text{verify}} = \text{安全校验耗时}
  4. T\_exec 的含义实际执行业务逻辑的耗时。
    Texec=业务执行耗时\displaystyle T_{\text{exec}} = \text{业务执行耗时}

AI 应用场景

企业知识库接入智能客服

阶段:Agent工具调用与数据接入

解决的问题:传统Embedding+RAG的方案,每次检索都要维护一套私有管线,换个前端应用就要重新对接,客服Agent无法直接复用。用MCP Server封装企业知识库后,把FAQ、产品文档作为Resources暴露,同时注册一个search_docs工具,任何支持MCP的客户端都能直接调用,不必为每家客服系统重写检索层。

具体案例:某SaaS公司把内部产品文档挂到自建的MCP Server上,客服Agent通过resources/read实时拉取最新说明文档,遇到不确定的问题再用search_docs做语义检索。新文档上传后Agent次日即可感知,不再需要把文档硬编码进提示词——就像老张把新菜挂上菜单,服务员马上能照着推荐。

数据分析Agent连接多类数据库

阶段:工具执行与多数据源集成

解决的问题:过去每接一个数据库就要写一套SQL工具,Agent要适配不同方言。现在用一个MCP Server聚合同一部门的PostgreSQL和MySQL,通过统一的tools/call接口暴露query_data,屏蔽方言差异。

具体案例:BI Agent向MCP Server发出“上月华东区销售额Top10”的自然语言请求,Server内部把它翻译成对应数据库的SQL并执行,返回Markdown表格。Agent不需要知道数据存在哪台机器、用的什么方言,只要认准MCP这一个“服务台”。

Python 演示

python86 行
"""迷你MCP演示:实现简化版MCP Server的工具注册与JSON-RPC消息处理基于正文第4节通信机制和第7节Server开发思路"""import json  class MCPServer:    def __init__(self, name):        self.name = name        self.tools = {}       # 工具名 -> {desc, fn}        self.resources = {}   # URI -> content        self.free_lists = {}  # 空闲资源登记表     def register_tool(self, name, description, fn):        self.tools[name] = {"desc": description, "fn": fn}     def add_resource(self, uri, content):        self.resources[uri] = content     def handle_request(self, raw_msg):        try:            msg = json.loads(raw_msg)        except json.JSONDecodeError:            return {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}}        if not isinstance(msg, dict) or msg.get("jsonrpc") != "2.0":            return {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}}        msg_id = msg.get("id")        method = msg.get("method", "")        params = msg.get("params", {})        if method == "tools/list":            return {"jsonrpc": "2.0", "result": list(self.tools.keys()), "id": msg_id}        if method == "resources/read":            uri = params.get("uri")            if uri not in self.resources:                return {"jsonrpc": "2.0", "error": {"code": -32602, "message": "Resource not found"}, "id": msg_id}            return {"jsonrpc": "2.0", "result": self.resources[uri], "id": msg_id}        if method == "tools/call":            tool_name = params.get("name")            args = params.get("arguments", {})            if tool_name not in self.tools:                return {"jsonrpc": "2.0", "error": {"code": -32602, "message": "Tool not found"}, "id": msg_id}            try:                result = self.tools[tool_name]["fn"](**args)                return {"jsonrpc": "2.0", "result": {"content": str(result)}, "id": msg_id}            except Exception as e:                return {"jsonrpc": "2.0", "error": {"code": -32603, "message": str(e)}, "id": msg_id}        return {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": msg_id}  server = MCPServer("demo-server")server.add_resource("file:///data/sales.json", '{"month": "2026-01", "sales": 128000}')  def register(name, desc):    def deco(fn):        server.register_tool(name, desc, fn)        return fn    return deco  @register("get_weather", "获取指定城市的当前天气")def get_weather(city):    data = {"beijing": "晴 5C", "shanghai": "阴 8C"}    return data.get(city, "暂无数据")  @register("add", "两个数字相加")def add(a, b):    return a + b  if __name__ == "__main__":    req = json.dumps({"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}})    print("== 客户端: 列出工具 ==")    print(server.handle_request(req))     req2 = json.dumps({"jsonrpc": "2.0", "id": 2, "method": "tools/call",                       "params": {"name": "get_weather", "arguments": {"city": "beijing"}}})    print("== 客户端: 调用天气工具 ==")    print(server.handle_request(req2))     req3 = json.dumps({"jsonrpc": "2.0", "id": 3, "method": "resources/read",                       "params": {"uri": "file:///data/sales.json"}})    print("== 客户端: 读取资源 ==")    print(server.handle_request(req3))
目的:用30行核心逻辑模拟MCP Server的JSON-RPC消息处理——注册工具、暴露资源、动态分发调用,展示正文第4、7节的协议骨架。
  1. class MCPServer:定义MCP服务端骨架,就是后厨的总服务台。
  2. self.tools = {}工具注册表,保存“工具名→函数”的映射。
  3. def handle_request(self, raw_msg):统一入口,接收JSON-RPC原始消息并路由。
  4. msg = json.loads(raw_msg)把JSON字符串解析成字典,对应“解码顾客的话”。
  5. if method == "tools/list":客户端问“你能干什么”,返回已注册的工具清单。
  6. if method == "tools/call":客户端实际点单,按工具名查找函数并执行。
  7. result = self.tools[tool_name]["fn"](**args)动态调用已注册的工具函数。
  8. return {"jsonrpc": "2.0", "result": ..., "id": msg_id}构造标准响应,id是配对暗号。
  9. @register("get_weather", ...)装饰器注册工具,相当于给厨房贴标签。
  10. if __name__ == "__main__":可执行入口,模拟三个客户端请求。

常见误区

误区看到MCP支持Streamable HTTP,以为它是要取代REST的下一代网络协议,甚至觉得以后所有API都应该改成MCP。

正解MCP定义的是“应用层消息语义”(JSON-RPC RPC风格),它依赖HTTP作为传输而不是取代HTTP。REST是面向资源的无状态风格,MCP是面向会话的RPC式调用。打个比方:HTTP是电话线路,MCP是电话里的礼貌用语,两者层次完全不同。在餐厅里,电话(HTTP)和“您好,请问需要什么”(MCP语义)不是竞争关系,而是配合关系。

为何常见:MCP的传输协议名字里带"HTTP",初学者容易把传输层和协议本身混为一谈。

误区很多人觉得让AI调用工具不就是那点事嘛,Function Calling和MCP只是不同叫法,随便用哪个都行。

正解Function Calling是单个模型厂商在模型层内置的输出规范,属于私有协议;MCP是客户端与服务端之间的开放标准,不绑定任何具体模型。前者是收银台自己的点单规则,后者是能跨餐厅通用的标准话术。实际开发中,你会在一个MCP Server里同时用多个模型,每个模型用自己的Function Calling能力产出JSON,MCP负责传输,二者是分层配合的朋友。

为何常见:它们都涉及“让AI调用工具”,表面看几乎一样,只看到结果没看到层次,自然容易混淆。

误区以为Server里注册好工具,模型就像装上了外挂,瞬间就会正确调用一切。

正解MCP只是把工具变成“可发现、可调用”的管道,模型能不能正确调用,还取决于工具描述是否清楚、参数Schema是否准确、上下文是否足够。工具描述写得烂,模型照样传错参数。就像餐厅挂出菜单,顾客还是会点错菜——菜单只是让点单成为可能,不等于把每个顾客都教成美食家。

为何常见:MCP的宣传图总给人“模型自动连接一切”的错觉,实际它只是配送系统,不是厨师。

误区受“Server=云端服务”的直觉影响,以为MCP天生就是客户端连远程服务,本地不应该装Server。

正解MCP最初的传输模式恰恰是stdio本地子进程——Client在本机拉一个进程,通过标准输入输出通信,这样工具访问本地文件时才能安全可控。远程HTTP模式是后来为跨网络场景扩展的。工具在哪儿,Server就在哪儿:读本机文件的工具走stdio,云端API才走HTTP。

为何常见:MCP新传输模式叫Streamable HTTP,宣传多、叫得响,而stdio像“老黄历”被忽略掉了。

面试题

Q1. MCP协议主要解决什么问题?

单选题 · easy

  • A.统一AI与外部工具的连接标准← 正确答案
  • B.提升大模型训练推理速度
  • C.替换现有的编程语言生态
  • D.优化GPU显存资源利用效率

答案:A。MCP的核心价值是统一“模型↔工具/数据”的连接方式,把N×M的适配成本降为N+M,A正确。训练速度、编程语言、GPU优化都与MCP解决的应用层连接问题无关。

Q2. MCP的三大核心原语不包括?

单选题 · easy

  • A.资源Resources
  • B.工具与函数Tools
  • C.提示词Prompts
  • D.权重Weights← 正确答案

答案:D。MCP定义的原语是Resources(只读数据)、Tools(可执行操作)、Prompts(协作模板)。Weights是模型训练参数,与MCP无关,故选D。原语就像餐厅的菜单、厨具和推荐卡,权重则是厨师脑内的配方。

Q3. MCP中Client与Server之间传递消息使用哪种协议?

单选题 · mid

  • A.JSON-RPC 2.0← 正确答案
  • B.RESTful HTTP
  • C.GraphQL规范
  • D.SOAP XML协议

答案:A。MCP的消息层明确采用JSON-RPC 2.0,固定结构为jsonrpc/id/method/params。REST和GraphQL是Web API风格,SOAP是旧式XML-RPC,它们都不构成MCP的消息语义基础。

Q4. 本地进程间MCP通信通常采用哪种传输方式?

单选题 · mid

  • A.stdio标准输入输出← 正确答案
  • B.Kafka消息队列系统
  • C.WebSocket双向流
  • D.UDP网络广播

答案:A。MCP最早支持的传输是stdio,Client在本地启动Server子进程并通过管道通信。Kafka用于事件流,WebSocket用于浏览器实时双向通信,UDP是无连接不可靠传输,都不符合“本地子进程管道”的定位。

Q5. 相比Function Calling,MCP具备哪些优势?

多选题 · mid

  • A.一个协议可跨应用复用← 正确答案
  • B.不依赖特定模型厂商← 正确答案
  • C.数据传输速度绝对更快
  • D.模型推理效果绝对更优

答案:AB。MCP是开放协议,同一Server可被Claude、Cursor、自研Agent复用,且不绑定厂商,A、B正确。MCP不承诺传输速度或推理质量的提升,C、D属于将“标准统一”偷换成“性能更优”的常见错觉。

Q6. MCP架构中的Host指的是什么?

单选题 · mid

  • A.承载Client的宿主应用← 正确答案
  • B.运行Server的物理机器
  • C.存放工具的容器镜像
  • D.模型的推理服务器

答案:A。Host是用户直接使用的应用,如Claude Desktop或IDE,它内部嵌入MCP Client。运行Server的机器是部署环境,容器镜像是打包产物,推理服务器是模型推理服务,均不是Host。

Q7. MCP中Resources原语的主要作用?

单选题 · easy

  • A.向模型暴露只读数据源← 正确答案
  • B.执行外部函数的功能
  • C.维护提示词模板库
  • D.管理用户会话登录

答案:A。Resources是只读数据原语,通过URI暴露文件、数据库内容等。执行函数是Tools的职责,提示词模板是Prompts的职责,会话登录属于鉴权模块而非原语职责。

Q8. MCP的JSON-RPC消息中id字段的核心作用?

单选题 · hard

  • A.匹配请求与对应响应← 正确答案
  • B.标识客户端网络地址
  • C.对消息进行加密签名
  • D.指定消息的优先级

答案:A。id是请求的唯一编号,响应携带相同id,让客户端在并发场景下准确配对。它不承载网络地址、加密或优先级信息,就像餐厅桌号只用来对菜,不负责定位或加密。

Q9. MCP Server开发时,哪些属于合理的安全设计?

多选题 · extreme

  • A.工具参数做白名单校验← 正确答案
  • B.资源读取前检查访问权限← 正确答案
  • C.密钥明文写在配置文件
  • D.高危操作增加人工确认← 正确答案

答案:ABD。参数白名单、权限检查、人工确认都是MCP Server安全要点。密钥明文写入配置是反模式,应使用环境变量或密钥管理服务。安全设计如同后厨管理:先确认来人身份,再决定能不能进仓库。

Q10. MCP Server中“注册工具”的本质是什么?

单选题 · mid

  • A.登记元数据供客户端发现← 正确答案
  • B.将函数编译为机器指令
  • C.把函数上传到云沙箱
  • D.写入操作系统的注册表

答案:A。注册工具是把函数名、描述、参数Schema登记到工具清单,客户端通过tools/list发现它后再调用。它不涉及编译、上传云沙箱或OS注册表,本质是“贴标签、上菜单”。