Agent 如何通过 MCP 调用工具:完整链路详解
一句话概括:MCP(Model Context Protocol)是给 Agent 接上”手”和”眼”的标准插头。它定义了一套严格的握手协议,让 Agent 知道”有什么工具可用、怎么调用”——从建立连接到最终执行工具,每一步都有明确的分工。LLM 在其中只负责推理和决策,真正调用 MCP 的是 Host(宿主应用)。
背景:为什么需要 MCP?
LLM 本质上只会”说话”,它没有办法自己去查数据库、调接口、读文件。要让 LLM 具备这些能力,必须给它一个标准化的方式来”发现工具”并”使用工具”。
以前每家公司都自己搞一套,乱得很。Anthropic 提出了 MCP,相当于制定了一个通用的 USB 接口标准——不管是什么工具(数据库插件、网页抓取、代码执行器),只要实现了 MCP 协议,LLM 就能用统一的方式调用。
整体架构:三个角色
1 | ┌─────────────────────────────────┐ |
- Host(宿主):运行整个 Agent 的应用,比如 Claude Desktop、Cursor、你自己写的 Agent 程序。它同时持有 LLM 调用能力和 MCP Client。
- LLM:只负责推理和决策,输出
tool_useJSON 表达意图,从不直接调用任何工具。 - MCP Client:嵌在 Host 内部,负责实现 MCP 协议、与 MCP Server 通信。Host 解析 LLM 的
tool_use输出后,由 MCP Client 代为执行。 - MCP Server:一个独立进程(或远程服务),向外暴露”工具 / 资源 / 提示词”。
通信方式有两种:
Networksetup代理与环境变量代理存在什么差异?
背景
用Proxyman抓包来分析一下CODEBUDDY、CLAUDE CODE与LLM之间的交互,如果是用Terminal的方式发现无法抓到,发现需要在环境变量里开启才行,所以就了解了下多种开启代理的区别。
两种方式的区别
有本质区别,作用范围完全不同:
networksetup— 系统级代理
修改的是macOS系统网络设置(等同于在「系统设置 → 网络 → 代理」里手动勾选)
立即生效,无需重启终端
影响范围:所有GUI应用(Safari、Chrome、Postman等)以及大多数遵循系统代理的程序
不影响终端里的命令行工具(curl、npm、pip、node 等默认不读系统代理)~/.zshrc环境变量 —Shell级代理
修改的是Shell进程的环境变量
需要重新source ~/.zshrc或开新终端才生效
影响范围:仅终端内的命令行工具(curl、npm、pip、git、node等)
不影响GUI应用
对比总结
| 对比项 | networksetup 系统代理 | ~/.zshrc 环境变量 |
|---|---|---|
| 作用对象 | GUI 应用 | 命令行工具 |
| 生效方式 | 立即生效 | 需 source 或新开终端 |
| Proxyman 抓包 | ✅ 能抓 GUI 流量 | ✅ 能抓终端流量 |
| curl / npm | ❌ 通常不走 | ✅ 走代理 |
| Chrome / Safari | ✅ 走代理 | ❌ 不走 |
结论
如果你用 Proxyman 抓 终端命令行工具(如 curl、npm 、install、node 发的请求)的包,必须设置 ~/.zshrc 环境变量,networksetup 对这些工具无效。
两者可以同时开启,互不冲突,Proxyman 本身也建议两者都配置。
AGENT.md:在LLM开口之前,它已经做了这些事
你有没有遇到过这种情况——
同一个 AI 助手,在 A 项目里像个老手:熟悉技术栈、遵守规范、说话有分寸。换到 B 项目,却像个刚入职的新人,什么都不知道,什么都要从头解释。
为什么?
因为 LLM 本身没有记忆。
每次对话,它都从零开始。它不知道你的项目用什么语言,不知道你的团队有什么规范,不知道你上次说了什么。它只是一个极其强大的”推理引擎”——给它什么上下文,它就基于什么上下文思考。
这就是问题所在,也是 AGENT.md 存在的原因。
LLM 是大脑,但大脑需要被”装配”
想象你雇了一个能力极强的顾问。
但每次见面,他都完全不记得你是谁、你的公司做什么、你们上次谈了什么。你每次都要从头介绍,每次都要重新建立信任,每次都要重新解释规则。
数据库遇到了死锁,你该如何排查?
MySql事务未提交导致锁等待如何解决?
背景
我们来先看一个图,了解一下故(事)事(故)的背景:

