MCP

本篇由「面试遇到的问题」拆解合并而来,保留原文与图片。原与 Agent、RAG、Skills 等同篇,现拆为独立文章。下文各 ## 为原面试小节标题。

MCP

智能体和大模型怎么交互、协同?工作流程是什么?(就是MCP与大模型怎么交互的)

交互工作流(The Workflow):

  1. 派发任务 (Task Input): 用户给 Spring Boot 应用发指令:“帮我查一下今天杭州的天气,然后根据天气决定是推荐喝冰美式还是热拿铁,并生成一段发给客户的文案。”
  2. 上报工具单 (Tool Discovery): Spring AI 将用户的提问,连同所有本地注册的工具说明书(JSON Schema),一起打包发给云端的大模型。
  3. 大模型推理与决策 (Reasoning): 大模型分析后认为:“我不知道杭州天气,但我看到应用提供了一个叫 get_weather 的工具。我需要先停下来,要求应用去执行这个工具。”
  4. 模型中断输出 (Tool Call): 大模型不返回自然语言文本,而是返回一个特殊的 JSON 指令:要求调用 get_weather(city=”杭州”)。
  5. 本地执行 (Acting): Spring AI 拦截到这个指令,在本地服务器上利用反射执行你写的 Java 方法查天气,得到结果:“今天杭州 35度,晴”。
  6. 结果回传 (Feedback): Spring AI 将“35度”这个结果,带着之前的上下文,再次发给大模型。
  7. 循环或终结 (Synthesize): 大模型拿到 35 度的数据,继续推理:“天气很热,应该推荐冰美式。” 此时不需要新工具了,大模型直接生成最终的营销文案,Spring AI 将文本返回给用户。

mcp安全性如何保证

这是理解 MCP 安全性最重要的一点:MCP 不是大模型主动来连你的服务器,而是你的本地客户端(比如你的 IDE、你的业务系统)主动去连大模型和本地 MCP Server。

  1. 没有公网暴露的必要:
    • 你的 MCP Server(比如一个读取本地文件的服务)完全可以部署在本地内网(localhost 或企业局域网)。
    • 云端的大模型(如 Claude API)根本不知道、也无法直接访问你的 MCP Server。
  2. “中间人”机制:
    • 所有的通信都是通过**客户端(Client)**作为中转的。
    • 流程是: 客户端问大模型 -> 大模型返回指令(”请调用文件读取工具”) -> 客户端在本地决定是否执行 -> 客户端调用本地 MCP Server -> 客户端把结果发回给大模型。
    • 安全屏障: 客户端(属于用户的资产)拥有最终的控制权和否决权。哪怕大模型发疯了想删除你的根目录,只要客户端不执行,就绝对安全。

调用MCP工具超时了怎么办

“遇到 MCP 工具调用超时,传统的做法往往是直接抛出异常让程序崩溃,或者无脑调大超时时间。但在多 Agent 系统中,这种做法非常脆弱。我的解决思路分为四个递进的层次:

第一层:底层代码防御(斩断无限等待)
首先必须保住系统底线。我在 MCP Client 层面设置了严格的短超时策略(Fail-Fast)(比如 10 秒)。一旦超时,立刻切断连接释放线程。同时,我会引入**熔断机制(如 Sentinel / Resilience4j)**,如果某个工具连续超时,直接熔断降级,防止拖垮整个 Agent 所在的线程池。

第二层:Agentic 级别的优雅处理(利用 LLM 自我纠错)【⚠️ 最核心的亮点】
这是大模型开发与传统开发最大的不同。当捕获到超时异常时,我绝不向上抛出导致程序中断,而是将这个异常**包装成一段自然语言的观察结果(Observation)*返回给 Agent。
比如告诉它:
‘系统提示:查询数据库超时(超过10秒),可能是您的查询范围过大,请尝试缩小时间范围重试。’*
这样,在 ReAct 的下一轮循环中,大模型就能基于这个反馈触发 Self-Correction(自我纠错),主动修改参数(比如把查一年改为查一周)再去调用工具,实现了系统的自愈。

