diff --git a/cad-mcp-aec.md b/cad-mcp-aec.md index a562e2a..98508f2 100644 --- a/cad-mcp-aec.md +++ b/cad-mcp-aec.md @@ -13,9 +13,9 @@ SourceCode/Code2026/cad_mcp_aec/ ├── StdAfx.h / StdAfx.cpp 与 cad_mcp_plugins 同款 MFC 预处理头 ├── mcp_aec.cpp 入口:EXPORT_MCP_PLUGIN_VERSION()(导出 SDK 版本数据符号) ├── aec_mcp_common.h/.cpp 参数解析、handle<->ObjectId、文档锁、结果信封 -├── aec_mcp_entity_tools.cpp 5 个 create_* + 5 个 update_* -├── aec_mcp_query.cpp list_aec_entities / get_aec_entity -└── cad-mcp-aec.json 工具描述(合到运行期 cad-mcp-frame.json 的 tools 数组里) +├── aec_mcp_entity_tools.cpp 5 个 add_aec_* + 5 个 update_aec_* +├── aec_mcp_query.cpp query_aec_entities / get_aec_entity_detail +└── mcp_config.json 运行期配置(28 个既有工具 + 本插件的 12 个,cad_mcp_frame 直接读) ``` 顶层 `cmakelists.txt` 里挂在 `cad_mcp_frame` 同一处: @@ -46,15 +46,18 @@ if (IS_ZWCAD OR CAD_SDK_VERSION GREATER_EQUAL 20) ## 3. 工具清单(12 个) +工具名和参数都按仓库既有工具的写法(`add_line` / `query_entities` / `get_entity_detail`): +**动词_名词、点用 `[x,y]` 或 `[x,y,z]` 数组、颜色用 `color`(ACI,256=ByLayer)**。 + | 工具 | 作用 | 关键参数 | | --- | --- | --- | -| `list_aec_entities` | 列出管道/墙/板/柱/梁(handle、类型、图层、起终点、包围盒) | `type`(all/pipe/wall/slab/column/beam)、`space`、`limit`、`offset` | -| `get_aec_entity` | 按 handle 取详细参数 | `handle` | -| `create_aec_pipe` / `update_aec_pipe` | 建/改管道(`AecDbEwPipe`) | `start`/`end`、`dn_mm`、`od_mm`、`wall_thickness_mm`、`system`、`material`、`color_index`、`layer` | -| `create_aec_wall` / `update_aec_wall` | 建/改墙(`AecDbXWall`) | `start`/`end`、`thickness`、`height`、`bottom_elevation`、`align`(left/center/right) | -| `create_aec_slab` / `update_aec_slab` | 建/改板(`AecDbXSlab`) | `points`([[x,y],...])、`thickness`、`bottom_elevation`、`center` | -| `create_aec_column` / `update_aec_column` | 建/改柱(`AecDbColumn`) | `position`、`height`、`length`、`width`、`rotation_deg` | -| `create_aec_beam` / `update_aec_beam` | 建/改梁(`AecDbBeam`) | `start`/`end`、`width`、`height` | +| `query_aec_entities` | 列出管道/墙/板/柱/梁(handle、类型、图层、起终点、包围盒),分页 | `type`(all/pipe/wall/slab/column/beam)、`space`、`limit`、`offset` | +| `get_aec_entity_detail` | 按 handle 取详细参数 | `handle` | +| `add_aec_pipe` / `update_aec_pipe` | 建/改管道(`AecDbEwPipe`) | `start`/`end`、`dn_mm`、`od_mm`、`wall_thickness_mm`、`system`、`material`、`color`、`layer`、`space` | +| `add_aec_wall` / `update_aec_wall` | 建/改墙(`AecDbXWall`) | `start`/`end`、`thickness`、`height`、`bottom_elevation`、`align`(left/center/right) | +| `add_aec_slab` / `update_aec_slab` | 建/改板(`AecDbXSlab`) | `points`([[x,y],...])、`thickness`、`bottom_elevation`、`center` | +| `add_aec_column` / `update_aec_column` | 建/改柱(`AecDbColumn`) | `position`、`height`、`length`、`width`、`rotation_deg` | +| `add_aec_beam` / `update_aec_beam` | 建/改梁(`AecDbBeam`) | `start`/`end`、`width`、`height` | **单位约定**(写进了每个工具的 description): @@ -62,7 +65,7 @@ if (IS_ZWCAD OR CAD_SDK_VERSION GREATER_EQUAL 20) - 管道的 `dn_mm` / `od_mm` / `wall_thickness_mm` 用 **mm**,内部按 `AS_MUNIT` 转米, 与仓库里所有管道对话框(`BZGDDlg`、`AecPipeLineTool`)一致。 -**返回值**:统一 `{"ok":true, "type":..., "handle":...}`;错误是 MCP 的 error 信道 +**返回值**:统一 `{"ok":true, "type":..., "handle":...}`;错误走 MCP 的 error 信道 (`-32001` 参数非法 / `-32002` 找不到 / `-32004` 类型不对 / `-32603` 内部错误)。 **update_\*** 只改传入的字段,其余保持原值;管道只给一个端点时,另一端沿用实体现有端点。 @@ -78,7 +81,7 @@ MCP 工具是通过 `PostMessage` 在主线程的消息里跑的,**不在命 ### 4.2 只读实体上取专业数据必须 `kForRead` `GetSpecialtyData()` 的**缺省参数是 `AcDb::kForWrite`**。 -`get_aec_entity` 里实体是按 `kForRead` 打开的,第一次实现没注意,**直接把 acad 崩了** +`get_aec_entity_detail` 里实体是按 `kForRead` 打开的,第一次实现没注意,**直接把 acad 崩了** (`%TEMP%\acminidump.dmp`)。现在统一写 `GetSpecialtyData(AcDb::kForRead)`。 `AecDbEwPipeBase.h` 的注释里也写着同样的话("在 draw2/3d 中使用必须 kForRead,否则图不能保存")。 @@ -96,20 +99,21 @@ MCP 工具是通过 `PostMessage` 在主线程的消息里跑的,**不在命 ## 5. 端到端实测(AutoCAD 2020 / R230 Debug) -做法:临时把 `cad-mcp-aec.json` 的 12 个工具合进部署目录的 `cad-mcp-frame.json` -(备份原文件,测完还原),用 `/b` 脚本加载 `cad_mcp_frame.arx`,然后用裸 socket 打 JSON-RPC。 +做法:把仓库里的 `mcp_config.json`(既有工具 + 本插件 12 个)拷到部署目录, +用 `/b` 脚本加载 `cad_mcp_frame.arx`,然后用裸 socket 打 JSON-RPC。 +`%TEMP%\mcp_plugin_load.log` 里 12 个 `add_aec_*` / `update_aec_*` 工厂都是 `[try ] ... create=... destroy=...` 成功。 | 用例 | 结果 | | --- | --- | -| `tools/list` | ✅ 返回 **30** 个工具(原 18 + 新增 12),`create_aec_pipe` 等描述可见 | -| `list_aec_entities` | ✅ pcf_1.dwg 里列出 20 根管道,handle/图层/起终点/包围盒正确 | -| `create_aec_pipe` | ✅ 返回 handle `3A0`,起点(0,0,0)→终点(2000,0,0) | -| `get_aec_entity` | ✅ 读回 `dn_mm=100 / od_mm=114 / wall_thickness_mm=4` | +| `tools/list` | ✅ 返回 **30** 个工具(原 18 + 新增 12),`add_aec_pipe` 等描述可见 | +| `query_aec_entities` | ✅ pcf_1.dwg 里列出 20 根管道,handle/图层/起终点/包围盒正确 | +| `add_aec_pipe`(点位用 `[x,y,z]`、颜色用 `color`) | ✅ 返回 handle `3A0`,起点(0,0,0)→终点(2000,0,0),`color=1` 读回一致 | +| `get_aec_entity_detail` | ✅ 读回 `dn_mm=100 / od_mm=114 / wall_thickness_mm=4` | | `update_aec_pipe` | ✅ 改 `dn_mm=150` + 只给 `end`:终点变 3000,**壁厚仍是 4**(4.3 的修复生效) | -| `create_aec_wall` | ✅ `3A1`;get 读回 thickness=200 / height=3000 / align=center | -| `create_aec_column` | ✅ `3A2`;get 读回 height=3000 / length=400 / width=400 | -| `create_aec_beam` | ✅ `3A3`;get 读回 width=300 / height=500 | -| `create_aec_slab` | ✅ `3A0`;get 读回 thickness=100 / bottom_elevation=-500 / material=钢筋混凝土 | +| `add_aec_wall` | ✅ `3A1`;get 读回 thickness=200 / height=3000 / align=center | +| `add_aec_column` | ✅ `3A2`;get 读回 height=3000 / length=400 / width=400 | +| `add_aec_beam` | ✅ `3A3`;get 读回 width=300 / height=500 | +| `add_aec_slab` | ✅ `3A0`;get 读回 thickness=100 / bottom_elevation=-500 / material=钢筋混凝土 | | `update_aec_slab`(只改厚度/标高) | ✅ 150 / -300 都生效 | | 类型不匹配 | ✅ `update_aec_wall` 传板的 handle → `-32004`「实体是 XSlab,不是 AecDbXWall」 | | 不存在的 handle | ✅ `-32002` | @@ -117,35 +121,50 @@ MCP 工具是通过 `PostMessage` 在主线程的消息里跑的,**不在命 导出表(`dumpbin -exports cad_mcp_aec.dll`):12 个 `Create*/Update*` 工厂 + `Destroy_*` + `g_mcpPluginApiVersion` 全部在。 -## 6. 部署与启用 +## 6. 配置与启用 -1. 编译:`cmake --build --preset R230 --config Debug --target CadMcpAec` - → `cad_mcp_aec.dll` 随 Debug 构建自动部署到 `c:\ShangHuaSoft\Envi3D-2026-M\R230\` - (与 `cad_mcp_frame.arx` 同目录,`plugin_directory` 是 `"."`)。 -2. 把 `cad-mcp-aec.json` 里的 `tools` 追加到部署目录的 `cad-mcp-frame.json`: +工具描述统一放 `SourceCode/Code2026/cad_mcp_aec/mcp_config.json`(与仓库既有工具同一个文件), +每个 `tool` 对象就是标准 MCP 协议字段 + `backend`: - ```python - python - <<'EOF' - import json - cfg = json.load(open('cad-mcp-frame.json', encoding='utf-8')) - add = json.load(open('cad-mcp-aec.json', encoding='utf-8')) - names = {t['name'] for t in cfg['tools']} - cfg['tools'] += [t for t in add['tools'] if t['name'] not in names] - json.dump(cfg, open('cad-mcp-frame.json','w',encoding='utf-8'), ensure_ascii=False, indent='\t') - EOF - ``` +```json +{ + "name": "add_aec_pipe", + "description": "...", + "inputSchema": { "type": "object", "properties": { ... }, "required": [ ... ] }, + "backend": { "type": "c++_dll", "dll_name": "cad_mcp_aec.dll", + "factory_function": "CreateAecPipeTool" } +} +``` - `cad-mcp-aec.json` 里**故意不写 `inputSchema`**:插件里 - `mcp_Tool::private_input_schema()` 已经带了完整 schema,配置留空时框架会自动回落 - (`inc/mcp_plugin_api.h` 的 `ToLLM()`)。这样描述与代码不会漂移。 -3. 重启 CAD(或 `/reload`),`tools/list` 里应能看到 12 个 `*_aec_*` 工具。 +`factory_function` 对应 DLL 里 `EXPORT_MCP_TOOL(ToolClass, FactoryName)` 导出的 +`FactoryName` / `Destroy_FactoryName`;框架按 `plugin_directory`("."=arx 同目录) +拼出 `dll_name` 再 `LoadLibrary`。 + +启用两步: + +1. 编译(Debug 会自动部署 DLL): + `cmake --build --preset R230 --config Debug --target CadMcpAec` + → `cad_mcp_aec.dll` 落到 `c:\ShangHuaSoft\Envi3D-2026-M\R230\` +2. 把 `mcp_config.json` 拷到同一个目录(`cad_mcp_frame` 优先读它,没有才回落 + `cad-mcp-frame.json`),重启 CAD。 + + 注意:`set_debug_post_build()` 只拷构建产物,**配置文件不自动部署**,需要手工拷一次。 + 仓库里这份 `mcp_config.json` 是"既有 28 个工具 + 本插件 12 个"的合集, + 拷过去就等于全量替换。 ## 7. 已知限制 - 只支持这 5 类实体;其它 `AecDb*` 返回 `-32004`。要扩就照 `aec_mcp_entity_tools.cpp` - 里"一个 apply 函数 + create/update 两个壳"的写法加。 + 里"一个 apply 函数 + add/update 两个壳"的写法加。 - 新建实体后 `bbox` 会是 `±1e20`:自定义实体的几何要等一次 REGEN 才建出来, `getGeomExtents` 在未绘制时返回无效值。不是错误。 -- 没有事务/撤销包装:调用即改库。批量改图建议先 `list` 拿 handle,再逐条 `update`。 -- 工具描述的中文来自代码里的 `private_description()`;如果最终以运行期 JSON 为准, - 记得两边同步(或者干脆像现在这样只在 JSON 里写 name + description + backend)。 +- 没有事务/撤销包装:调用即改库。批量改图建议先 `query_aec_entities` 拿 handle,再逐条 `update`。 +- **`mcp_config.json` 里有 10 个工具在当前 `cad_mcp_plugins.dll` 里没有对应工厂函数** + (`CreateSetDrawingAreaTool`、`CreateFindCollisionsTool`、`CreateDrawRevisionCloudTool`、 + `CreateAddTextTool`、`CreateAddMTextTool`、`CreateAddPolylineTool`、`CreateAddLineTool`、 + `CreateAddLeaderAnnotationTool`、`CreateDeleteEntitiesTool`、`CreateSetEntityPropertiesTool`)。 + 这份配置来自更新一版的插件工程,而仓库里的 `cad_mcp_plugins` 只有 16 个工具, + 于是这 10 个在 `tools/list` 里会被静默丢掉(实测 `tools/list` = 30 = 18 + 12)。 + 要补齐得先把那 10 个工具的实现补进 `cad_mcp_plugins`。 +- 插件侧 `private_description()` / `private_input_schema()` 与 `mcp_config.json` 保持同文案; + **改描述时两边一起改**(配置里写了 `description` / `inputSchema` 时以配置为准)。