
1. 从一条报错说起为什么 Verdi 2026 要配 MCP如果你最近在跑 VCS Verdi 的联合仿真大概率见过这条报错an assistant message with tool_calls must be followed by tool messages responding to each tool_call_id第一次看到它的人会懵——这明明是 OpenAI 风格的对话协议报错怎么会出现在 EDA 工具链里答案就在 Verdi 2026 引入的Assistant MCP架构。Verdi 不再只是一个看波形的工具它开始内置一个能调用外部能力的智能助手而助手和工具之间的通信走的就是 MCPModel Context Protocol这套协议。上面那条报错本质是助手发起了一次工具调用但对应的工具返回消息没接上协议栈断链了。这篇内容我打算把 Verdi 2026 Assistant 与 MCP 的配置从头到尾讲清楚MCP 到底是什么、Verdi 里 Assistant 和 MCP Server 怎么分工、配置文件写在哪、怎么验证连通、以及我在实际调试中踩过的坑。适合正在用 VCS/Verdi 做验证的 IC 工程师也适合想把 MCP 这套思路迁移到自己工具链上的开发者。不管你是第一次听说 MCP还是已经在别的工具里用过 MCP Server这里都有能直接抄的配置和排查方法。先说结论Verdi 2026 的 Assistant 是MCP Host它通过标准输入输出或本地端口去连一个个MCP Server每个 Server 暴露若干tool。Assistant 决定调哪个 toolServer 负责执行并把结果回传。理解了这条链路配置和排错就都顺了。2. MCP 到底是什么用一句话和一张表讲透2.1 MCP 的本质给 AI 装一个统一的外设接口MCP 全称 Model Context Protocol直译是模型上下文协议。你可以把它类比成 USB以前每个外设都有自己的接口鼠标一个口、键盘一个口、打印机一个口乱得很USB 出现之后所有外设都用同一套协议主机只要支持 USB 就能接任何设备。MCP 干的就是这件事——它定义了 AI 助手Host和外部能力Server之间的统一通信格式让助手不用为每个工具单独写适配代码。在 Verdi 的场景里这个外部能力可以是查信号、跑仿真、读 RTL、生成波形脚本甚至是查文档。只要有人写一个对应的 MCP ServerVerdi Assistant 就能调用它。这就是为什么 Verdi 2026 要引入 MCP它把助手能干什么从写死的功能变成了可插拔的生态。2.2 Host、Server、Tool 三层关系很多人第一次接触 MCP 会被 Host 和 Server 绕晕。我用一张表把这三个角色和它们在 Verdi 里的对应关系列清楚角色职责Verdi 2026 中的对应类比MCP Host发起调用、管理会话、决定用哪个工具Verdi Assistant用电脑的人MCP Server暴露工具、执行具体操作、返回结果独立的 server 进程打印机/扫描仪ToolServer 暴露的一个具体能力如query_signal、run_sim打印机的一个按钮关键点在于Host 不直接干活它只负责决定和调度。真正执行的是 Server。所以当 Assistant 说我要查一下 clk 信号的翻转次数它其实是发了一条 tool_call 给某个 ServerServer 执行完把结果塞回一条 tool message。这就解释了开头那条报错——tool_call 发出去了但 tool message 没回来链路断了。2.3 MCP 和 RAG 的区别别搞混热词里有人问rag 和 mcp 区别这俩确实容易混。RAG检索增强生成解决的是知识从哪来——它把文档切片、向量化、检索喂给模型当上下文。MCP 解决的是动作怎么执行——它让模型能真正去调用一个函数、跑一个命令、查一个实时数据。打个比方RAG 是给助手一本参考书它只能读MCP 是给助手一双手它能做。在 Verdi 里你想让助手知道某个项目的信号命名规范用 RAG你想让助手真的去波形里查一个信号的值用 MCP。两者不冲突经常一起用。3. Verdi 2026 Assistant 的定位与能力边界3.1 Assistant 不是聊天框是调度中枢很多人以为 Verdi 2026 的 Assistant 就是个内置的聊天窗口问它问题它回答。这只是表象。Assistant 真正的价值在于它是一个调度中枢它理解你的自然语言意图把它翻译成一系列 tool_call分发给不同的 MCP Server再把结果整合成人类能读的回答。比如你说帮我看看 testbench 里那个复位信号为什么没拉高Assistant 可能会先调一个读 RTL 的 tool 找到复位信号定义再调一个查波形的 tool 看它的实际值最后调一个查日志的 tool 看仿真有没有报错。这一串动作全靠 MCP 串起来。3.2 能力边界它能做什么不能做什么Assistant 的能力完全取决于你挂了哪些 MCP Server。没挂 Server它就是个只会聊天的空壳。挂了波形 Server它能查波形挂了仿真 Server它能跑仿真挂了文档 Server它能查手册。这里有个常见误区有人以为 Verdi 自带的 Assistant 开箱就能干所有事。不是的。Verdi 2026 出厂只带基础能力真正的威力在于你自己配 Server。这也是为什么配置指南这么重要——配置决定了这个助手到底有多大本事。3.3 为什么用 MCP 而不是私有插件你可能会问Verdi 以前也有 Tcl 接口、也有各种插件为什么非要搞 MCP我的理解是三点。第一标准化MCP 是通用协议同一个 Server 理论上能被任何支持 MCP 的 Host 调用不用为每个工具重写。第二解耦Server 可以独立开发、独立部署、独立升级不用动 Verdi 本体。第三生态当大家都用 MCP工具之间就能互相复用你写的 Server 别人也能用。这跟当年大家从私有脚本转向 Python 生态是一个道理——标准化的接口长期看一定赢。4. 配置文件到底写在哪路径、格式与字段详解4.1 配置文件的三个可能位置Verdi 2026 的 MCP 配置遵循就近覆盖原则从高优先级到低优先级依次是项目级project_root/.verdi/mcp.json只对当前项目生效用户级~/.verdi/mcp.json对当前用户所有项目生效全局级Verdi 安装目录下的config/mcp.json对所有用户生效实际用的时候我建议把通用 Server 放用户级把项目特有的 Server 放项目级。这样换项目不用重配项目特有的又不会污染全局。注意项目级配置会完全覆盖同名 Server 的用户级配置不是合并。如果你在项目级只改了某个 Server 的一个字段其他字段不会从用户级继承会直接丢失。这个坑我踩过排查了半天。4.2 mcp.json 的完整字段说明一个典型的mcp.json长这样{ mcpServers: { verdi-waveform: { command: python, args: [-m, verdi_mcp.waveform_server], env: { VERDI_HOME: /tools/verdi/2026, WAVE_DB: ./sim/waves.fsdb }, disabled: false, autoApprove: [query_signal, list_signals] }, verdi-sim: { command: /tools/vcs/bin/sim_mcp_server, args: [--port, 0], env: {}, disabled: false, autoApprove: [] } } }逐个字段解释commandServer 的启动命令可以是可执行文件路径也可以是解释器如 python、nodeargs传给 command 的参数数组注意是数组不是字符串env注入给 Server 进程的环境变量这是传配置的主要方式disabled临时禁用某个 Server调试时很有用autoApprove免确认的工具白名单列在这里的 tool 调用不会弹确认框4.3 传输方式stdio 还是 SSEMCP 支持两种传输方式。stdio是默认的Host 启动 Server 进程通过标准输入输出通信适合本地工具。SSEServer-Sent Events走 HTTP适合远程 Server 或需要长期运行的场景。Verdi 2026 默认用 stdio。如果你要连远程 Server配置里加transport: sse和url: http://...。但说实话本地验证场景 stdio 就够了SSE 主要用在团队共享 Server 的场景。提示stdio 模式下Server 进程的 stdout 被协议占用你的调试打印必须走 stderr否则会污染协议流导致解析失败。这是新手最容易犯的错。5. 手把手配置一个波形查询 MCP Server5.1 环境准备与依赖确认先确认你的 Verdi 版本和 Python 环境verdi -version # 期望输出Verdi ... 2026.xx python3 --version # 期望Python 3.9 以上 pip list | grep mcp # 期望mcp 包已安装如果没装 mcp 包pip install mcp即可。Verdi 2026 自带的 Python 环境通常已经预装了但独立环境需要手动装。5.2 写一个最小可用的 Server下面是一个查信号翻转次数的 Server代码不长但五脏俱全from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent import os app Server(verdi-waveform) app.list_tools() async def list_tools(): return [ Tool( namequery_signal, description查询指定信号在波形中的翻转次数, inputSchema{ type: object, properties: { signal: {type: string, description: 信号全路径名} }, required: [signal] } ) ] app.call_tool() async def call_tool(name, arguments): if name query_signal: sig arguments[signal] db os.environ.get(WAVE_DB, ) # 这里调用实际的波形查询逻辑 count do_query(db, sig) return [TextContent(typetext, textf{sig} 翻转 {count} 次)] async def main(): async with stdio_server() as (r, w): await app.run(r, w, app.create_initialization_options()) if __name__ __main__: import asyncio asyncio.run(main())关键点list_tools告诉 Host 我有哪些工具call_tool负责执行。inputSchema用 JSON Schema 描述参数Host 靠它决定怎么填参数。5.3 注册到 mcp.json 并验证把 Server 写进~/.verdi/mcp.json然后重启 Verdi。验证连通有两种方式# 方式一命令行直接测 Server 能否启动 echo {jsonrpc:2.0,id:1,method:initialize,params:{}} | python -m verdi_mcp.waveform_server如果返回了 initialize 响应说明 Server 本身没问题。方式二是在 Verdi Assistant 里直接问列出可用工具能列出来就说明注册成功。注意改完 mcp.json 一定要完全重启 Verdi热加载不一定生效。我遇到过改了配置没重启Assistant 一直用旧配置的情况。6. 联调排错那条 tool_calls 报错怎么解6.1 报错的根因分析回到开头那条报错an assistant message with tool_calls must be followed by tool messages responding to each tool_call_id翻译成人话助手发了一条带 tool_calls 的消息协议要求后面必须紧跟对应的 tool 返回消息但没跟上。可能的原因有三类Server 崩溃或超时tool_call 发出去了Server 没返回链路悬空tool_call_id 不匹配返回的 id 和请求的 id 对不上协议认为没响应Server 返回格式错误返回了内容但不是合法的 tool message 格式6.2 排查顺序与速查表我整理了一个排查顺序从最常见到最罕见现象可能原因排查方法报 tool_calls 未响应Server 进程挂了看 Server 的 stderr 日志报 id 不匹配Server 返回格式错抓协议流看原始 JSON调用超时Server 执行太慢加日志看卡在哪一步工具列表为空配置没生效确认重启 路径正确权限拒绝autoApprove 没配手动确认或加白名单6.3 抓协议流的实用技巧想看 Host 和 Server 之间到底传了什么最直接的办法是在 Server 入口加一层日志代理把 stdin/stdout 的内容同时写到文件import sys def log_wrapper(stream, logfile): for line in stream: with open(logfile, a) as f: f.write(line \n) yield line把 stdin 包一层就能看到 Host 发来的每一条请求。这个技巧在排查 id 不匹配时特别管用——你能直接看到请求 id 和响应 id 对不对得上。提示日志文件别写太大协议流很啰嗦跑一会儿就几百 MB。建议加个大小限制或者只记关键字段。7. 实操心得与常见坑7.1 环境变量是传参的主力MCP 的 tool 参数是给单次调用用的而 Server 的配置比如波形库路径、工具路径应该走环境变量。我见过有人把波形库路径写死在代码里换个项目就得改代码非常痛苦。正确做法是全部走env代码里只读os.environ。7.2 autoApprove 要慎用autoApprove能让某些工具免确认直接执行调试时很方便。但只读类工具查信号、列文件可以加白名单写操作类工具跑仿真、改文件千万别加。我有次把跑仿真的工具加了白名单结果 Assistant 理解错意图连着跑了三次仿真浪费了半小时机时。7.3 Server 要能独立测试写 Server 的时候一定要保证它能脱离 Verdi 独立运行和测试。用命令行发 JSON-RPC 请求就能测不用每次都开 Verdi。这样开发效率高很多也更容易定位问题——到底是 Server 的锅还是 Host 的锅。7.4 版本兼容性MCP 协议本身在演进不同版本的 Host 和 Server 可能对协议细节有不同要求。Verdi 2026 用的 MCP 版本最好和你的 Server 依赖的 mcp 包版本对齐。我遇到过 Server 用新版 mcp 包写的Verdi 内置的是旧版握手阶段就失败了。解决办法是锁定 mcp 包版本或者用 Verdi 自带的 Python 环境。8. 把 MCP 思路迁移到自己的工具链Verdi 这套 Assistant MCP 的架构其实是个很好的模板。任何有 CLI 或 API 的工具都能包一层 MCP Server然后被支持 MCP 的 Host 调用。热词里提到的 Figma MCP、Playwright MCP、IDA MCP本质都是这个思路——把已有能力用 MCP 协议暴露出来。如果你想给自己的验证流程做一套我的建议是先从最痛的一个点开始比如查波形或跑回归写一个最小 Server 跑通再逐步扩展。别一上来就想搞个大而全的平台MCP 的价值在于快速组合不在于一次做全。最后分享一个我自己的习惯每个 Server 我都配一个--selftest参数启动时先自检依赖是否齐全、路径是否存在自检不过直接退出并打印原因。这样配置出问题时Verdi 那边报的错会清晰很多不用每次都去翻协议流。这个习惯帮我省了大量排查时间推荐你也试试。