第三层:MCP 服务端性能优化(限制边界)
如果工具频繁超时,往往是工具本身设计太重。我会对 MCP Server 端进行改造:

  1. 强制分页:查询类工具强制要求传入 limit 参数,拒绝全表扫描或返回 MB 级别的超大文本(因为这不仅会超时,还会撑爆 LLM 的上下文窗口 Token)。
  2. 加缓存:对高频重复查询的工具加 Redis 缓存,实现秒回。

第四层:终极架构解耦(长任务异步化)
如果评估下来,这个工具注定就是一个极耗时的任务(比如跑数据分析、生成报表,需要几分钟),那就绝对不能用同步调用去卡住 Agent。
我会将工具重构为**‘异步提交 + 状态轮询’**模式:
把工具拆成两个。Agent 先调工具 A 提交任务,工具 A 立刻返回一个 Task_ID;然后 Agent 拿着这个 ID,每隔一段时间去调工具 B 查询进度。在等待期间,Agent 可以去执行其他子任务,或者直接休眠,彻底解耦了耗时瓶颈。”

你们的 Agent 底层用到多线程了吗?里面调用的那些 MCP 工具,是同步执行的还是异步执行的?”

“在我们的系统中,多线程和并发控制是底层的基石。

结合我的 SOP 架构来看,多线程主要体现在两个维度:
第一是提升并行效率:当‘任务分析 Agent’将大目标拆解为多个独立子任务时,系统会通过线程池(如 Java 的 CompletableFuture)并发拉起多个‘执行 Agent’同时开工,最后进行结果聚合。
第二是非核心链路解耦:比如流程末端的‘日志总结 Agent’,我们强制要求它在独立的后台异步线程池中运行,绝不阻塞主业务链路对用户的快速响应。

另外,需要特别注意一点,因为 Agent 之间是通过**‘共享动态上下文(状态机)’**通信的,在多线程并发读写这个上下文时,我专门做过并发安全处理(比如使用 ConcurrentHashMap 或者通过加读写锁),防止出现数据脏读。

至于工具调用(Tool Calling),我采用的是**‘快慢分离的混合调用策略’**:

  1. 对于耗时在 1~3 秒内的轻量级工具(比如基础数据校验、简单查询),我采用同步调用。让 Agent 线程短暂等待,立刻拿到 Observation 进入下一轮 ReAct 循环,保证逻辑的连贯性。
  2. 但对于那些注定耗时很久的重型 MCP 工具(比如跑批处理、复杂图表渲染),我坚决采用了异步架构。我会将工具拆分为‘提交任务’和‘查询状态’两个接口。执行 Agent 提交任务后立刻拿到一个 Job_ID 释放线程,然后由‘任务监督 Agent’在后台开启多线程定时轮询。

通过这种**‘多线程并发执行 + 快同步慢异步’**的设计,我们的 Agent 系统既保证了 ReAct 推理的严谨性,又最大化了吞吐量,不会因为某个烂工具拖垮整个系统的线程池。”

“工具变成异步了,Agent 拿不到结果没法往下走,这时候该怎么设计架构?”

“如果是一个耗时极长的异步工具,绝对不能让 Agent 的线程去同步阻塞等待结果,这会导致服务器雪崩。我的架构设计参考了业界主流的**‘状态机挂起与唤醒(Suspend & Resume)’**机制。

  1. 状态持久化(挂起):当 Agent 调用异步 MCP 工具并拿到一个 Task_ID 后,我会通过 Prompt 约束让 Agent 暂时中止这一轮的 ReAct 循环。此时,系统会将当前 Agent 的全局上下文(Context)和记忆快照持久化保存到 Redis 中,然后立刻释放当前的服务器工作线程,并给用户返回‘任务处理中’的提示。
  2. 事件驱动或轮询:真正耗时的计算交由底层的独立服务去执行。执行完毕后,通过 MQ 消息队列或者 Webhook 回调通知系统。
  3. 上下文恢复(唤醒):系统收到完成事件后,拿着真实结果去 Redis 中唤醒刚才那个 Agent(恢复它的上下文历史),将结果作为新一轮的 Observation 注入,并重新拉起一个线程继续执行 SOP 的后续步骤(比如进入‘任务总结 Agent’阶段)。

