From 3245474b3278a0755fd00e91ea63cbf8a7e04dcb Mon Sep 17 00:00:00 2001 From: V-LiuShuang Date: Wed, 23 Sep 2026 11:20:51 +0800 Subject: [PATCH] add --- se-agent-mcp-tool.md | 1220 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1220 insertions(+) create mode 100644 se-agent-mcp-tool.md diff --git a/se-agent-mcp-tool.md b/se-agent-mcp-tool.md new file mode 100644 index 0000000..8315462 --- /dev/null +++ b/se-agent-mcp-tool.md @@ -0,0 +1,1220 @@ +# SE 工作流 MCP 工具 API 文档 + +> **来源**:`docs/task/se工作流mcp工具统计.md`(草稿统计)→ 本文档按 `mis-mcp-server` **源码实现**补全为可调用的 API 文档 +> **覆盖范围**:SE 工作流相关的 **9 个** MCP 工具 +> **生成日期**:2026-09-22 +> **代码基线**:`mis-mcp-server` 分支 `dev` +> +> **一句话定位**:面向「AI 生成 SKILL / Prompt」场景的机器友好型接口文档。每个工具给出**参数表 + 返回 JSON 结构 + 字段语义 + 关联工具**。 + +### 修订记录(2026-09-22) + +编写本文档时发现 **5 处工具描述与实现不符**,已在 `mis-mcp-server` 侧修正(**未改动 `mbx` 下游代码**): + +| 工具 | 原描述问题 | 处置 | +|---|---|---| +| `queryDriverListVersion` | 说「驱动列表唯一标识」「返回 `packageFileUrl` 即可」——实为**版本**标识,且该字段**嵌套**在数组里、未裁剪 | ✅ 改描述 | +| `queryTestUnitListByProjectName` | 说「根据项目名称、**五码**查询」——下游 SQL **未按五码过滤** | ✅ 改描述 + 参数说明 | +| `queryProjectNameList` | 说「获取项目名称列表」——实为**固定最多 2 条** | ✅ 常量 `2` → `100`,并同步描述(**行为变更**) | +| `queryTestPcByUuids` | 未说明在线状态字段名——直觉会取恒为 null 的 `onlineStatus` | ✅ 描述里点明用 `onlineStatusKey` | +| `creatOrSendTask` | 未说明 `other.type` 必填——漏传会下游报错 | ✅ 描述里点明必填项 | + +--- + +## ⚠️ 确信度标记(务必先读) + +本文档所有字段名均来自**源码或真实响应样本**,不使用推测值。为便于区分,全文使用以下标记: + +| 标记 | 含义 | +|---|---| +| ✅ **已确定** | 直接来自 `mis-mcp-server` 源码逻辑、下游实现源码,或真实接口响应样本,字段名与结构可信 | +| ⚠️ **透传** | MCP 层**不裁剪**,原样返回下游响应;字段结构以**下游 MIS/MBX 服务为准**,可能随下游变更 | + +> 无法完全确证的少量细节**不散落在正文里**,统一收在 **§6.5 残留不确定项**。 +> 写 prompt 前建议先扫一遍 **§6.4 高风险陷阱汇总**——那里是「照直觉写就会错」的地方。 + +> **给 AI 生成 prompt 的建议**:引用 ⚠️ 透传 字段时,prompt 里应写成「从返回的 `xxx` 字段读取」而非硬编码整个结构; +> 引用 ✅ 已确定 字段时可以放心直接取值。 + +--- + +## 目录 + +- [1. 工具总览](#1-工具总览) +- [2. 全局约定](#2-全局约定) +- [3. 工具详情](#3-工具详情) + - [3.1 `debuggingProjectQuery`](#31-debuggingprojectquery) + - [3.2 `queryProjectNameList`](#32-queryprojectnamelist) + - [3.3 `queryDriverListNamesByProject`](#33-querydriverlistnamesbyproject) + - [3.4 `queryDriverListVersion`](#34-querydriverlistversion) + - [3.5 `prepareTaskFile`](#35-preparetaskfile) + - [3.6 `generatePreloadUpdateLink`](#36-generatepreloadupdatelink) + - [3.7 `creatOrSendTask`](#37-creatorsendtask) + - [3.8 `queryTestUnitListByProjectName`](#38-querytestunitlistbyprojectname) + - [3.9 `queryTestPcByUuids`](#39-querytestpcbyuuids) +- [4. 典型工作流(编排剧本)](#4-典型工作流编排剧本) +- [5. 生成 SKILL 与 Prompt 的指引](#5-生成-skill-与-prompt-的指引) +- [6. 附录](#6-附录) + - [6.1 MCP 接入信息](#61-mcp-接入信息) + - [6.2 下游接口与白名单对照](#62-下游接口与白名单对照) + - [6.3 易混淆 / 已废弃工具](#63-易混淆--已废弃工具) + - [6.4 高风险陷阱汇总(写 prompt 前必读)](#64-高风险陷阱汇总写-prompt-前必读) + - [6.5 残留不确定项](#65-残留不确定项) + +--- + +## 1. 工具总览 + +### 1.1 速查表 + +| # | MCP 工具名 | 所属服务 | 用途 | 必填参数 | 返回形态 | +|---|---|---|---|---|---| +| 1 | `debuggingProjectQuery` | `mis-mcp-tool-debugging` | 按项目名称/别名/五码查**项目基础信息** | 无(全部可选) | 分页信封 `{code,msg,total,rows[]}` ⚠️ | +| 2 | `queryProjectNameList` | `mis-mcp-tool-driver` | 取 MBX 侧**项目名称 + projectUuid** | 无(`creatorCode` 可选) | 数组 `[{name,projectUuid}]` ✅ | +| 3 | `queryDriverListNamesByProject` | `mis-mcp-tool-driver` | 按项目组/项目取**全部 Driver List Name** | `projectGroupUuid` 或 `projectUuid` | 数组 `[{driverListVersionUuid,driverListName}]` ✅ | +| 4 | `queryDriverListVersion` | `mis-mcp-tool-driver` | 取驱动列表版本的**包下载地址** | `driverListVersionUuid` | 下游对象 ⚠️(含 `packageFileUrl`) | +| 5 | `prepareTaskFile` | `mis-mcp-tool-driver` | 下载文件→重传到文件服务,换**任务下发可用的 fileUuid** | `fileUrl` | 对象 `{fileUuid,fileUrl,fileName,fileExtensionKey}` ✅ | +| 6 | `generatePreloadUpdateLink` | `mis-mcp-tool-vlab` | 生成 VLAB Preload 向导的**更新下载链接** | `project` | 对象(成功/失败两种结构)✅ | +| 7 | `creatOrSendTask` | `mis-mcp-tool-vlab` | **创建或下发测试任务** | `creatorCode`,`requestBodyJson` | 下游对象 ⚠️ / 纯文本提示 ✅ | +| 8 | `queryTestUnitListByProjectName` | `mis-mcp-tool-vlab` | 按项目名+五码查**机器列表** | `projectName`,`modelCode`,`ownerCode` | 下游列表 ⚠️ / 纯文本提示 ✅ | +| 9 | `queryTestPcByUuids` | `mis-mcp-tool-vlab` | 按 UUID 批量查**测试机在线状态** | `testUnitUuids` | 下游列表 ⚠️ | + +> **参数必填标记来自源码**:`@ToolParam` 未写 `required = false` 即为**必填**。 + +### 1.2 数据依赖关系(谁喂谁) + +这是生成 prompt 时最关键的一张图——**ID 都是一跳一跳传递的**: + +``` +① debuggingProjectQuery ──► projectName / projectUuid / modelCode + │ +② queryProjectNameList ──────► projectUuid(MBX 侧)──┐ + │ +③ queryDriverListNamesByProject ◄─────────────────────┘ + │ (需要 projectGroupUuid 或 projectUuid) + └──► driverListVersionUuid + │ +④ queryDriverListVersion ◄──┘ + └──► packageFileUrl + │ +⑤ prepareTaskFile ◄─┘ + └──► fileUuid + │ +⑦ creatOrSendTask ◄──┘ (填入 requestBodyJson.model.children[].documentCopyDTOList[].fileUuid) + +⑥ generatePreloadUpdateLink 独立链路,只需 project 名称(+ 可选 osVer) +⑧ queryTestUnitListByProjectName ──► testUnitUuid[] + │ +⑨ queryTestPcByUuids ◄────────────────────┘ (查在线状态) +``` + +**关键 ID 传递表**: + +| 上游工具 | 产出字段 | 下游工具 | 填入位置 | +|---|---|---|---| +| `queryDriverListNamesByProject` | `driverListVersionUuid` | `queryDriverListVersion` | 入参 `driverListVersionUuid` | +| `queryDriverListVersion` | `driverPackageVersionList[].packageFileUrl` | `prepareTaskFile` | 入参 `fileUrl` | +| `prepareTaskFile` | `fileUuid` | `creatOrSendTask` | `requestBodyJson.model.children[].documentCopyDTOList[].fileUuid` | +| `queryProjectNameList` | `projectUuid` | `queryDriverListNamesByProject` | 入参 `projectUuid` | +| `queryTestUnitListByProjectName` | `testUnitUuid` | `queryTestPcByUuids` | 入参 `testUnitUuids[]` | + +### 1.3 语义说明:两个「项目查询」不重复(用户补充) + +- `debuggingProjectQuery` —— 查的是 **SE 系统同步过来的 project** 信息; +- `queryProjectNameList` —— 查的是 **MBX 系统**的 project 信息; +- **两边的 project 的 `name` 值是对应的**,可互为参照。 + +> AI 写 prompt 时:要「项目基础信息」用 ①;要「能直接喂给 Driver 链路的 `projectUuid`」用 ②。 + +--- + +## 2. 全局约定 + +### 2.1 返回信封与三种裁剪行为 + +MCP 工具**全部返回字符串**(JSON 文本)。脱掉下游信封 `{code, msg, data}` 后,按模块有**三种**不同行为——写 prompt 时必须区分: + +| 行为 | 工具 | 返回内容 | +|---|---|---| +| **A. 全量透传** | `debuggingProjectQuery` | 返回**整个信封** `{code,msg,total,rows[]}`,`rows[]` 内字段**不裁剪** ⚠️ | +| **B. 取 data(分页自动展平)** | `queryDriverListVersion`、`creatOrSendTask`、`queryTestUnitListByProjectName`、`queryTestPcByUuids` | 若 `data` 是对象且含 `records`,**只返回 `records` 数组**;否则返回 `data` 本身;`data` 为空返回 `"[]"` ⚠️ | +| **C. 取 data + 字段裁剪** | `queryProjectNameList`、`queryDriverListNamesByProject`、`prepareTaskFile`、`generatePreloadUpdateLink` | 只返回源码中显式挑出的字段 ✅ | + +> **注意 B 类的空值表现**:`data` 为空时返回字符串 `"[]"`(**空数组**,不是空对象)。 + +### 2.2 分页参数命名不统一(易错点) + +| 服务 | 页码参数 | 每页参数 | 默认值 | +|---|---|---|---| +| `mis-mcp-tool-debugging` | `pageNum` | `pageSize` | 1 / 10 | +| `mis-mcp-tool-driver` | `current` | `size` | 1 / 10(`queryProjectList*` / `queryDriverChidPage` / `queryDriverPackageList` / `queryWu*`)
1 / 5(`queryDriverListPage`) | +| `mis-mcp-tool-vlab` | `current` | `size` | 1 / 10 | + +> **两个工具没有分页参数**(内部固定,调用方无需也无法指定): +> `queryProjectNameList`(固定第 1 页、每页 **100** 条)、`queryDriverListNamesByProject`(自动翻页取全量,每页 100 条)。 + +### 2.3 用户 ID 与鉴权 + +- **两种用户 ID 不能混用**:MCP 工具入参普遍收 **MIS 系统用户 ID**(如 `creatorCode`、`ownerCode`), + 由服务内部通过 `MbxUserIdConverter` 转成 **MBX 用户 ID** 后传给下游(5 分钟本地缓存)。 +- **转换失败有专用提示**:部分工具转换失败时返回**纯文本**而非 JSON,见 §2.4。 +- **鉴权由 MCP 客户端透传**,调用方(LLM 侧)**不需要**在工具参数里传 token; + 凭证走 MCP 请求头 `auth` / `clientid`,由 `MisAuthForestInterceptor` 注入下游。 + +### 2.4 非 JSON 返回(prompt 必须能识别) + +以下情况返回的是**纯文本字符串**,不是 JSON——AI 解析时应先判断首字符: + +| 工具 | 返回文本 | 触发条件 | +|---|---|---| +| `creatOrSendTask` | `请输入当前登录人信息` | `creatorCode` 为空,或 MIS→MBX 用户 ID 转换失败 | +| `queryTestUnitListByProjectName` | `登录人信息转换异常` | `ownerCode` 有值但转换失败 | + +### 2.5 异常的处理方式 + +参数校验失败、下游业务失败时,工具**抛异常**(`IllegalArgumentException` / `RuntimeException`), +由 MCP 协议以错误形式返回,**不会**包装成 JSON。典型触发: + +- `queryDriverListVersion`、`queryTestPcByUuids` 等:必填参数为空(如 `testUnitUuids` 空列表); +- 所有工具:下游返回非 JSON、下游业务码失败(错误信息取下游 `msg`)。 + +--- + +## 3. 工具详情 + +### 3.1 `debuggingProjectQuery` + +**摘要**:根据项目名称、项目别名、项目五码查询项目基础信息,返回项目列表分页数据。 + +| 项 | 值 | +|---|---| +| 所属服务 | `mis-mcp-tool-debugging`(端口 18780,`STATELESS`) | +| Java 方法 | `ProjectMcpService.queryProjectList` | +| MCP 工具名 | `debuggingProjectQuery`(**源码中显式指定**,与 Java 方法名不同,调用时必须用这个名字) | +| 下游接口 | `GET {misApiBaseUrl}/debugging/project`(MIS 平台,网关白名单已放行) | +| 返回裁剪 | **A 类:全量透传**(`rows[]` 内 34 个字段全部返回,不裁剪)⚠️ | + +**参数表** + +| 参数名 | 类型 | 必填 | 默认 | 说明 | 示例 | +|---|---|---|---|---|---| +| `projectName` | String | 否 | — | 项目名称 | `IdeaPad 5 Pro 16ARP8` | +| `projectAlias` | String | 否 | — | 项目别名 | — | +| `modelCode` | String | 否 | — | 项目五码 | `LT4EN` | +| `ppmQuery` | String | 否 | — | 项目负责人名称或参与人 | `san.zhang` | +| `mtCode` | String | 否 | — | 项目四码 | `81S3` | +| `pageSize` | Integer | 否 | `10` | 每页条数 | `10` | +| `pageNum` | Integer | 否 | `1` | 页码 | `1` | + +**返回 JSON 结构** ✅(顶层 5 字段)/ ⚠️(透传,未裁剪) + +```jsonc +{ + "total": 0, // number 总条数 + "rows": [ /* 见下表,Project 实体,34 个字段 */ ], + "code": 200, // number 成功码固定 200 + "msg": "查询成功", // string + "extraData": null // 该接口路径下恒为 null +} +``` + +**`rows[]` 元素字段表**(实体 `com.lcfc.debugging.domain.Project`,**无任何 JSON 重命名注解**) + +| 字段名 | 类型 | 说明 | +|---|---|---| +| `projectUuid` | Long → number/string | 主键。**类型不确定**:雪花 ID 超 2^53-1 时会被序列化成字符串,须按「可能是数字也可能是字符串」处理 | +| `idpProjectUuid` | String | idp 项目管理表主键 | +| `projectName` | String | Project Name 数据 | +| `projectCode` | String | Project code 数据 | +| `projectAlias` | String | 项目别名 | +| `lenovoProductName` | String | 联想产品名称 | +| `issueOwners` | Array\ | Issue Owner 列表,元素为 `{category, component, owner}` | +| `modelCode` | String | 五码信息(BOX Model Code) | +| `mtCode` | String | 四码信息 | +| `finCode` | String | 财务代码 | +| `bu` | String | BU 信息 | +| `developmentType` | String | Development Type | +| `generation` | String | Generation | +| `segment` | String | Segment | +| `pm` | String | PM | +| `ppm` | Array\ | PPM 列表 | +| `pje` | Array\ | PJE 列表 | +| `biosCode` | String | BIOS Code | +| `ddsTime` | String | 日期,格式固定 `yyyy-MM-dd` | +| `pkoTime` | String | 同上 | +| `evtStartTime` / `evtEndTime` | String | 同上 | +| `fvtStartTime` / `fvtEndTime` | String | 同上 | +| `sitStartTime` / `sitEndTime` | String | 同上 | +| `svtStartTime` / `svtEndTime` | String | 同上 | +| `sovpTime` / `ssTime` | String | 同上 | +| `actuaiEopTime` | String | 同上。**字段名拼写就是 `actuaiEopTime`**(非 `actual`),按原样使用 | +| `sort` | Integer | 排序号 | +| `delFlag` | String | 删除标志,查询恒定返回 `"0"` | +| `ppmQuery` | null | **恒为 null**(该字段只用于查询条件,不参与返回取值) | + +**返回示例**(截取) + +```json +{ + "total": 1, + "rows": [ + { + "projectUuid": "2049298006288687105", + "projectName": "IdeaPad 5 Pro 16ARP8", + "projectAlias": "LCP8", + "modelCode": "LT4EN", + "mtCode": "81S3", + "ppm": ["san.zhang"], + "pje": [], + "ddsTime": "2026-03-01", + "delFlag": "0", + "ppmQuery": null + } + ], + "code": 200, + "msg": "查询成功", + "extraData": null +} +``` + +**注意事项** + +- 这是**唯一一个返回「整个信封」**的工具(其余 debugging 工具会裁剪成 `{code,msg,total,rows[]}` 且过滤行内字段)。若 prompt 里要遍历数据,需先取 `rows`。 +- `total` 是**总条数**,不是当前页条数。 +- `projectUuid` 在别的工具里常以字符串形式出现,**做参数传递时按字符串处理最安全**。 +- 查的是 **SE 系统同步过来的项目信息**,与 `queryProjectNameList` 的 MBX 侧数据来源不同,但 `name` 对应(见 §1.3)。 + +**关联工具**:`queryDriverListNamesByProject`(其 `projectUuid` 可来自本工具)、`queryProjectNameList` + +--- + +### 3.2 `queryProjectNameList` + +**摘要**:获取项目名称列表,返回 `name` 与 `projectUuid`。`creatorCode` 有值时只查该用户所涉及的项目。 + +| 项 | 值 | +|---|---| +| 所属服务 | `mis-mcp-tool-driver`(端口 18767,`STATELESS`) | +| Java 方法 | `DriverService.queryProjectNameList` | +| MCP 工具名 | `queryProjectNameList` | +| 下游接口 | `GET {misDriverBusinessBaseUrl}/project/mcpvlabpage`(MBX `mbv-service-business`) | +| 返回裁剪 | **C 类:取 data + 裁剪**,只保留 `name` / `projectUuid` ✅ | + +**参数表** + +| 参数名 | 类型 | 必填 | 默认 | 说明 | +|---|---|---|---|---| +| `creatorCode` | String | 否 | — | 当前登录人 **MIS 系统用户 ID**。有值 → 只查该用户涉及的项目 | + +> **入参规则**(源码 `MbxUserIdConverter`):`creatorCode` 必须是**纯数字**的 MIS 用户 ID; +> 为空、非数字、或查不到映射时,工具**不会**把用户条件传给下游(即退化成「不带用户过滤」查询),**不报错**。 + +**返回 JSON 结构** ✅ + +```jsonc +[ + { + "name": "string", // 项目名称:实际取自下游 portfolioProjectName + "projectUuid": "string" // 项目唯一标识,可直接喂给 queryDriverListNamesByProject + } +] +``` + +> **已核实**:下游 `ProjectPageInfo` **没有 `name` 字段**,所以源码里「`portfolioProjectName` 为空则回退 `name`」的兜底**永远不会触发**。 +> 实际取值路径只有 `portfolioProjectName` 一条;该字段为空的行会被**整条丢弃**(不返回)。 +> 因此 prompt 中应把 `name` 理解为「下游的项目名称(portfolioProjectName)」,不要去找别的名字来源。 + +**返回示例** + +```json +[ + { "name": "IdeaPad 5 Pro 16ARP8", "projectUuid": "018cd84ba0a82b020410bb40a1d85ba5" }, + { "name": "Legion 5 15IRX10", "projectUuid": "000c798c09d537f4f25f5b29046aebd1" } +] +``` + +**注意事项** + +- **本工具固定只取第 1 页、每页 100 条**(源码常量 `PROJECT_NAME_LIST_SIZE = 100`),即**最多返回 100 条项目**,且**没有分页参数**。 + 超过 100 条时**不会自动翻页**;需要按名称检索或取更多项目请用 `debuggingProjectQuery`。 + (该常量原为 `2`,2026-09-22 修正为 `100`。) +- 返回**不含分页信息**,是纯数组。 +- 两个字段都为空的下游记录会被过滤掉。 +- 空结果返回 `"[]"`。 + +**关联工具**:`queryDriverListNamesByProject`(承接 `projectUuid`)、`debuggingProjectQuery`(项目基础信息) + +--- + +### 3.3 `queryDriverListNamesByProject` + +**摘要**:根据项目组唯一标识或项目唯一标识,查询对应的**全部** Driver List Name,返回 `driverListVersionUuid` 与 `driverListName` 集合。 + +| 项 | 值 | +|---|---| +| 所属服务 | `mis-mcp-tool-driver` | +| Java 方法 | `DriverService.queryDriverListNamesByProject` | +| MCP 工具名 | `queryDriverListNamesByProject` | +| 下游接口 | `GET {misDriverBaseUrl}/driverlist/mcpvlabpage`(内部自动翻页,每页 100 条直到取完) | +| 返回裁剪 | **C 类:裁剪并去重** ✅ | + +**参数表** + +| 参数名 | 类型 | 必填 | 默认 | 说明 | 示例 | +|---|---|---|---|---|---| +| `projectGroupUuid` | String | 否 | — | 项目组唯一标识(与 `projectUuid` **至少给一个**) | `d5ac3039abf4c8302cccefc40f7056dd` | +| `projectUuid` | String | 否 | — | 项目唯一标识(与 `projectGroupUuid` **至少给一个**) | `018cd84ba0a82b020410bb40a1d85ba5` | + +**返回 JSON 结构** ✅ + +```jsonc +[ + { + "driverListVersionUuid": "string", // 驱动列表【版本】唯一标识 —— 给 queryDriverListVersion 用 + "driverListName": "string" // 驱动列表名称:实际取自下游 driverListName + } +] +``` + +> **已核实**:源码写的是「优先取 `nameRemark`,为空则回退 `driverListName`」,但下游**分页接口的 SQL 根本没查 `name_remark` 列**, +> 因此 `nameRemark` 在这个链路上**恒为 null**,回退分支**必然生效** —— 返回值里的 `driverListName` 实际就是下游的 `driverListName`。 +> (`nameRemark` 只在单查版本详情、即 §3.4 的返回里才有值。)对调用方而言:**只会拿到一个非空的 `driverListName`,直接用它做筛选即可**。 + +**返回示例** + +```json +[ + { "driverListVersionUuid": "93b947167dad2e8956a70358742f70cc", "driverListName": "MyLNBGroup_Win10 21H2_V02" }, + { "driverListVersionUuid": "c95301ca9485a1bb2102989f91e5eaf9", "driverListName": "MyLNBGroup_Win10 21H2_V01_20260407" } +] +``` + +**注意事项** + +- 返回的是**版本级**标识(`driverListVersionUuid`),**不是**驱动列表级标识;一个驱动列表有多个版本时会有多条。 +- 结果已按 `driverListVersionUuid` **去重**;名称为空、UUID 为空的记录会被跳过。 +- 工具内部会**自动翻页**(`size=100`)取全量,调用方**不需要**传分页参数,也没有分页参数。 +- 返回**不含分页信息**,是纯数组;空结果返回 `"[]"`。 +- 项目组/项目下驱动列表很多时,返回可能较长——prompt 里应引导模型**先按名称筛选**再取版本。 +- ⚠️ **已知下游缺陷**:下游分页接口对「**一个版本都没有的空驱动列表**」会抛数组越界 → 返回 **500**。 + 出现「Driver 接口业务失败」类报错时,除了参数问题,也要考虑该分组下存在空驱动列表这一可能。 + +**关联工具**:`queryDriverListVersion`(承接 `driverListVersionUuid`)、`queryProjectNameList`(提供 `projectUuid`) + +--- + +### 3.4 `queryDriverListVersion` + +**摘要**:根据驱动列表唯一标识查询驱动列表版本信息,用于拿到**驱动包的下载地址**。 + +| 项 | 值 | +|---|---| +| 所属服务 | `mis-mcp-tool-driver` | +| Java 方法 | `DriverService.queryDriverListVersion` | +| MCP 工具名 | `queryDriverListVersion` | +| 下游接口 | `GET {misDriverBaseUrl}/listversion/mcpvlab/{driverListUuid}` | +| 返回裁剪 | **B 类:取 data(未做字段裁剪)** ⚠️ | + +**参数表** + +| 参数名 | 类型 | 必填 | 默认 | 说明 | 示例 | +|---|---|---|---|---|---| +| `driverListVersionUuid` | String | **是** | — | 驱动列表版本唯一标识,来自 `queryDriverListNamesByProject` 的返回 | `93b947167dad2e8956a70358742f70cc` | + +**返回 JSON 结构** ⚠️ **透传(未裁剪)** + +MCP 层**只拆信封**,返回下游 `data` 的**完整内容**(`data` 为分页对象时取 `records`,为空时返回 `[]`)。 +`data` 的结构是 `DriverListVersionVO`: + +```jsonc +{ + "driverListVersionUuid": "string", + "nameRemark": "string", // 本接口有值(与分页列表不同) + "driverListUuid": "string", + "driverListVersion": 0, + "status": null, // 下游未映射,恒 null + "driverListReleaseDate": "yyyy-MM-dd", + "creatorCode": "string", + "modifierCode": "string", + "createTime": "string", + "modifyTime": "string", + "activityStatusKey": "string", + "homesFlag": true, + "driverListName": "string", + "projectGroupUuid": "string", + "projectGroupName": "string", + "spmsProjectName": "string", + "driverListGmStatus": 0, + "driverListStatus": 0, // 0-临时 1-草稿 2-发布 + "changeList": "string", + "remark": "string", + "comment": "string", + "osTypeKeyList": [0], + + // ★★★ 目标产物在这里 ★★★ + "driverPackageVersionList": [ + { + "packageFileUrl": "★★ 驱动包下载地址 —— prepareTaskFile 的 fileUrl 入参", + "...": "DriverPackageVersionVO 其余字段" + } + ], + "solidifiedDriverPackageVersionList": [ /* 结构同上,仅 driverListStatus==2(发布态)时有值 */ ], + + "releaseContactUuidList": ["string"], + "releaseContactList": [ /* DriverContactVO */ ], + "driverPoContactList": [ /* DriverContactVO */ ], + "packageVersionNum": 0, + "bu": 0, + "logoNeeded": [0], + "platform": "string", + "baseIntelBkcVersion": "string", + "specialField": "string" +} +``` + +> ### ⚠️ 最容易踩的坑:`packageFileUrl` **不在顶层** +> +> MCP 实现**不裁剪字段**,`packageFileUrl` 也**不是顶层字段**—— +> 它是嵌套元素 `DriverPackageVersionVO` 的字段,位于: +> +> - `driverPackageVersionList[].packageFileUrl`,或 +> - `solidifiedDriverPackageVersionList[].packageFileUrl`(仅发布态 `driverListStatus == 2` 时有值) +> +> **prompt 必须写成「从 `driverPackageVersionList[].packageFileUrl` 取值」**, +> 若写成「取返回的 `packageFileUrl`」会取到 `undefined`。 +> 建议优先读 `solidifiedDriverPackageVersionList`(发布态更稳定),为空再回退 `driverPackageVersionList`。 +> +> ✅ **2026-09-22 已修正**:本工具的 `description` 原先写「返回 `packageFileUrl` 的下载地址即可」, +> 与实现不符,现已改为明确说明该字段的嵌套位置(见 `DriverService.queryDriverListVersion`)。 + +**注意事项** + +- 入参 `driverListVersionUuid` 填的是**版本 UUID**(照抄 `queryDriverListNamesByProject` 的返回值)。 + MCP 源码内部把它命名成 `driverListUuid` 只是**命名失误**,下游实际按版本 UUID 查询,不影响调用。 +- 拿到的 `packageFileUrl` **不能直接**用于任务下发,必须再过 `prepareTaskFile`(见 §3.5)。 +- 该 URL 形如 `http://<文件服务>/mbvClient/downloadFile?objectName=...&bucketName=driver`,**必须带 `objectName` 与 `bucketName`**。 +- 传错成「驱动列表 UUID」时不会报错,而是 `data` 为 null → 返回 `[]`,需按空结果处理。 + +**关联工具**:`queryDriverListNamesByProject`(上游,提供 `driverListVersionUuid`)、`prepareTaskFile`(下游,消费 `packageFileUrl`) + +--- + +### 3.5 `prepareTaskFile` + +**摘要**:把「文件下载地址」对应的文件**重新上传到文件服务**,返回可用于测试任务下发的文件参数。 + +| 项 | 值 | +|---|---| +| 所属服务 | `mis-mcp-tool-driver` | +| Java 方法 | `DriverService.prepareTaskFile` | +| MCP 工具名 | `prepareTaskFile` | +| 下游接口 | ① `GET {misFileServiceBaseUrl}/mbvClient/downloadFile`(下载,`mbv-service-minio`)
② `POST {misFileServiceBaseUrl}/uploadFileSpecial`(上传到 `document` 桶) | +| 返回裁剪 | **C 类:裁剪** ✅ | + +**参数表** + +| 参数名 | 类型 | 必填 | 默认 | 说明 | 示例 | +|---|---|---|---|---|---| +| `fileUrl` | String | **是** | — | 文件下载地址,来自 `queryDriverListVersion` 返回的 `packageFileUrl` | `http://10.159.41.130:8080/mbvClient/downloadFile?objectName=20260413/16/55/33/xxx.zip&bucketName=driver` | + +> **`fileUrl` 必须带 `objectName` 与 `bucketName` 两个查询参数**,否则工具直接抛错(无法解析文件名/桶名)。 + +**返回 JSON 结构** ✅ + +```jsonc +{ + "fileUuid": "string", // ★ 任务下发要用的文件唯一标识 + "fileUrl": "string", // 上传后文件服务返回的访问地址 + "fileName": "string", // 文件名(取自 objectName 最后一段) + "fileExtensionKey": 0 // 文件扩展名标识;类型不固定(可能是数字/字符串/null),按原样传递即可 +} +``` + +**返回示例** + +```json +{ + "fileUuid": "8f2a1c9e4b7d4a1f9c3e5b6a7d8e9f01", + "fileUrl": "http://10.159.41.130:8080/mbvClient/downloadFile?objectName=document/20260922/xxx.zip&bucketName=document", + "fileName": "xxx.zip", + "fileExtensionKey": 3 +} +``` + +**它在链路中的位置**(**这是本工具存在的唯一理由**) + +``` +queryDriverListVersion → packageFileUrl →【prepareTaskFile】→ fileUuid + → creatOrSendTask 的 + requestBodyJson.model.children[].documentCopyDTOList[].fileUuid +``` + +> 为什么不能直接把 `packageFileUrl` 填进任务下发?因为 `creatOrSendTask` 的 `documentCopyDTOList` **只认 `fileUuid`**, +> 而 driver 桶的文件不落文件信息表、没有 `file_uuid`。重传到 `document` 桶才能换到一个可用的 `fileUuid`。 +> **因此这一步不可省略**,顺序也不能颠倒。 + +**注意事项** + +- ⏱ **耗时较长**:驱动包可能有几百 MB,内部超时为 **10 分钟**。prompt 里应提示用户「这一步较慢,请稍候」。 +- **不校验文件内容**,只做下载 + 上传;下载得到的是「HTTP 200 + 纯文本」形式的错误提示(如「文件不存在」)时会被拦截并报错。 +- 返回的 `fileUrl` 是**上传后**的新地址(`document` 桶),与入参的 `packageFileUrl`(`driver` 桶)**不同**,不要混用。 + +**关联工具**:`queryDriverListVersion`(上游,提供 `fileUrl`)、`creatOrSendTask`(下游,消费 `fileUuid`) + +--- + +### 3.6 `generatePreloadUpdateLink` + +**摘要**:根据项目信息生成 VLAB Preload 向导页面的 **preload 更新下载链接**。 + +| 项 | 值 | +|---|---| +| 所属服务 | `mis-mcp-tool-vlab`(端口 18769,`STREAMABLE`,端点 `/mcp`) | +| Java 方法 | `PreloadService.generatePreloadUpdateLink` | +| MCP 工具名 | `generatePreloadUpdateLink` | +| 下游接口 | `mbv-service-preload`:`/preload/preloadNameList` → `/preload/init` → `/preload/select` → `/preload/getOtherList` → `/preload/preloadVersion` → `/aod` | +| 返回裁剪 | **C 类:自行组装** ✅ | + +**参数表** + +| 参数名 | 类型 | 必填 | 默认 | 说明 | 示例 | +|---|---|---|---|---|---| +| `project` | String | **是** | — | 项目名称,**必须是 Preload 向导页面下拉选项中的值** | `IdeaPad 5 Pro 16ARP8` | +| `osVer` | String | 否 | 下拉列表**第一个**版本 | 操作系统版本,可用**页面展示名**(`description`)或**目录名**(`directory`) | `9.Win11` | + +> 其余页面字段(Location、Preload VER、OS Type、Language、OS Edition、HDD Config、模块穿梭框)**均按页面默认值自动提交**, +> 调用方**无需也无法**指定。 + +**返回 JSON 结构** ✅ —— **成功与失败是两种不同结构,prompt 必须能区分** + +**① 成功** + +```jsonc +{ + "downloadUrl": "string", // ★ 生成的 preload 更新下载链接 + "project": "string", // 实际匹配到的项目名(回显下游下拉里的原值) + "osVer": "string", // 实际匹配到的 OS 版本展示名 + "os": "string", // OS 目录名(directory) + "preloadVersion": "string", // 实际使用的 Preload VER + "locationName": "string" // 实际使用的 Location +} +``` + +**② 失败**(三种触发场景,`msg` 不同,附加字段也不同) + +```jsonc +{ + "success": false, // ★ 失败标志(注意:成功时【没有】success:true) + "msg": "string", // 失败原因 + // 以下附加字段随场景变化: + "project": "string", // 回显入参 + "osVer": "string", // 回显入参(可能为 null) + "os": "string", // 仅「未找到 Preload VER」场景 + "preloadVersion": "string", // 仅「未返回下载地址」场景 + "availableProjects": ["string"], // ★ 仅「Project 不在下拉选项中」场景 + "availableOsVer": ["string"] // ★ 仅「OS VER 不在下拉选项中」场景 +} +``` + +**失败场景对照表** + +| `msg` | 触发条件 | 可用来自纠偏的字段 | +|---|---|---| +| `Project 不在 Preload 向导下拉选项中` | `project` 与下游下拉值**不匹配**(忽略大小写、去空格后仍不匹配) | `availableProjects` —— 全部合法项目名 | +| `OS VER 不在 Preload 向导下拉选项中` | `osVer` 既不是某版本的 `description` 也不是 `directory` | `availableOsVer` —— 全部合法 OS 版本名 | +| `未找到对应的 Preload VER` | 项目+OS 组合下 select 返回空 | `project` / `osVer` / `os` | +| `生成 Preload 更新链接失败,下游未返回下载地址` | 走到 `/aod` 但没拿到地址 | `project` / `osVer` / `preloadVersion` | + +**返回示例** + +```json +{ + "downloadUrl": "http://mbxqas.lcfuturecenter.com/prod-api/preloadService/xxx/download?file=xxx.zip", + "project": "IdeaPad 5 Pro 16ARP8", + "osVer": "9.Win11", + "os": "Win11", + "preloadVersion": "PR9.0.0", + "locationName": "LCFC" +} +``` + +**注意事项** + +- ⚠️ **判成功的正确写法**:检查是否**存在 `downloadUrl`**,或检查 `success === false`。 + **不要**写 `success === true` —— 成功返回里**没有** `success` 字段。 +- `project` / `osVer` 都是**按下游下拉值精确匹配**(忽略大小写与首尾空格),**不是模糊查询**。 + 传错时不会返回空数组,而是返回**失败对象 + 候选列表**——这是自纠偏的关键: + **prompt 应引导模型用 `availableProjects` / `availableOsVer` 重新选取后再重试一次,而不是反复用同一个错值重试。** +- `osVer` 不传时取下拉列表**第一个**版本,与页面默认行为一致。 +- 该工具**会真实生成下载链接**(非只读查询),属于链路中副作用最强的一步。 + +**关联工具**:无上游依赖;项目名可参照 `debuggingProjectQuery` / `queryProjectNameList` 的 `name`(但**必须**用 Preload 页面下拉里的值) + +--- + +### 3.7 `creatOrSendTask` + +**摘要**:创建或下发测试任务。**本工作流中唯一的写操作**。 + +| 项 | 值 | +|---|---| +| 所属服务 | `mis-mcp-tool-vlab` | +| Java 方法 | `TaskService.creatOrSendTask` | +| MCP 工具名 | `creatOrSendTask`(**注意是 `creat` 不是 `create`**) | +| 下游接口 | `POST {misDispatcherBaseUrl}/mcpvlabcreatOrSendTask?fs={fs}&userId={userId}`,请求体为任务 JSON | +| 返回裁剪 | **B 类:取 data(未做字段裁剪)** ⚠️ | + +**参数表** + +| 参数名 | 类型 | 必填 | 默认 | 说明 | +|---|---|---|---|---| +| `creatorCode` | String | **是** | — | 当前登录人 **MIS 系统用户 ID**(纯数字)。服务内部转成 MBX `userId` 传给下游 | +| `requestBodyJson` | String | **是** | — | **任务请求体 JSON 字符串**。结构见下方“请求体”一节 | +| `fs` | Boolean | 否 | `false` | 随请求透传给下游的 `fs` 参数 | + +### 请求体 `requestBodyJson` 结构 ✅(已核实下游 `DispatcherServiceImpl.commonCreateTask`) + +> **`requestBodyJson` 必须是一个 JSON 字符串**(不是对象)。模型调用时需要把整个任务体**序列化成字符串**再作为参数传入。 +> 下游用 Hutool 解析(非强类型 DTO),只有一个字段是硬性必填:**`other.type`**。 + +**顶层结构** + +```jsonc +{ + "base": { /* TestTask —— 任务基本信息 */ }, + "other": { "type": 2 }, // ★ 必填!1=只保存 2=创建并下发 + "rule": { /* TestTaskUnitBinding —— 选机器规则 */ }, + "model": { /* TestModelNode —— 任务步骤树(模型视图时必填) */ }, + "testTaskIssueTypeCode": 1, // 1=模型视图(默认) 2=用例视图 + "testCaseAssociationVersionUuidList": [] // 仅用例视图(2)时必填 +} +``` + +**`base` 常用字段**(`TestTask`) + +| 字段 | 类型 | 说明 | +|---|---|---| +| `testTaskName` | String | 任务名称 | +| `testTaskTypeKey` | Integer | 1=测试任务,2=debug 任务 | +| `projectUuid` | String | 项目唯一标识 | +| `remark` | String | 备注 | +| `scheduleTime` | Date | 定时下发时间(**必须晚于当前时间**,否则报错) | + +> 下发时会被下游**强制覆盖**的字段(传了也不生效):`testTaskCode`(重新生成)、 +> `testModelUuid` / `testModelVersionUuid`、`ownerCode` / `creatorCode` / `operatorCode` / `modifierCode`(一律用 `userId` 覆盖)、 +> `activityStatusKey`、`testTaskStatusKey`、`submitState`。 + +**`other` 字段** + +| 字段 | 类型 | 说明 | +|---|---|---| +| `type` | Integer | **必填**。1=只保存,2=创建并下发。**为 null 会导致下游 NPE(500)** | +| `isClear` | Boolean | 是否清除当前执行任务 | +| `isDevice` | Boolean | — | +| `scheduleTime` | Date | 定时下发时间 | + +**`rule` 常用字段**(`TestTaskUnitBinding`)——「选哪些机器」 + +| 字段 | 类型 | 说明 | +|---|---|---| +| `testTaskUnitBindingRuleKey` | Integer | 绑定规则:1=ssn 2=sku 3=mo 4=model 5=project | +| `bindingRulePriority` | Integer | 优先级 | +| `entityPrimaryKeyList` | Array\ | ★ 选中的机器。**元素是 JSON 字符串,不是裸 UUID**,形如 `"{\"key\":\"\",\"value\":\"<机器名>\"}"` | + +**`model` 关键字段与文件挂载点**(`TestModelNode`,可递归) + +```jsonc +{ + "testModelUuid": "string", + "testModelVersionUuid": "string", + "testModelName": "string", + "nodeSequenceTypeKey": 1, // 1 串行 / 2 并行 + "children": [ + { + "testModelUuid": "string", + "testModelVersionUuid": "string", + "children": [], + + // ★★★ 驱动包文件挂在这里 ★★★ + "documentCopyDTOList": [ + { + "fileUuid": "★ 填 prepareTaskFile 返回的 fileUuid", + "documentName": "string", // 可选 + "documentVersionNo": "string" // 可选 + } + ] + } + ] +} +``` + +> **`documentCopyDTOList` 里下游实际只用 `fileUuid`**(做判重与建文档映射),其余字段可选。 +> 这与 §3.5 的链路说明一致。 + +**返回** ✅ / ⚠️ + +| 形态 | 内容 | 触发条件 | +|---|---|---| +| **纯文本** ✅ | `请输入当前登录人信息` | `creatorCode` 为空,**或** MIS→MBX 用户 ID 转换失败 | +| 异常 | `IllegalArgumentException: 任务请求体 requestBodyJson 不能为空` | `requestBodyJson` 为空 | +| **JSON** ⚠️ | 见下 | 正常调用 / 下游业务失败 | + +**成功返回**(下游 `R.ok(statusMap, "任务下发已提交,正在生成中")`,MCP 取出 `data`) + +```jsonc +{ + "offLineTestUnit": "uuid1,uuid2,", // 离线机器 uuid,逗号拼接、末尾带逗号 + "unWorkingTestUnit": "uuid3,", // 使用中机器 uuid,同上 + "testTaskUuid": "string", // ★ 新建任务唯一标识 + "testTaskCode": "string" // ★ 任务编号 +} +``` + +**失败返回**(MCP 同样按 B 类处理:`data` 为 null 时返回 `[]`) + +| 下游 `msg` | 含义 | +|---|---| +| `参数异常请检查` | ⚠️ 请求体为空。**下游这里返回的是 `code="0"`**,MCP 会当成成功路径处理,返回 `[]` —— 无法只靠 code 区分,需结合 `msg` | +| `任务定时下发时间需在当前时间之后,请重新设置!` | `scheduleTime` 早于当前时间 | +| `该任务含有禁用的用例关联版本,请修改重试!` | 用例关联版本被禁用 | +| `关联版本对应的绑定模型数据为空!` / `testCaseAssociationVersionUuid参数不得为空!` | 用例视图参数缺失 | +| `该任务含有不可用的原子模型,请修改重试!` | ⚠️ 此时下游 `code="26"`,`data` 是**列表** `[{deviceTypeKey, testUnitUuid, testUnitName}]`,MCP 会把该列表返回 | + +**注意事项** + +- ⚠️ **写操作**:会真实创建/下发任务,不可撤销。prompt 中应要求**先展示完整 `requestBodyJson` 给用户确认**再执行。 +- `fileUuid` **必须**来自 `prepareTaskFile`,不能用 `packageFileUrl` 顶替(§3.5)。 +- `creatorCode` 必须是**纯数字**的 MIS 用户 ID;非数字会走到「请输入当前登录人信息」分支,而不是报错——**prompt 需能识别这句纯文本并提示用户提供正确 ID**。 +- `other.type` 忘了传 → 下游 NPE(500),这是最常见的构造错误。 +- `fs=true` 会**跳过「机器未绑定设备类型」校验**并强制下发;默认 `false` 时该校验不过会返回 `code="26"`。 +- 参数名与下游路径都是 `creat...`(少一个 e),拼错会找不到工具。 + +**关联工具**:`prepareTaskFile`(上游,提供 `fileUuid`) + +--- + +### 3.8 `queryTestUnitListByProjectName` + +**摘要**:根据项目名称、五码查询机器列表的数据,按列表分页显示。 + +| 项 | 值 | +|---|---| +| 所属服务 | `mis-mcp-tool-vlab` | +| Java 方法 | `UnitService.queryTestUnitListByProjectName` | +| MCP 工具名 | `queryTestUnitListByProjectName` | +| 下游接口 | `GET {misVlabBaseUrl}/testUnit/qv/mcpvlabqueryTestPcList` | +| 返回裁剪 | **B 类:取 data(未做字段裁剪)** ⚠️ | + +**参数表** + +| 参数名 | 类型 | 必填 | 默认 | 说明 | 示例 | +|---|---|---|---|---|---| +| `projectName` | String | **是** | — | 项目名称 | `TNB Commercial OS Refresh-X_CS24` | +| `modelCode` | String | **是** | — | 五码 | `LT4EN` | +| `ownerCode` | String | **是** | — | 当前用户的 UUID(**MIS 用户 ID**,纯数字) | — | +| `current` | Long | 否 | `1` | 当前页码 | `1` | +| `size` | Long | 否 | `10` | 每页条数 | `10` | + +> **参数名与下游字段名不一致**:工具入参叫 `modelCode`(五码),传给下游时字段名是 **`modelName`**。调用方只需按 `modelCode` 传值。 + +**返回** ✅ / ⚠️ + +| 形态 | 内容 | 触发条件 | +|---|---|---| +| **纯文本** ✅ | `登录人信息转换异常` | `ownerCode` 有值但 MIS→MBX 转换失败 | +| 异常 | `IllegalArgumentException: 项目名称 projectName 不能为空` | `projectName` 为空 | +| **JSON** ⚠️ | 下游 `data`(含 `records` 时只取 `records`;`data` 为空返回 `[]`) | 正常调用 | + +**返回 JSON 结构** ⚠️ 透传(下游 `data` 是 MyBatis-Plus 分页对象,MCP 取出 `records`) + +```jsonc +[ + { + "testUnitUuid": "string", // ★ 测试机唯一标识 —— 给 queryTestPcByUuids 用 + "testUnitName": "string", // 机器名称 + "shippingSn": "string", // 出货流水号 / 机器 SSN + "onlineStatus": 2, // ★ 在线状态:1=离线 2=在线 + "workingStatusKey": 2, // 机器状态:1=闲置中 2=使用中 + "lockStatus": 0, // 锁定状态:0=未锁定 1=已锁定 + "tokenStatus": 0, // 认领状态 + "borrowStatus": 0, // 借用状态 + "isStore": 0, // 是否收藏:0/1 + "projectCode": "string", // 项目编号 + "projectName": "string", // 项目名称 + "projectPhase": "string", // 项目阶段 + "programName": "string", // 项目集名称 + "modelName": "string", // 五码 + "groupName": "string", // 分组名称 + "testUnitGroupUuid": "string", // 分组 uuid + "testUnitTypeKey": 0, // 测试机类型键 / 物料组 + "matterDescription": "string", // 物料描述 + "fgPn": "string", // SKU + "skuShortName": "string", // SKU 别名 + "skuExtensionNo": "string", // SKU 扩展序号 + "mbPn": "string", // 主板料号 + "mbSn": "string", // 主板流水号 + "fgSn": "string", // 整机流水号 + "gerberVersion": "string", // 主板 Gerber 版本 + "ipAddress": "string", // IP + "ipAddressBridge": "string", // 桥接 IP + "ownerCode": "string", // 机器管理员 + "ownerName": "string", // 管理员名称 + "userCode": 0, // 当前使用人编号 + "userName": "string", // 当前使用人名称 + "groupLeaderName": "string", // Smart FAI 组长姓名 + "groupLeaderCode": "string", // Smart FAI 组长工号 + "testUnitSource": "string", // 数据源:1=手工导入 2=开机注册 + "comments": "string", // 备注 + "gmtCreate": "yyyy-MM-dd HH:mm:ss", // 录入时间 + "mbUuid": "string", // 主板 uuid + + // 以下字段恒为 null(下游 SQL 未查出) + "mo": null, + "vncParam": null, + "lcfcUuid": null + } +] +``` + +**关键字段速查** + +| 字段 | 取值 | 说明 | +|---|---|---| +| `testUnitUuid` | — | 传给 `queryTestPcByUuids` | +| `onlineStatus` | `1` 离线 / `2` 在线 | 在线状态 | +| `workingStatusKey` | `1` 闲置中 / `2` 使用中 | 机器状态 | +| `lockStatus` | `0` 未锁定 / `1` 已锁定 | 锁定状态 | + +**注意事项** + +- 三个入参(`projectName` / `modelCode` / `ownerCode`)在源码中**都是必填**。 +- ⚠️ **`ownerCode` 不能留空**:MCP 允许它为空(不报错),但下游会直接返回 + `未获取到用户信息`(`code="1"`)→ MCP 抛异常。**必须传纯数字的 MIS 用户 ID。** +- ⚠️ **`modelCode`(五码)实际不生效**:已核实下游该查询语句的 WHERE 子句里 + **没有 `modelName` 过滤条件**,MCP 传过去的五码被**静默忽略**。 + 即实际过滤条件只有 `projectName`(+ 用户维度)。 + **prompt 不要声称「按五码精确筛选」**;若确需按五码收窄,应在拿到结果后**在模型侧自行过滤** `modelName` 字段。 +- 与已废弃的 `queryTestUnitList` 区分:**按项目查机器用本工具**(见 §6.3)。 + +**关联工具**:`queryTestPcByUuids`(下游,承接 `testUnitUuid`)、`debuggingProjectQuery`(提供 `modelCode` 五码) + +--- + +### 3.9 `queryTestPcByUuids` + +**摘要**:根据测试机 UUID 列表**批量**查询测试机在线状态信息。 + +| 项 | 值 | +|---|---| +| 所属服务 | `mis-mcp-tool-vlab` | +| Java 方法 | `TestUnitQvService.queryTestPcByUuids` | +| MCP 工具名 | `queryTestPcByUuids` | +| 下游接口 | `POST {misVlabBaseUrl}/testUnit/queryTestPcByUuids`,请求体 `{"testUnitUuids": [...]}` | +| 返回裁剪 | **B 类:取 data(未做字段裁剪)** ⚠️ | + +**参数表** + +| 参数名 | 类型 | 必填 | 默认 | 说明 | 示例 | +|---|---|---|---|---|---| +| `testUnitUuids` | Array\ | **是** | — | 测试机 UUID **列表**(**批量传入**,不要循环单传) | `["0062c70e645c4d0a824d8b2c3134015d"]` | + +**返回** ✅ / ⚠️ + +| 形态 | 内容 | 触发条件 | +|---|---|---| +| 异常 | `IllegalArgumentException: testUnitUuids 不能为空` | 入参为空列表或 null | +| **JSON** ⚠️ | 下游 `data` 本身就是一个**数组**(非分页) | 正常调用 | + +**返回 JSON 结构** ⚠️ 透传 + +> 与 §3.8 不同,本接口的 `data` **直接就是数组**(不是分页对象),无需再取 `records`。 + +```jsonc +[ + { + "testUnitUuid": "string", // 机器统一标识码 + "testUnitName": "string", // 机器名称 + "onlineStatusKey": 2, // ★★★ 在线状态:1=离线 2=在线 + "workingStatusKey": 2, // 机器状态:1=空闲 2=使用中 + "ownerCode": 0, // 所有者编号(数字) + "ownerName": "string", // 所有者名称 + "userCode": 0, // 使用者编号(数字) + "sortField": null, // 继承自 SortEntity,非查询用途 + "positiveOrReverse": null, + + // ❌ 陷阱:这个字段名很像在线状态,但本接口恒为 null + "onlineStatus": null, + + // 其余约 50 个字段(projectName / fgSn / lockStatus / gmtCreate …)也会出现,但恒为 null + "projectName": null, + "fgSn": null, + "lockStatus": null + } +] +``` + +> ### ⚠️ 最容易踩的坑:`onlineStatus` 是 null,要用 `onlineStatusKey` +> +> 下游 `Machines` 类里**同时存在**两个长得像在线状态的字段: +> - **`onlineStatusKey`** —— ✅ 本接口真正返回的在线状态(1 离线 / 2 在线) +> - `onlineStatus` —— ❌ 本接口的 SQL **没有查这一列**,恒为 `null` +> +> **prompt 里必须写 `onlineStatusKey`**。若写 `onlineStatus`,会拿到 `null` 并被误判成「离线/未知」。 + +**注意事项** + +- **批量语义**:一次传入多个 UUID,比逐个调用高效——prompt 里应显式要求**合并成一次调用**。 +- UUID 来源:`queryTestUnitListByProjectName` 返回的 `testUnitUuid`。 +- 空数组会**直接抛异常**,不会返回空结果。 +- 返回体很大(每台机器约 50+ 个恒 null 字段),**机器数量多时注意上下文占用**; + 建议 prompt 里只提取 `testUnitUuid` / `testUnitName` / `onlineStatusKey` 三个字段。 + +**关联工具**:`queryTestUnitListByProjectName`(上游,提供 `testUnitUuids`) + +--- + +## 4. 典型工作流(编排剧本) + +> 每条链路都给出**步骤 → 工具 → 参数来源 → 产出**,可直接转成 SKILL 的调用顺序。 + +### 4.1 驱动包下发测试任务(最长链路,5 步) + +| 步骤 | 工具 | 关键参数 | 取值来源 | 产出 | +|---|---|---|---|---| +| 1 | `queryProjectNameList` 或 `debuggingProjectQuery` | `projectName` / `creatorCode` | 用户输入 | `projectUuid` | +| 2 | `queryDriverListNamesByProject` | `projectUuid`(或 `projectGroupUuid`) | 步骤 1 | `driverListVersionUuid`、`driverListName` | +| 3 | `queryDriverListVersion` | `driverListVersionUuid` | 步骤 2 | `driverPackageVersionList[].packageFileUrl`(**非顶层字段**,见 §3.4) | +| 4 | `prepareTaskFile` | `fileUrl` = `packageFileUrl` | 步骤 3 | `fileUuid` ⏱ 耗时较长 | +| 5 | `creatOrSendTask` | `creatorCode`、`requestBodyJson`(含 `fileUuid`) | 步骤 4 + 用户输入 | 任务创建/下发结果 | + +**决策点**(prompt 里必须交代清楚): +- 步骤 2 若匹配到**多个** `driverListName` → **列出让用户选**,不要自选。 +- 步骤 4 耗时可能达数分钟 → 应提示用户。 +- 步骤 5 是**写操作**(会真实创建/下发任务)→ 建议**先展示完整 `requestBodyJson` 给用户确认**再执行。 + +### 4.2 Preload 更新链接(单步 + 自纠偏) + +| 步骤 | 工具 | 参数 | 产出 | +|---|---|---|---| +| 1 | `generatePreloadUpdateLink` | `project`(必填,页面下拉精确值)、`osVer`(可选) | `downloadUrl` | + +**失败分支**:返回 `success: false` 时,用 `availableProjects` / `availableOsVer` 让用户确认后再**重试一次**(见 §3.6)。 + +### 4.3 项目 → 测试机 → 在线状态(3 步) + +| 步骤 | 工具 | 关键参数 | 产出 | +|---|---|---|---| +| 1 | `debuggingProjectQuery` | `projectName` / `modelCode` | 项目信息(含五码 `modelCode`) | +| 2 | `queryTestUnitListByProjectName` | `projectName`、`modelCode`(五码)、`ownerCode` | 机器列表(含 `testUnitUuid`) | +| 3 | `queryTestPcByUuids` | `testUnitUuids` = 步骤 2 的 UUID 数组 | 在线状态 | + +> 步骤 2 的三个参数在源码中**均为必填**(未标 `required = false`)。`ownerCode` 传 MIS 用户 ID。 + +--- + +## 5. 生成 SKILL 与 Prompt 的指引 + +> 本节把上文的结构化信息转成**可直接抄用的模板**。 + +### 5.1 推荐的 SKILL 结构 + +```markdown +--- +name: se-driver-workflow # 建议按业务域命名,见下方 5.1.1 +description: > + 处理 SE 驱动的查询与测试任务下发。当用户提到「驱动列表」「Driver List」 + 「驱动包版本」「下发测试任务」「preload 更新链接」时使用。 + 覆盖 9 个 MCP 工具:项目查询、驱动列表查询、驱动包下载、文件准备、任务下发、 + Preload 链接生成、测试机查询。 +--- + +# 工具调用顺序(严格按此顺序,ID 逐跳传递) +1. queryProjectNameList / debuggingProjectQuery → 拿 projectUuid / 项目信息 +2. queryDriverListNamesByProject → 拿 driverListVersionUuid +3. queryDriverListVersion → 拿 packageFileUrl +4. prepareTaskFile → 拿 fileUuid(耗时较长) +5. creatOrSendTask → 下发任务 + +# 详细参数与返回结构 +见 `references/se工作流mcp工具API文档.md`(按需加载,不要一次性全读) +``` + +**5.1.1 SKILL 拆分建议**:按**链路**而非按工具拆,推荐 3 个: + +| SKILL 名(建议) | 覆盖工具 | 触发场景 | +|---|---|---| +| `se-driver-workflow` | ①~⑤ + ⑦(`debuggingProjectQuery`/`queryProjectNameList`/`queryDriverListNamesByProject`/`queryDriverListVersion`/`prepareTaskFile`/`creatOrSendTask`) | 驱动查询与任务下发 | +| `se-preload-link` | ⑥ `generatePreloadUpdateLink` | 生成 Preload 更新链接 | +| `se-test-unit-query` | ⑧⑨ `queryTestUnitListByProjectName`/`queryTestPcByUuids` | 测试机列表与在线状态 | + +### 5.2 工具 → 自然语言触发词 + +写 SKILL 的 `description` 时直接取用,能显著提高工具被正确选中的概率: + +| 工具 | 用户可能说的话 | +|---|---| +| `debuggingProjectQuery` | 「查一下 XX 项目」「XX 项目的五码/四码/负责人」「项目别名是 XX 的项目」 | +| `queryProjectNameList` | 「有哪些项目」「我能看到哪些项目」「项目名和 projectUuid」 | +| `queryDriverListNamesByProject` | 「XX 项目下的 Driver List」「这个项目组有哪些驱动列表」「驱动列表版本」 | +| `queryDriverListVersion` | 「这个驱动列表的包在哪下载」「驱动包的下载地址」 | +| `prepareTaskFile` | 「准备一下这个文件」「把驱动包传上去」 | +| `generatePreloadUpdateLink` | 「生成 preload 更新链接」「Preload 更新包的下载地址」 | +| `creatOrSendTask` | 「下发测试任务」「创建测试任务」「跑一下这个驱动」 | +| `queryTestUnitListByProjectName` | 「XX 项目有哪些机器」「五码 XX 的测试机」 | +| `queryTestPcByUuids` | 「这几台机器在线吗」「机器在线状态」 | + +### 5.3 编排 Prompt 模板 + +**模板 A:驱动包下发测试任务(最完整链路)** + +```text +目标:把「{项目名}」项目下「{驱动列表名匹配词}」的驱动包,下发测试任务。 + +请严格按顺序执行,每步只用上一步的返回结果,不要跳步或猜 ID: + +1. 调用 queryDriverListNamesByProject,参数 projectUuid = {projectUuid}(若只知道项目名, + 先用 queryProjectNameList 拿 projectUuid)。 + → 从返回数组中选出名称匹配「{驱动列表名匹配词}」的那条,记下它的 driverListVersionUuid。 + → 如果匹配到多条,先列出来问我要哪一个,不要自己选。 + +2. 调用 queryDriverListVersion,参数 driverListVersionUuid = 上一步的值。 + → 从返回中取出 packageFileUrl。 + +3. 调用 prepareTaskFile,参数 fileUrl = 上一步的 packageFileUrl。 + → 这一步会下载并重新上传驱动包,可能耗时数分钟,请告知我在处理中。 + → 取出返回的 fileUuid。 + +4. 调用 creatOrSendTask: + - creatorCode = {当前登录人 MIS 用户 ID} + - requestBodyJson = 见下方模板,把 fileUuid 填入 + model.children[].documentCopyDTOList[].fileUuid + - 执行前把完整 requestBodyJson 展示给我确认。 + +【重要】任何一步失败就停下来报告原因,不要用猜测的参数重试。 +``` + +**模板 B:Preload 更新链接** + +```text +帮我生成「{项目名}」的 preload 更新链接(OS VER 用 {9.Win11 / 不指定})。 + +调用 generatePreloadUpdateLink,参数 project = "{项目名}",osVer = "{osVer}"。 + +判断结果: +- 返回里有 downloadUrl → 成功,把链接给我。 +- 返回里有 "success": false → + · 若带 availableProjects,从里面挑最接近的候选,把候选列表给我确认后再重试一次; + · 若带 availableOsVer,同理。 + · 不要用同一个错值反复重试。 +``` + +**模板 C:测试机在线状态** + +```text +1. 调用 queryTestUnitListByProjectName: + projectName = "{项目名}",modelCode = "{五码}",ownerCode = "{当前登录人 MIS 用户 ID}" + → 从返回里取出各机器的 testUnitUuid。 +2. 调用 queryTestPcByUuids,testUnitUuids = 上一步的 UUID 数组(批量传入,不要一次一个)。 + → 汇总每台机器的在线状态给我。 +``` + +### 5.4 反模式(写 prompt 时明确禁止) + +| ❌ 不要这样写 | ✅ 应该这样写 | 原因 | +|---|---|---| +| 让模型「猜」或「编」`projectUuid` / `driverListVersionUuid` 等 UUID | 强制从上一个工具的返回中取值 | UUID 无法凭空生成,编造必然查不到 | +| 用 `queryProjectNameList` 取超过 100 条项目 | 用 `debuggingProjectQuery` 检索项目 | 前者单页最多 100 条且无分页参数(§3.2) | +| 把 `packageFileUrl` 直接填进 `creatOrSendTask` | 先过 `prepareTaskFile` 换成 `fileUuid` | 任务下发只认 `fileUuid`(§3.5) | +| 写「取返回的 `packageFileUrl`」 | 写「取 `driverPackageVersionList[].packageFileUrl`」 | 它不是顶层字段(§3.4) | +| 用 `onlineStatus` 判断测试机在线状态 | 用 `onlineStatusKey` | `onlineStatus` 恒为 null(§3.9) | +| 声称 `queryTestUnitListByProjectName` 按五码筛选 | 说明只按项目名筛选,五码需自行过滤 | 下游忽略 `modelName`(§3.8) | +| 让模型自行拼 `other.type` 等必填项 | 在 prompt 里给出完整请求体模板 | 漏传必填项会 500(§3.7) | +| 用 `success === true` 判断 Preload 成功 | 判断 `downloadUrl` 是否存在 / `success === false` | 成功返回里没有 `success` 字段(§3.6) | +| 对 `generatePreloadUpdateLink` 传模糊项目名 | 传 Preload 页面下拉里的**精确**值 | 该工具是精确匹配,不匹配会返回失败对象(§3.6) | +| 假设返回一定是 JSON | 先看首字符:`请`/`登` 开头是纯文本提示 | 两个工具会返回**纯文本**(§2.4) | +| 一次性读取整份 API 文档 | 按需加载对应工具小节 | 文档较长,全量加载浪费上下文 | + +--- + +## 6. 附录 + +### 6.1 MCP 接入信息 + +| 服务 | 端口 | `protocol` | 配置来源 | 工具数(本工作流涉及) | +|---|---|---|---|---| +| `mis-mcp-tool-debugging` | 18780 | `STATELESS` | 仓库 `application.yml` | 1 | +| `mis-mcp-tool-driver` | 18767 | `STATELESS` | 仓库 `application.yml` | 4 | +| `mis-mcp-tool-vlab` | 18769 | `STREAMABLE`(`mcp-endpoint: /mcp`) | **Nacos**(仓库内无此配置) | 4 | + +> ⚠️ **鉴权由 MCP 客户端透传**:调用方(LLM 侧)**不需要**把 token 作为工具参数传入。 +> 凭证走 MCP 请求头 `auth` / `clientid`,由服务端 `MisAuthForestInterceptor` 注入下游。 +> 排查「工具连不上」时,别只看仓库代码——`vlab` 的协议配置**不在代码里**,在 Nacos。 + +### 6.2 下游接口与白名单对照 + +下列路径**必须**存在于对应服务的 Nacos `security.oauth2.client.ignore-urls`(MBX 侧)或网关 `ignore.whites`(MIS 侧), +否则工具上线后必然 **401 / 403**。 + +| # | 工具 | 下游路径 | 下游服务 | 白名单位置 | 快照核对 | +|---|---|---|---|---|---| +| 1 | `debuggingProjectQuery` | `/debugging/project` | `mis-debugging` | **MIS 网关** `mis-gateway.yml` → `ignore.whites` | ✅ 已存在 | +| 2 | `queryProjectNameList` | `/project/mcpvlabpage` | `mbv-service-business` | `mbv-service-business-dev.yml` | ✅ 已存在 | +| 3 | `queryDriverListNamesByProject` | `/driverlist/mcpvlabpage` | `mbx-service-driver` | `mbx-service-driver-dev.yml` | ✅ 已存在 | +| 4 | `queryDriverListVersion` | `/listversion/mcpvlab/*` | `mbx-service-driver` | 同上 | ✅ 已存在 | +| 5 | `prepareTaskFile` | `/mbvClient/downloadFile`、`/uploadFileSpecial` | `mbv-service-minio` | `mbv-service-minio-dev.yml` | ✅ 已存在 | +| 6 | `generatePreloadUpdateLink` | `/preload/preloadNameList`、`/preload/init`、`/preload/select`、`/preload/getOtherList`、`/preload/preloadVersion`、`/aod` | `mbv-service-preload` | `mbv-service-preload-dev.yml` | ✅ 已存在(6 条齐全) | +| 7 | `creatOrSendTask` | `/mcpvlabcreatOrSendTask` | `mbv-service-dispatcher` | `mbv-service-dispatcher-dev.yml` | ✅ 已存在 | +| 8 | `queryTestUnitListByProjectName` | `/testUnit/qv/mcpvlabqueryTestPcList` | `mbv-service-business` | `mbv-service-business-dev.yml` | ✅ 已存在 | +| 9 | `queryTestPcByUuids` | `/testUnit/queryTestPcByUuids` | `mbv-service-business` | 同上 | ✅ 已存在 | + +> **路径参数写 `*` 不写 `{}`**(如 `/listversion/mcpvlab/*`)。 +> **本节结论基于本地 Nacos 快照**(`docs/nacos/`),快照是导出副本、**随时可能过期**, +> 上线前请以**远程 Nacos 实际值**为准。快照文件名是 `*-dev.yml` 但装的是 **qas** 环境的值。 + +### 6.3 易混淆 / 已废弃工具 + +| 工具 | 状态 | 说明 | +|---|---|---| +| `queryTestUnitList` | ⚠️ **已废弃**(源码 javadoc 明确标注「废弃」,但 `@Tool` 注解**未摘除**,仍会暴露给模型) | 参数多且语义宽泛,**不要**在新的 SKILL / prompt 里使用;按项目查机器请用 `queryTestUnitListByProjectName`,按 UUID 批量查用 `queryTestPcByUuids` | +| `debuggingProjectQuery` | 命名特殊 | 源码用 `@Tool(name = ...)` 显式指定,**Java 方法名是 `queryProjectList`**,两者不同名;调用时用 `debuggingProjectQuery` | +| `creatOrSendTask` | 拼写特殊 | 是 `creat` 不是 `create`,**必须原样使用** | +| `queryProjectNameList` vs `debuggingProjectQuery` | 数据源不同 | 前者查 MBX 侧,后者查 SE 同步侧;`name` 值对应(§1.3) | + +### 6.4 高风险陷阱汇总(写 prompt 前必读) + +> 以下每一条都来自**源码逐行核对**,都是「照直觉写就会错」的地方。 + +| # | 陷阱 | 正确做法 | 出处 | +|---|---|---|---| +| 1 | 以为 `queryDriverListVersion` 顶层有 `packageFileUrl` | 取 **`driverPackageVersionList[].packageFileUrl`**(或 `solidifiedDriverPackageVersionList[]`) | §3.4 | +| 2 | 用 `onlineStatus` 判断测试机在线状态 | 用 **`onlineStatusKey`**(`onlineStatus` 恒为 null) | §3.9 | +| 3 | 以为 `queryTestUnitListByProjectName` 会按五码筛选 | **`modelCode` 被下游静默忽略**;只按 `projectName` 过滤,需要五码请自行在结果里过滤 `modelName` | §3.8 | +| 4 | 给 `queryTestUnitListByProjectName` 传空 `ownerCode` | **必须传纯数字 MIS 用户 ID**,否则下游报「未获取到用户信息」 | §3.8 | +| 5 | 用 `success === true` 判断 Preload 成功 | 判断 **`downloadUrl` 是否存在** / `success === false`(成功返回里**没有** `success` 字段) | §3.6 | +| 6 | `creatOrSendTask` 漏传 `other.type` | **必须传**(`1` 只保存 / `2` 创建并下发),否则下游 NPE 500 | §3.7 | +| 7 | 把 `creatOrSendTask` 的空请求体当成失败 | 该场景下游返回 **`code="0"` + `msg="参数异常请检查"`**,是**假成功**,需结合 `msg` 判断 | §3.7 | +| 8 | 用 `queryProjectNameList` 拉取超过 100 条项目 | 它**单页最多 100 条且无分页**;项目检索用 `debuggingProjectQuery` | §3.2 | +| 9 | 假设返回值一定是 JSON | 两个工具会返回**纯文本**(`请输入当前登录人信息` / `登录人信息转换异常`) | §2.4 | +| 10 | 把 `packageFileUrl` 直接填进 `creatOrSendTask` | 必须先过 `prepareTaskFile` 换成 `fileUuid` | §3.5 | +| 11 | 把 `creatOrSendTask` 当只读查询 | 它是**写操作**,prompt 应先展示请求体让用户确认 | §3.7 | + +### 6.5 残留不确定项 + +> 以下是本次核对**未能完全确证**的部分,不要在这些细节上做硬编码假设。 + +| # | 事项 | 状态 | 建议 | +|---|---|---|---| +| 1 | `debuggingProjectQuery` 返回的 `projectUuid` 是 number 还是 string | 下游类型为 `Long`,且全局注册了 `BigNumberSerializer`(值超 2^53-1 时输出**字符串**)。源码示例值 `2049298006288687105` 是 19 位雪花 ID → **实际应为字符串**;小 ID 历史数据可能为数字 | 统一**按字符串处理**(比较前归一) | +| 2 | MyBatis-Plus `Page` 序列化出的完整字段集 | 仅实锤 `records` / `total`(前端代码印证);`size`/`current`/`pages`/`orders` 等为框架常规字段,**未从 jar 逐项确认** | 只依赖 `records` / `total` | +| 3 | 下游抛 `RuntimeException`(如「生成任务编号失败」)经全局异常处理后的 JSON 结构 | **未确认** | 按 MCP 抛异常处理即可 | +| 4 | `queryTestUnitListByProjectName` 的 `machineListType` 分支 | MCP 不传该参数,走默认 SQL;另两条分支 SQL 未逐条核对 | 无需关注 | +| 5 | `debuggingProjectQuery` 的 `ppmQuery` 字段 | **已核实**:恒为 `null`(只作查询条件,不进结果映射) | 不要从返回值读它 | +| 6 | 值为 `null` 的字段是否会**整键消失** | **已核对**:下游无 `spring.jackson.*` 配置,MIS 共享配置 `application-common.yml` 未设 `default-property-inclusion` → **null 字段会原样输出** | 字段存在,判空即可 | + +> **另需注意**:`mbv-service-test-unit` 模块里存在一份**同名**的 `/testUnit/queryTestPcByUuids` 实现, +> 但网关 `/business` 前缀落到的是 **`mbv-service-business`**,以本文档结论为准。 + +--- + +**文档结束** · 生成于 2026-09-22 · 基线 `mis-mcp-server@dev` +