有2个跑批任务,其实做的事情是同一件事情,都是为了跟下游系统保持数据的一致性。大任务是每个2h跑一次,小任务是每隔10mins跑一次。除了这2个定时任务以外,还有一个额外的监控任务来做类似的对账,如果发现出现对账不平,就会出现邮件/短信告警到相关的责任上。
这是一个非常有特点的定时任务跑批任务+监控告警的场景了。
从上面的场景上看,我们可以得出一些结论:为了保证一致性写了大小Job来保证,并且还给出了监控告警,说明数据的重要性是比较强的。
某天,出现了频繁的告警提示,每10分钟就告警一次,而且内容没有发生变化,说明同步的index没有变化过。
错误排查
任务有在正常的执行吗?
第一反应肯定是在思考,我的大任务与小任务都有正常执行吗?因为之前的都是正常的。上午看了一下日志与进程发现有在跑,还跑除了多次任务,日志打印不明确,看不到具体分支的逻辑。总结一下问题点:
如何反编译一个Docker镜像还原Dockerfile?
前言
现在对于一个开发来说,Docker应该是再熟悉不过了。 还记得在2013~2014左右的时候,听说多最多的就是Cloud Foundry,那个时候就一直在说云的事情。后面Docker就绝杀了它~
那它帮我们解决了一个什么问题了?面试的时候也许会问到。
在很久以前,我们开发代码,估计最蛋疼的事情就是发布版本了。我还记得在房多多的时候(2014~2016)左右,每次发布几个开发围绕在运维的身边,有时候运维忙不过来,开发就直接在运维的电脑上开始VIM干活了,修改若干配置。由于多环境的原因,我们无法保证每个环境都是一样的。
- 可能你的操作系统不同,导致打包、发布的脚本不同
- 环境不同,没有很好的配置管理,你的代码有不同的写法
- 特别是跟操作系统相关的那些参数,可能瞬间就会带来性能问题
那么Docker就可以把我们的操作系统、代码、脚本等都一起打包成一个Image,就可以保证只要是运行同一个Image,我们的所有内容都是一样的。就不会出现,我在测试环境跑的好好的,一到生产连启动都成问题。
问题
现在一般一个POD就只跑一个进程,DevOps会根据我们的发布流水线自动的将一个项目进行打包、发布,整套的CI、CD做的是行云流水。但是,每个项目ROOT下都会需要一个叫Dockerfile的文件。但偏偏有一些历史项目,没有Dockerfile文件,只有一个Run的容器再跑,真的是非常惊悚。docker rm [OPTIONS] CONTAINER [CONTAINER...],就GAME OVER了。
怎么办?
方法1:以当前容器作为基础镜像
如何利用k8s的label与ingress做蓝绿发布?
背景
之前在思考双活/多活架构的时候,其实对于蓝绿发布是有一些了解的,也梳理过在底层存储是一份,服务是多份的模式有做过深入的分析。但那个时候对于Kubernetes的了解还不是很熟悉,是通过传统的方式来考量的。
因为现在的互联网公司基本都是上云了,我们也必须对于Kubernetes那一整套要有比较深入、熟悉的运用才能真的提高我们的效率。先聊一下,我为什么需要利用灰度+蓝绿发布的模式来去做?
现在有一个比较老的项目,应该在10年+,每天请求量大概在1.5亿+,峰值的QPS在6000/s,存在着比较多性能问题。现在需要在它上面新增一个服务,为了后面优化做准备,比如:请求的分流、限流、熔断、日志的上报与监控(新)、统一编译处理,特殊报文转换等。也就是说,只要你新增加了一层,你才有可能更好的去做更多的事情。
那么我们需要达到一些什么的基础条件了?
- 服务流量比较大,我们需要对新服务的可靠性需要验证,需要灰度先了解
- 因为存在慢查询,不能在滚动发布中,导致请求还未执行完毕,就被k8s kill掉了,业务会感知到502
如果是你?针对于这2个基础的要求,你会如何去思考的你架构方案呢?
思考
新增服务的思考:
- 它的性能必须要强、服务稳定。一个服务的性能好不好,其实跟它的:
I/O模型、线程模型、数据结构、算法等息息相关。比如:你在思考Redis单线程为什么快的时候?应该就很能get到这里的点了。解决这个问题,我们选择了Go语言来开发(当然,最熟悉的语言风险最小),为了保证性能,也是做了2轮非常细致的压测。 - 发布过程中不能因为kill掉服务导致请求
502。如果说我在发布的过程中,我把滚动这一步省略掉,直接先准备好一份最新的,验证可以后,我一刀直接把流量引导最新服务上,老的服务也不会断掉,这是否就可以达到效果了?
如何利用wrk与Jmeter做性能压测
我们在新做项目的时候,需要对我们的服务有有一些性能指标,比如:SLA(需要达到多少个9)、QPS、TPS等。因为这些量化的数字让我们更加的了解我们的系统。
我们如何压测?其实个人觉得有2种场景。
第一种:是我们明确的知道目标,看我们通过大量的并发看我们是否有达到。如果没有达到,我们需要通过水平扩容、性能优化等让其达到。
第二种:是我们不知道目标,通过压测可以知道一个固定配置下的单机单服务的最大性能,让我们对它有一个彻底的认识。为后面的目标做更多的铺垫与准备,或者跟行业水平对比,看看差距有多少。
如何用wrk进行压测?
Github地址:https://github.com/wg/wrk,该项目也是开源项目,关注的人还不少,有30.4K。咨询了一下身边的同事,使用它的人还不少。主要的语言的是C语言。

安装
1 | git clone https://github.com/wg/wrk |
压测脚本
【go学习】地址符&与指针*的差别
背景
项目中开始用Go,最近写了一下Demo,发现语法还是非常好用,大部分比Java还是简洁很多,也有一些很细节的约定。比如:
- 字母大小写控制是全包还是本包内访问
- 变量定义了就一定要使用,否则就会编译不通过等等
更好的就是方法可以返回多个值,这个跟Java比较就是减少很多的封装。因为Go的线程模型特点,用来写一些需要高并发、高性能的项目还是非常好的。所以,趁这个机会也好好的深入了解下。现在也是把Python、PHP、Go等都学习一遍,每种语言都有它的优缺点,其实都还挺不错的。
针对于Go语言里:&与*的区别,什么时候该用什么做一个总结。
指针
我们经常会听到别人说Go是值传递,某某某是引用传递,某某某是指针传递,等等各种各样的说法。
那么首先他们的区别是什么呢?什么是指针?指针其实也是一个变量,只不过这个变量里面存的不是int,float,struct,而是一个地址address,然后在这个address上所存储的数据可以通过指针来被阅读到。
OK,指针变量存储的是一个地址,地址从哪里来的?那就得问一个变量的地址怎么取得呢?在变量前面加上一个&符号就行。
好的,指针变量存储了这个地址了,那这个地址所存储的值怎么被阅读到呢?也就是指针所指向的值怎么拿到呢?在指针变量前面加上一个*符号就行。