这种**‘事件驱动 + 上下文状态流转’**的设计,不仅完美解决了 LLM 必须等待结果才能推理的悖论,还实现了 Agent 调度系统与底层重型执行工具的彻底解耦,极大提升了系统的并发吞吐量。”

面试官上来问mcp协议,问网络底层是如何实现的

1. 本地实现:基于 stdio (Standard I/O) 的进程间通信

  • 适用场景: 当 MCP Server 和大模型客户端(如 Claude Desktop)运行在同一台机器上时(例如让 Claude 读取本地的 SQLite 数据库或本地文件)。
  • 底层机制:
    • 它不走 TCP/IP 网络栈。客户端(Host)会通过操作系统底层的进程创建机制(如 spawn)启动 MCP Server 作为一个子进程。
    • 两者之间的通信完全通过操作系统的**标准输入(stdin)和标准输出(stdout)**管道来完成。
    • 数据流向: 客户端向子进程的 stdin 写入 JSON 数据;服务端处理后,将结果写入自己的 stdout,客户端再从管道中读取。
  • 设计考量(向面试官展示架构思维): 为什么要设计 stdio?因为极致的安全和零配置。它不需要在本地机器上开放任何网络端口,避免了防火墙拦截和本地端口冲突,非常适合桌面端 AI 应用。

2. 远程网络实现:基于 HTTP POST + SSE 的双通道通信

  • 适用场景: 当 MCP Server 部署在云端或企业内网(例如连接企业内部的飞书 API、GitLab 或远程数据库)时。
  • 底层机制: 这是一道极佳的系统设计题。MCP 没有选择传统的 WebSocket 来做全双工通信,而是采用了一种**“双通道(Dual-Channel)”**的 HTTP 架构:
    • 通道 A(服务端到客户端的推送):采用 SSE (Server-Sent Events)。
      客户端发起一个 HTTP GET 请求到 Server,建立一条持久的单向 HTTP 链接。Server 通过 text/event-stream 格式,不断地向客户端推送事件和响应。
    • 通道 B(客户端到服务端的请求):采用 HTTP POST。
      当客户端建立 SSE 链接时,Server 会在首次推送中,下发一个专门用于接收消息的 endpoint URL(例如 /message?session_id=123)。客户端要发请求给 Server 时,就对这个 URL 发送标准的 HTTP POST 请求。
  • 设计考量(惊艳面试官的切入点):为什么用 HTTP+SSE 而不用 WebSocket?
    • 基础设施兼容性: WebSocket 在很多企业级网关、负载均衡器(Nginx/HAProxy)和防火墙中经常遇到连接超时掐断、代理配置复杂的问题。而 HTTP POST 和 SSE 都是纯粹的、符合 RESTful 语义的 HTTP 流量,对现代云原生基础设施(K8s, API Gateway)100% 友好。
    • 调试友好: HTTP 报文比 WebSocket 帧更容易被抓包工具(如 Wireshark, Charles)截获和调试。

“总结来说,MCP 的底层实现是一种**‘接口与传输解耦’**的优秀设计。
对于本地桌面级应用,它利用 stdio 管道实现安全的、无端口冲突的进程间通信;
对于云端分布式应用,它利用 HTTP POST + SSE 的双通道机制,在保证服务端具备实时推送能力的同时,最大程度地妥协和兼容了现有的企业级 Web 基础设施。


MCP如何与大模型进行交互的,和function call的交互过程一样吗

1. Function Calling 的交互过程(没有 MCP)

这是直连模式。

  1. 定义:你在 Java/Python 代码里写好 get_weather 函数。
  2. 注册:把函数描述塞给 LLM。
  3. LLM 思考:输出 JSON {“call”: “get_weather”, “args”: …}。
  4. 执行:你的主程序 拦截 JSON,执行本地代码。
  5. 反馈:你的主程序 把结果塞回 LLM。
  • 特点:工具代码和主程序是死绑定的。

2. MCP 的交互过程(有 MCP)

这是代理/中介模式。

  1. 连接:你的主程序(MCP Client)连接到一个独立的 MCP Server(比如运行在 8080 端口)。
  2. 发现:MCP Server 告诉 Client:“我有 get_weather 工具”。
  3. 注册:Client 把这个工具转发给 LLM。
  4. LLM 思考:输出 JSON {“call”: “get_weather”, …}(这步和上面一模一样)。
  5. 转发执行(关键差异):
    • 你的主程序 不执行代码!
    • 你的主程序 把请求打包成 MCP 协议包,发给 MCP Server。
    • MCP Server 在它的进程里执行代码。
  6. 反馈:MCP Server 返回结果给 Client -> Client 给 LLM。

是不是又了Skills就不用MCP了

  • Skills (工具/函数调用): 就像是给 AI 大脑配备的定制机械臂。
  • MCP (模型上下文协议): 就像是 IT 界的 USB-C 接口标准。

一、 核心定位的区别

1. Skills / Tools / Function Calling(定制化能力)

  • 它是什么: 它是大模型本身的一种底层能力。比如在 Spring AI 或 LangChain 中,你写了一个 Java 方法 @Tool public String getWeather(String city)。你告诉大模型:“你有这个方法可用。” 大模型思考后决定调用它,你的代码执行完毕后,把结果塞回给大模型。
  • 局限性(痛点): 它是高度定制、强耦合的。你在 Spring Boot 项目里写了一个查数据库的 Skill,这个 Skill 只能在这个项目里被你的代码调用。如果你今天换成了 Claude 官方的桌面客户端,或者换用了 Cursor 这个 IDE,它们根本无法使用你用 Java 写的这个天气 Skill,除非你为它们重新写一遍适配插件。

2. MCP (Model Context Protocol)(标准化协议)

  • 它是什么: 这是 Anthropic(Claude的母公司)提出的一种开放标准的客户端-服务器协议。它不是具体的代码逻辑,而是一套类似于 HTTP 或 REST 的通信规范。
  • 它的伟大之处: 它旨在解决上面提到的“各自为战”的痛点。你只需要按照 MCP 的标准,把你的查数据库逻辑封装成一个 MCP Server。
    • 从此以后,任何支持 MCP 的客户端(无论是 Claude Desktop 客户端、Cursor 编辑器、还是你自己写的 Spring AI 后端程序),只要插上这个“USB-C 接口”,就可以直接无缝调用这个数据库工具。
    • 一次编写,到处接入。 极大地降低了生态适配的成本。

面试/交流话术总结

“Skills 解决的是 ‘AI 能不能做事’ 的问题,它是我们具体实现的业务逻辑;
而 MCP 解决的是 ‘这些能力如何被标准化复用’ 的问题,它是一个类似 USB-C 的通用协议。

如果我们只是在自己封闭的系统内开发特定的 AI 功能,直接使用原生框架的 Skills/Tools 调用是最轻量高效的。但如果我们希望打造一个能够跨越各种终端(如 IDE、桌面客户端、不同语言的后端服务)的通用 AI 工具链,那么将这些 Skills 封装进 MCP Server 就是未来的必然趋势。”

function call和MCP的模型绑定的区别,加上skills把

1. Function Calling(函数调用):底层的“引擎能力”

这是大模型(如 GPT-4、Claude 3)本身具备的一种原生 API 能力。

  • 本质:它是模型的一种结构化输出能力。模型不会自己执行代码,它只是通过训练,学会了在什么时候该输出一段特定格式的 JSON(包含函数名和参数),来告诉开发者:“我现在需要你帮我执行这个函数”。
  • 绑定方式(强绑定 API):它与特定的 LLM 厂商强绑定。比如 OpenAI 的 Function Calling 格式,和开源模型 Llama 3 的格式可能就有细微差别。开发者需要针对特定的模型 API 编写特定的解析逻辑。
  • 工作流:
    1. 开发者把“天气函数”的定义(JSON格式)发给模型。
    2. 用户问:“今天北京天气如何?”
    3. 模型分析后,不直接回答,而是返回一个 JSON:{“name”: “get_weather”, “arguments”: {“location”: “Beijing”}}。
    4. 开发者自己的代码拦截到这个 JSON,去调用真实的第三方天气 API。
    5. 开发者把真实的温度数据再次发给模型。
    6. 模型最终生成自然语言回答用户。

3. MCP(模型上下文协议):标准化的“USB-C 接口”

MCP 是 Anthropic(Claude的母公司)最新推出的一项开源标准协议,旨在解决当前 AI 行业的一个巨大痛点:N x M 的集成灾难。

在 MCP 出现之前:如果 Slack、Notion、Github 想让 AI 访问它们的数据,它们必须为 OpenAI 写一套接口,为 Claude 写一套接口,为各种 Agent 框架(如 LangChain、Coze)各写一套接口。

  • 本质:MCP 是一个客户端-服务端(Client-Server)架构的通信协议。它定义了 AI 模型(Client)和本地/云端数据源及工具(Server)之间应该如何安全、标准地对话。
  • 绑定方式(解耦,全网通用):MCP 彻底打破了绑定!
    • 只要开发者把自己的应用封装成一个 MCP Server(比如一个连接本地数据库的 MCP Server)。
    • 那么,任何支持 MCP 的客户端(比如 Claude Desktop,或者未来的 Cursor、AnythingLLM),都可以直接连接并使用它。
    • 它就像电脑上的 USB-C 接口。只要你的设备是 USB-C 标准,无论插在苹果电脑还是 Windows 电脑上都能用。
  • 核心能力:MCP 不仅仅包含工具(Tools,即替代普通的 Function Calling),它还标准化了资源(Resources,让模型读取本地文件/数据库)和提示词模板(Prompts)。

一个用户触发了多个MCP工具,怎么做一些限流和排队的操作

“如果用户并发触发了大量 MCP 工具,为了防止后端资源崩溃,我会采用**‘拦截 + 缓冲’**的策略。

首先,我会利用 Redis 实现细粒度的限流。针对每个用户设定每分钟调用特定工具的次数上限,并在触发限流时,引导大模型用自然语言优雅地回复用户系统繁忙。同时,使用 Redisson 分布式锁防止同一个用户重复提交相同的复杂指令。

其次是排队机制。这要区分业务场景:
如果是轻量级、需要大模型实时根据结果回答的工具,我会使用 Spring 内部定制的 ThreadPoolExecutor 配合有界阻塞队列。让突发的调用请求在内存中排队,平滑地发往 MCP Server。
如果是极度耗时的重型工具,为了防止大模型上下文超时,我会引入我之前项目中用过的 RabbitMQ 做异步排队解耦。Agent 只负责把任务丢进 MQ,立刻响应前端,后台慢慢消费执行,最后通过 WebSocket 将结果异步推给用户。这种方式既保证了系统的绝对高可用,又兼顾了用户体验。”


MCP
https://kyy-logs.github.io/2026/05/20/ai/MCP/
作者
Yangyang Kong
发布于
2026年5月20日
许可协议