flycode-mcp 1.14.2 → 1.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -1
- package/dist/cli.js +6893 -0
- package/dist/skills/flycode-mcp-skill/SKILL.md +321 -0
- package/dist/skills/flycode-mcp-skill/scripts/save_full_via_mcp.mjs +199 -0
- package/dist/skills/flycode-mcp-skill/tools/core.md +56 -0
- package/dist/skills/flycode-mcp-skill/tools/entity.md +255 -0
- package/dist/skills/flycode-mcp-skill/tools/form.md +248 -0
- package/dist/skills/flycode-mcp-skill/tools/function.md +114 -0
- package/dist/skills/flycode-mcp-skill/tools/gateway.md +65 -0
- package/dist/skills/flycode-mcp-skill/tools/h5.md +70 -0
- package/dist/skills/flycode-mcp-skill/tools/logic.md +296 -0
- package/package.json +4 -3
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
\---
|
|
2
|
+
name: flycode-mcp-skill
|
|
3
|
+
agent_created: true
|
|
4
|
+
description: >-
|
|
5
|
+
面向 flycode-mcp(玄瞳 APaaS / IDE / FlyCode 平台 MCP 服务)的「工具选择路由」Skill:当任务涉及 APaaS
|
|
6
|
+
平台的实体(数据表)、业务逻辑(model logic / flycode 脚本 / 业务领域)、表单/页面(UI form)、H5 页面、flycode 函数、
|
|
7
|
+
数据库函数(PostgreSQL 自定义函数)、业务库数据探查,或需要新建/修改上述内容时,帮助 AI 从 27 个工具中
|
|
8
|
+
选对工具、走对渐进式披露(域激活)流程与写工具分工(str_replace_flycode / patch_model_logic / patch_form
|
|
9
|
+
/ save_model_logic / create_entity / patch_entity),并规避「回传整份大 JSON」「校验假阴性」「导出列误用
|
|
10
|
+
set_meta」「实体未发布态误以为已落库」等高频坑。
|
|
11
|
+
触发词:查/看/搜/找 实体、业务逻辑、表单、脚本、H5、函数;改/加/修/删 逻辑脚本、表单控件、input/output
|
|
12
|
+
参数、UI flycode;新建逻辑;执行 SQL / 查表数据;生成唯一编码(code);工具 not found(需激活域)。
|
|
13
|
+
红线:严禁凭旧版记忆自写 Python/Node/curl/psql 脚本直接调平台接口或模拟旧工具,一切操作只走官方 MCP 工具;
|
|
14
|
+
唯一例外:必须用 save_model_logic/save_form 全量保存且完整 JSON 过大无法内联、局部工具无法满足时,可用「八、官方 MCP 客户端脚本」(真·MCP 客户端,非直连 REST)。
|
|
15
|
+
\---
|
|
16
|
+
|
|
17
|
+
# flycode-mcp 工具选择路由 🧭
|
|
18
|
+
|
|
19
|
+
> **本 Skill 只回答「用哪个工具、走什么流程、避开哪些坑」,不替代 flycode 语言本身的编写/审计知识
|
|
20
|
+
> (那部分见 `flycode` Skill)。** 凡是用户要动 APaaS 平台上的实体 / 业务逻辑 / 表单 / H5 / 函数 / 库表数据,
|
|
21
|
+
> 先在本 Skill 里定位工具,再调用对应 MCP 工具。
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🚫 零、红线(最高优先级,务必先读)
|
|
26
|
+
|
|
27
|
+
**禁止自写脚本调用平台。** 部分用户曾使用工具较少的旧版 flycode-mcp,其 AI 可能「凭记忆」学会用
|
|
28
|
+
Python / Node(或 curl / psql / 其他语言)直接打 APaaS 平台 HTTP 接口、或手写脚本去「模拟」某个旧工具。
|
|
29
|
+
**最新版已提供完整的官方 MCP 工具,任何此类自写脚本调用一律禁止,包括但不限于:**
|
|
30
|
+
|
|
31
|
+
1. ❌ 用 Python / Node / curl 直接请求平台 REST 接口(如 `/bizserv/bizmodel/*`、`/biserv/bizpage/*`、`/api/biz/sqlrun`、`/api/multiplat/auth/*` 等)来读写实体 / 逻辑 / 表单 / 执行 SQL。
|
|
32
|
+
2. ❌ 手写脚本「封装」或「替代」MCP 工具(例如自己实现 `get_entity` / `save_model_logic` / `run_sql` 的 HTTP 客户端)。
|
|
33
|
+
3. ❌ 另开数据库连接(psql / pg 驱动 / ORM)绕过 `run_sql` 直接查业务库;`run_sql` 是**唯一** sanctioned 的查库通道。
|
|
34
|
+
4. ❌ 凭记忆调用**已不存在或已改名**的旧工具 / 旧参数(旧版工具集与最新版不一致),或臆造工具名。
|
|
35
|
+
5. ❌ 即使「记得以前这么干过能跑」,也以**当前 `mcp_catalog` 列出的工具为准**,不以记忆覆盖本 Skill 的路由结论。
|
|
36
|
+
|
|
37
|
+
> **✅ 唯一被许可的脚本例外(sanctioned)**:当且仅当**同时满足**以下三点时,允许写一个脚本:
|
|
38
|
+
> ① 确实必须使用完整保存工具 `save_model_logic` / `save_form`(如新建逻辑/表单,或已有逻辑 JSON 巨大);
|
|
39
|
+
> ② 完整 `LogicResult` / `FormResult` JSON **过大,无法经「AI → MCP 工具调用」内联传递**(超长截断 / 上下文预算耗尽);
|
|
40
|
+
> ③ 局部工具 `str_replace_flycode` / `patch_model_logic` / `patch_form` **确实无法满足**需求。
|
|
41
|
+
> 该脚本**必须是真正的 MCP 客户端**(用 `@modelcontextprotocol/sdk` 的 `StdioClientTransport` + `Client.callTool` 走 MCP 协议),
|
|
42
|
+
> 调用**官方** `save_model_logic` / `save_form` 工具——这**不绕过 MCP**,也**严禁**在脚本里直接打平台 REST 接口(那仍属上面第 1/2 条,禁止)。
|
|
43
|
+
> 官方标准脚本与用法见「**八、超大 JSON 全量保存的官方脚本**」。**除此之外任何自写脚本调用平台一律禁止。**
|
|
44
|
+
|
|
45
|
+
**正确做法:** 所有对 APaaS 平台的数据读写,**只通过本 Skill 路由到的 flycode-mcp 官方 MCP 工具**完成。
|
|
46
|
+
若所需能力在 `mcp_catalog` 中找不到,先 `mcp_activate` 解锁对应域;仍无对应工具再如实告知用户「当前 MCP 未提供该能力」,
|
|
47
|
+
**不要退而求其次写脚本绕过**(唯一例外见上方「✅ 唯一被许可的脚本例外」,且仅限官方脚本)。
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 一、工具全景(27 个 = 25 域工具 + 2 网关)
|
|
52
|
+
|
|
53
|
+
按能力域分组。**连接后默认只有 `core` 域 + 2 个网关工具可见**,其余 5 个域(`entity`/`form`/`logic`/`function`/`h5`)默认隐藏,需 `mcp_activate` 激活(见第二节)。
|
|
54
|
+
|
|
55
|
+
| 域 | 工具 | 读写 | 一句话用途 |
|
|
56
|
+
| ----------------- | ----------------------------- | -------- | --------------------------------------------------------------- |
|
|
57
|
+
| **core**(始终可见) | `run_sql` | 只读 | 在业务库执行**单条 SELECT**,探查表数据 / 核对字段值 / 验证同步结果 |
|
|
58
|
+
| **core**(始终可见) | `generate_unique_code` | 只生成本地 ID | 雪花算法生成唯一 `code`(新建实体/逻辑/表单时填 JSON 的 `code` 字段) |
|
|
59
|
+
| **gateway**(始终可见) | `mcp_catalog` | 只读 | 查看各域工具清单与启用状态(不含入参 schema) |
|
|
60
|
+
| **gateway**(始终可见) | `mcp_activate` | 切换可见性 | 启用/收起某域工具(`entity`/`form`/`logic`/`function`/`h5`) |
|
|
61
|
+
| **entity** | `page_entity_list` | 只读 | 分页搜实体(按中文名/表名/objectcode 模糊匹配) |
|
|
62
|
+
| **entity** | `get_entity` | 只读 | 查实体详情(含 `properties` 字段数组、类型、关联实体) |
|
|
63
|
+
| **entity** | `page_entity_directories` | 只读 | 查**实体所属目录**:返回 `directorytypecode`(即实体 `pageinfo.directorytypecode` 的取值)。接口无分页、全量数百条,故**必须带 `keys` 检索**(目录名模糊,也命中 `centername`),或 `parentdirectorytypecode` 下钻(`root`=只看顶级);默认回 30 条、硬上限 200,回 `truncated`/`hint` |
|
|
64
|
+
| **entity** | `create_entity` | 写 | **新增**实体(数据表):组装完整 JSON 调 `/bizserv/biz/saveObject`,实体级 `publishstatus`=2(未发布,需去平台点发布);数据字典(`objecttypecode=3`)`publishstatus`=1 且 `properties=[]`;双闸 `confirm`+`dryRun` |
|
|
65
|
+
| **entity** | `patch_entity` | 写 | 已有实体**新增字段**(`op:'add_field'`);`op:'modify'`/`op:'delete'` 未实现、传入即失败提示「修改/删除字段请让用户到 IDE 平台手动操作」;数据字典(`objecttypecode=3`)任何字段 op 均提示「数据字典不允许调整字段」 |
|
|
66
|
+
| **logic** | `page_model_logic_list` | 只读 | 分页搜业务逻辑(按名 `keys` / 代码关键字 `keywords`) |
|
|
67
|
+
| **logic** | `page_model_logic_directories` | 只读 | 分页查**业务模型目录**(ModelDirectory):定位某条逻辑挂在哪个目录;返回 `modelcode`(即 modellogic 的 `modelcode`)、`modelname`、`metamodelcode`、`directorytypecode`(父目录,一级为 `rootNode`),可按 `modelname` 搜 / `directorytypecode` 下钻子目录 |
|
|
68
|
+
| **logic** | `get_model_logic` | 只读 | 查业务逻辑完整详情(flycode 脚本在 `operations[].templateparams.flycode`) |
|
|
69
|
+
| **logic** | `save_model_logic` | 写 | **新增**逻辑 / 全量 JSON 很小的已有逻辑(如微服务 `actioncategory=2`) |
|
|
70
|
+
| **logic** | `patch_model_logic` | 写 | 局部改 **input/output 结构 / 顶层元数据 / 导出列**(`actioncategory=5`) |
|
|
71
|
+
| **logic** | `str_replace_flycode` | 写 | 局部/整份改 **flycode 脚本本体** |
|
|
72
|
+
| **form** | `page_form_directories` | 只读 | 查**表单所属目录**:返回 `directorytypecode`(即表单 `pageinfo.directorytypecode` 的取值)。接口无分页、全量数百条,故**必须带 `keys` 检索**(目录名模糊,也命中 `centername`),或用 `parentdirectorytypecode` 下钻(`root`=只看顶级);默认回 30 条、硬上限 200,回 `truncated`/`hint` |
|
|
73
|
+
| **form** | `page_form_list` | 只读 | 分页搜表单/页面(按 `code`/`title`/`keywords`/`type`/`status`) |
|
|
74
|
+
| **form** | `get_form` | 只读 | 查表单完整详情(88KB 级,含 `view` 控件树 / `presenter` 事件 / `businessmodel`) |
|
|
75
|
+
| **form** | `get_form_outline` | 只读 | 表单轻量骨架(2–4KB):节点树 + handlers/interfaces 清单 + 逻辑名 |
|
|
76
|
+
| **form** | `get_form_fragment` | 只读 | 表单局部内容(按 `nodeCode`/`handlerCode`/`section` 取几 KB) |
|
|
77
|
+
| **form** | `patch_form` | 写 | 局部改表单(11 类 ops:控件属性/数组项增删/action 内数组绑定/插删移节点/改脚本/增删 businessmodel) |
|
|
78
|
+
| **form** | `save_form` | 写 | 整页重构时才用:回传完整 `FormResult` 全量保存 |
|
|
79
|
+
| **h5** | `page_h5_form` | 只读 | 分页搜 H5 表单(智慧100 引擎) |
|
|
80
|
+
| **h5** | `get_h5_form` | 只读 | 查 H5 表单详情(核心 `confjson` 含 Vue 代码) |
|
|
81
|
+
| **function** | `page_function_list` | 只读 | 分页搜 flycode 函数 |
|
|
82
|
+
| **function** | `get_function` | 只读 | 查 flycode 函数详情(核心 `confjson` 函数代码) |
|
|
83
|
+
| **function** | `page_database_function_list` | 只读 | 分页搜 PostgreSQL 自定义函数(**函数体 `functionscript` 列表随行返回**,无详情工具) |
|
|
84
|
+
|
|
85
|
+
> 注:H5 引擎可能未安装(返回 HTTP 404),属正常;`run_sql` 仅支持 SELECT、一次一条,不支持写操作。
|
|
86
|
+
|
|
87
|
+
### 1.1 分模块工具手册(27 个工具的字段级入参/出参明细,按需查阅)
|
|
88
|
+
|
|
89
|
+
上表只是导航。每个工具的**详细描述、输入参数表、输出结构、错误码、典型坑**按业务域拆分在 `tools/` 目录(与本文件同目录),**用到哪个域再读哪个文件**,不要一次性全部读入浪费上下文:
|
|
90
|
+
|
|
91
|
+
| 文件 | 覆盖域 | 工具数 | 覆盖工具 |
|
|
92
|
+
| --- | --- | --- | --- |
|
|
93
|
+
| `tools/gateway.md` | gateway(始终可见) | 2 | `mcp_catalog` / `mcp_activate` |
|
|
94
|
+
| `tools/core.md` | core(始终可见) | 2 | `run_sql` / `generate_unique_code` |
|
|
95
|
+
| `tools/entity.md` | entity | 5 | `page_entity_list` / `get_entity` / `page_entity_directories` / `create_entity` / `patch_entity` |
|
|
96
|
+
| `tools/logic.md` | logic | 6 | `page_model_logic_directories` / `page_model_logic_list` / `get_model_logic` / `save_model_logic` / `patch_model_logic` / `str_replace_flycode` |
|
|
97
|
+
| `tools/form.md` | form | 7 | `page_form_directories` / `page_form_list` / `get_form` / `get_form_outline` / `get_form_fragment` / `patch_form` / `save_form` |
|
|
98
|
+
| `tools/function.md` | function | 3 | `page_function_list` / `get_function` / `page_database_function_list` |
|
|
99
|
+
| `tools/h5.md` | h5 | 2 | `page_h5_form` / `get_h5_form` |
|
|
100
|
+
|
|
101
|
+
> 两个目录工具(`page_entity_directories` / `page_form_directories`)输出结构相同,entity.md 为主文档、form.md 只引用。共用结构(`commitMessage`、枚举速查)在各域手册文件头部各有一份,读单文件即可自足。
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 二、渐进式披露(最重要前置!)
|
|
106
|
+
|
|
107
|
+
flycode-mcp 默认开启 `PROGRESSIVE_DISCLOSURE=on`:连接后客户端只看到 `core` 域(`run_sql`、`generate_unique_code`)+ 2 个网关(`mcp_catalog`、`mcp_activate`),**其余 5 个域全部隐藏**。
|
|
108
|
+
|
|
109
|
+
**执行逻辑(必走):**
|
|
110
|
+
|
|
111
|
+
1. 要用的工具报 `tool not found` / 客户端工具列表里找不到 → 先调 `mcp_catalog`(不传 `domain`)看各域 `active` 状态。
|
|
112
|
+
2. 调 `mcp_activate('logic')`(或 `entity`/`form`/`function`/`h5`)启用目标域;服务端推 `tools/list_changed`,合规客户端重拉后即见该域工具。
|
|
113
|
+
3. 若该域工具仍报 `tool not found` → 说明当前客户端未实现 `tools/list_changed` 重拉,提示用户把环境变量 `PROGRESSIVE_DISCLOSURE` 设为 `off`(退回全部工具始终启用)。
|
|
114
|
+
4. 用完可 `mcp_activate('logic', true)` 收起回收上下文。
|
|
115
|
+
|
|
116
|
+
> ⚠️ `mcp_catalog` **刻意不返回入参 schema**(单域全量 schema 曾达 504KB,会撑爆上下文);参数细节以 `mcp_activate` 启用后客户端重拉的 `tools/list` 为准。`generate_unique_code` 与 `run_sql` 在 `core` 域,无需激活即可直接用。
|
|
117
|
+
|
|
118
|
+
### 2.1 客户端不支持 `tools/list_changed` 重拉时的兜底(以 Trae 为例)
|
|
119
|
+
|
|
120
|
+
部分 MCP 客户端(如 **Trae**)未实现 `tools/list_changed` 通知后的工具清单重拉:`mcp_activate` 启用某域后,客户端工具列表里仍看不到该域工具,直接调用会持续报 `tool not found`。此时**不要反复重试 `mcp_activate`**(在无力重拉的客户端上它属无效操作),按以下兜底顺序处理:
|
|
121
|
+
|
|
122
|
+
1. **优先用客户端的 exec 沙箱直调 MCP**:在 Trae 中可通过 exec 沙箱直接发起
|
|
123
|
+
`tools.run_mcp({ server_name: "......", ... })` 调用(把 `server_name` 换成当前连接的
|
|
124
|
+
flycode-mcp 服务名,参数按目标工具 schema 传入)。这种方式**不依赖客户端重拉清单**,可绕过
|
|
125
|
+
「看不到工具」的限制,是渐进式披露下的首选兜底。
|
|
126
|
+
2. **仍不行 → 关掉渐进式披露**:提示用户在 MCP 连接配置里加
|
|
127
|
+
`"env": { "PROGRESSIVE_DISCLOSURE": "off" }`,让服务退回「全部工具始终启用」模式,连接后即可见
|
|
128
|
+
全部 27 个工具,无需 `mcp_activate`。
|
|
129
|
+
- ⚠️ **副作用**:所有工具(含大 schema 的 logic / form 域)一次性进入上下文,**有上下文过长 /
|
|
130
|
+
截断风险**;仅当 exec 直调也不可行时才作为最后手段使用。可同时加
|
|
131
|
+
`"FLYCODE_MCP_INPUT_SCHEMA_PROFILE": "compact"` 压缩公告 schema 缓解(工具描述不减、
|
|
132
|
+
服务端校验不变;实测 27 工具公告 155KB→103KB、最大单 schema 14.9KB→1.4KB)。
|
|
133
|
+
|
|
134
|
+
> 注:`PROGRESSIVE_DISCLOSURE` 是服务端环境变量(flycode-mcp README 有载),由 MCP 启动配置传入;
|
|
135
|
+
> 它在 `off` 时 `mcp_activate` 退化为无害空操作。exec 直调与关披露二选一即可,不必叠加。
|
|
136
|
+
> `FLYCODE_MCP_INPUT_SCHEMA_PROFILE=compact`(v1.14.2 新增)与上述任一选项**可叠加**,见 README「第三道防线(公告 schema 压缩)」。
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 三、意图 → 工具 决策矩阵(核心)
|
|
141
|
+
|
|
142
|
+
**只读(看清现状)先激活对应域,再选:**
|
|
143
|
+
|
|
144
|
+
| 用户意图 | 激活域 | 用哪个工具 |
|
|
145
|
+
| ------------------------- | ---------- | -------------------------------------------------------------- |
|
|
146
|
+
| 平台有哪些实体 / 搜名字带「订单」的实体 | `entity` | `page_entity_list`(`objectname` 模糊匹配) |
|
|
147
|
+
| 某实体有哪些字段、什么类型、关联谁 | `entity` | `get_entity`(`objectcode` 必填,数字码) |
|
|
148
|
+
| 查实体挂在哪个目录 / 取目录编码 directorytypecode | `entity` | `page_entity_directories`(**先 `keys` 模糊搜目录名**;无筛选时只回顶级目录。切勿裸调拉全量——接口无分页、实测数百条) |
|
|
149
|
+
| 搜名字/代码关键字带「价格同步」的逻辑 | `logic` | `page_model_logic_list`(`keys`/`keywords`) |
|
|
150
|
+
| 查业务逻辑所属目录 / 找某逻辑挂在哪个目录 | `logic` | `page_model_logic_directories`(`modelname` 模糊搜 / `directorytypecode` 下钻子目录,默认 `rootNode` 查一级;返回 `modelcode` 即查该目录逻辑要用的编码) |
|
|
151
|
+
| 看某条业务逻辑完整内容(含 flycode) | `logic` | `get_model_logic`(`modellogiccode`) |
|
|
152
|
+
| 先看表单长啥样(骨架) | `form` | `get_form_outline`(`code`)→ 2–4KB |
|
|
153
|
+
| 看表单里某段脚本 / 某节点 | `form` | `get_form_fragment`(`code`+`nodeCode`/`handlerCode`/`section`) |
|
|
154
|
+
| 看表单完整结构(含控件树/事件) | `form` | `get_form`(`code`,88KB 级,非必要不用) |
|
|
155
|
+
| 搜表单/页面 | `form` | `page_form_list`(`keys`/`type`/`status`) |
|
|
156
|
+
| 查表单挂在哪个目录 / 取目录编码 directorytypecode | `form` | `page_form_directories`(**先 `keys` 模糊搜目录名**;无筛选时只回顶级目录。切勿裸调拉全量——接口无分页、实测 352 条/74KB) |
|
|
157
|
+
| 看 H5 页面代码 | `h5` | `get_h5_form`(`pagecode`,核心 `confjson`) |
|
|
158
|
+
| 列 flycode 函数 | `function` | `page_function_list` |
|
|
159
|
+
| 看某 flycode 函数代码 | `function` | `get_function`(`modelcode`) |
|
|
160
|
+
| 列 PostgreSQL 自定义函数(函数体随行) | `function` | `page_database_function_list` |
|
|
161
|
+
| 查某张业务表的数据 / 核对同步结果 | `core` | `run_sql`(`sql` 单条 SELECT) |
|
|
162
|
+
|
|
163
|
+
**写(改 / 建)按「改什么」分流(详见第四节):**
|
|
164
|
+
|
|
165
|
+
| 用户意图 | 用哪个工具 |
|
|
166
|
+
| ----------------------------- | ---------------------------------------------------------------- |
|
|
167
|
+
| 改 1~N 处 flycode 脚本片段 | `str_replace_flycode` |
|
|
168
|
+
| 整份重写 flycode 脚本 | `str_replace_flycode`(`rewrite:true`,只传新全文) |
|
|
169
|
+
| 加/删 input 或 output 参数、加/改字段属性 | `patch_model_logic` |
|
|
170
|
+
| 改逻辑标题/描述/`actiontype` 等顶层元数据 | `patch_model_logic`(`set_meta`) |
|
|
171
|
+
| 改导出类(`actioncategory=5`)的导出列 | `patch_model_logic`(5 类导出 op,**勿用 `set_meta` 碰 `bindjson`**) |
|
|
172
|
+
| 给表单加/删/移控件、改控件属性、改 UI 脚本、改 action 内数组绑定 | `patch_form`(11 类 ops) |
|
|
173
|
+
| 整页重构表单布局 | `save_form`(回传完整 `FormResult`) |
|
|
174
|
+
| **新建**一条业务逻辑(首次创建) | `save_model_logic`(配 `generate_unique_code` 生成 `modellogiccode`) |
|
|
175
|
+
| 已有逻辑全量 JSON 很小(如微服务) | `save_model_logic` 全量保存 |
|
|
176
|
+
| **新建**一条实体(数据表,首次创建) | `create_entity`(配 `generate_unique_code` 生成 `objectcode`,无 prefix;`objecttypecode` 必填、`tablename`=`objectmark` 同值) |
|
|
177
|
+
| 给已有实体**加字段** | `patch_entity`(`op:'add_field'`,`columnname` 必填;视图/超级表单新属性 `seq:null`) |
|
|
178
|
+
| 改/删已有实体字段 | ❌ `patch_entity` 未支持 → 提示用户到 IDE 平台手动操作(`modify`/`delete` 传入即失败;数据字典任何字段 op 也失败) |
|
|
179
|
+
|
|
180
|
+
**通用:** 任何「新建实体/逻辑/表单需要唯一 `code`」→ 先 `generate_unique_code`,返回的 `code` 是**字符串**(避免大整数精度丢失),新建逻辑务必用它、不要自己拼 epoch。
|
|
181
|
+
⚠️ **新建逻辑 `modellogiccode`/`modellogicstepcode` 必须调 `generate_unique_code` 且【不要加 prefix】**:无 prefix 时返回纯 18 位数字 ID(如 `893714100482347008`),后端 `saveModelLogic` 要求 `modellogiccode` 为**纯数字**,带字母前缀(如 `AIT893713469948432384`)会被后端按数字解析失败、判 `modellogiccode不能为空` 而拒绝保存。实体/表单 code 可视情况带 prefix。
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 四、写工具三选一(最易选错,重点)
|
|
186
|
+
|
|
187
|
+
### 4.1 业务逻辑层:str_replace_flycode vs patch_model_logic vs save_model_logic
|
|
188
|
+
|
|
189
|
+
| 改的内容 | 选 |
|
|
190
|
+
| --------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
191
|
+
| flycode 脚本本体(任意片段局部改) | **`str_replace_flycode`**(`oldString`/`newString`,partial 默认 `normalized` 匹配) |
|
|
192
|
+
| 整个 operation 的脚本大范围重构 | **`str_replace_flycode`**(`ops:[{rewrite:true, newString:"整份全文"}]`,**不回传旧全文**省上下文) |
|
|
193
|
+
| input / output 结构(增删参数、增删/改字段属性) | **`patch_model_logic`**(6 类结构 op) |
|
|
194
|
+
| 顶层元数据(`modellogicname`/`description`/`actiontype`/`execmode` 等) | **`patch_model_logic`**(`set_meta`,**不能含 input/output/operations**) |
|
|
195
|
+
| 导出列(`actioncategory=5`) | **`patch_model_logic`**(5 类导出 op) |
|
|
196
|
+
| 首次新建一条逻辑 | **`save_model_logic`** |
|
|
197
|
+
| 已有逻辑且全量 JSON 很小(微服务 `actioncategory=2` 等) | **`save_model_logic`** 全量 |
|
|
198
|
+
| ❌ 已有普通 flycode 逻辑(JSON 大)回传整份 | **不要用 `save_model_logic`**(极易超长截断、误改不相关字段)→ 改用上面两个局部工具 |
|
|
199
|
+
|
|
200
|
+
**铁律:已有大 JSON 的逻辑,永远优先 `str_replace_flycode`(改脚本)/ `patch_model_logic`(改结构),绝不整份回传 `save_model_logic`。**
|
|
201
|
+
|
|
202
|
+
### 4.2 表单层:patch_form vs save_form
|
|
203
|
+
|
|
204
|
+
| 改的内容 | 选 |
|
|
205
|
+
| --------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
206
|
+
| 局部:改控件属性、数组内单元素(列 `options`/样式)、插/删/移节点、改某 action 脚本、增删 businessmodel、**把界面输出绑定到控件(改 `datarequest.setter`)** | **`patch_form`**(11 类 ops;view 树数组用 `patch_node_array_item`(merge/append/insert/remove),handler action 内数组用 `patch_action_array_item`,避免 `set_attrs` 整段覆盖大数组) |
|
|
207
|
+
| 整页重构(重排布局、模板迁移) | **`save_form`**(回传完整 `FormResult`) |
|
|
208
|
+
|
|
209
|
+
> 数组元素级改动一律用 `patch_node_array_item`(view 树节点数组,如单列 `options`/`cols`,支持 merge/append/insert/remove)或 `patch_action_array_item`(presenter.handlers/initial 的 action 内数组,如 `datarequest.setter` 绑定输出到控件、`param.properties`,支持四种模式)。**`set_attrs` 默认禁止整段覆盖数组**(`patch` 含数组属性会直接阻断并要求显式 `overwriteArray:true`),因其大 payload 曾触发调用方 stringify bug 致 36 列丢失;且 `set_attrs` 只覆盖 view 树节点/controls,够不到 action 结构。
|
|
210
|
+
|
|
211
|
+
> **常见陷阱:界面输出绑定的两种真实落点必须先用 `get_form_fragment` 核对**。① 字段级绑定在 `datarequest.setter` **顶层**(如部分表单的 `pack5_barcode`/`status`,`setter` 直接是 `[{name, ctrl:{code}}]`);② 模型级属性映射在 `datarequest.setter[*].properties` **嵌套数组**(如 `kx_kq_product` 的 67 条 `properties`)。patch 前应 `get_form_fragment({handlerCode})` 看清到底是哪一层:**若落点在嵌套数组,用 `patch_action_array_item` + `itemKey`/`itemKeyValue` 定位外层元素(如 `name="kx_kq_product"`)+ `elementArrayProp:"properties"` 钻进去**,单个 op 即可写一条新绑定,无需内联整段 setter。误把嵌套数组当成顶层 `append` 会被 `ARRAY_NOT_FOUND` 阻断(默认 fail-closed,防误造平行 orphan `datarequest.setter`),不会污染表单。
|
|
212
|
+
|
|
213
|
+
### 4.3 实体层:create_entity vs patch_entity
|
|
214
|
+
|
|
215
|
+
| 改的内容 | 选 |
|
|
216
|
+
| ------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
217
|
+
| 首次新建一张实体表(数据表) | **`create_entity`**(组装完整 JSON 调 `saveObject`;`objectcode` 用 `generate_unique_code` 无 prefix;`objecttypecode` 必填:`1` 基本 / `2` 树型 / `3` 数据字典 / `4` 关联;`tablename` 必填且 `objectmark` 复制同值;`datapattern` 固定 `"1"` 非必填) |
|
|
218
|
+
| 给已有实体加一个字段 | **`patch_entity`**(`op:'add_field'`,`columnname` 必填为数据库字段名;字段级 `publishstatus` 由 MCP 补 `"2"`;视图/超级表单新属性 `seq:null`) |
|
|
219
|
+
| 数据字典(`objecttypecode=3`) | **只能整张新建**(`create_entity`,`properties=[]`、`publishstatus=1`);任何字段 op(`add_field`/`modify`/`delete`)均直接失败,提示「数据字典不允许调整字段」 |
|
|
220
|
+
| 改/删已有实体的字段 | ❌ 未实现 → 失败提示「修改/删除字段请让用户到 IDE 平台手动操作」,不要臆造 op 或绕过 |
|
|
221
|
+
|
|
222
|
+
> **双闸(写实体前必读)**:`create_entity` / `patch_entity` 默认 `confirm:false` 且 `dryRun:true`——即默认**不真正落库、只预览**。若要真正保存,调用方必须**显式**传 `confirm:true` 且 `dryRun:false`;任一为 false 都 fail-closed 不写库。务必先 `dryRun:true` 看 diff、并向用户确认方案后再 `confirm:true`。
|
|
223
|
+
> **未发布态约定**:实体保存后默认 `publishstatus=2`(仅更新元数据、未发布到数据库),数据字典为 `1`;两者都需用户**手动去平台点「发布」**才能真正建表/生效,AI 不可替用户发布。保存后建议用 `get_entity` 回读核对。
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 五、标准执行逻辑(工作流)
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
1. 识别意图 → 在第三节决策矩阵定位工具与所需域。
|
|
231
|
+
2. 若目标域非 core → 先 mcp_catalog 看状态,再 mcp_activate('xxx') 解锁。
|
|
232
|
+
3. 只读看清:先 get_entity / get_form_outline / get_form_fragment / get_model_logic 看现状;
|
|
233
|
+
大 JSON 表单优先 outline→fragment,别一上来 get_form。
|
|
234
|
+
4. 写之前:复杂任务先向用户输出修改方案(涉及哪个工具、改哪段、预期),确认后再动手。
|
|
235
|
+
5. 小步改:用 str_replace_flycode / patch_model_logic / patch_form 做局部修改;
|
|
236
|
+
改前想清楚 oldString 是否唯一命中;先 dryRun:true 预览 diff。
|
|
237
|
+
6. 保存:所有写工具自带事务性(整批 ops 预演失败则零写入),提交配 commitMessage
|
|
238
|
+
(committype∈saverecord|feature|fix|stylefix|refactor|testfix,与 committypetext 配对;
|
|
239
|
+
saverecord 的 msgbody 由服务端填,其余必填)。
|
|
240
|
+
7. 校验:写工具默认 verify:true 回读;保存后建议再 get_* 核对最新版本。
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 六、关键避坑(高频雷区)
|
|
246
|
+
|
|
247
|
+
- **🚫 禁止自写脚本调用平台(最高优先级)**:见「零、红线」。旧版用户记忆里可能残留「Python/Node 直连接口」「手写脚本模拟工具」的做法,**一律禁止**;所有读写只走官方 MCP 工具,找不到能力就如实告知用户,不绕过。
|
|
248
|
+
- **不回传整份大 JSON**:已有普通逻辑/表单只用局部工具(`str_replace_flycode`/`patch_model_logic`/`patch_form`),`save_model_logic`/`save_form` 全量仅用于新建或极小 JSON;**当且仅当** JSON 过大无法内联且局部工具无法满足时,才可用「八、超大 JSON 全量保存的官方脚本」(真正的 MCP 客户端,非直接打 REST)。
|
|
249
|
+
- **校验口径(2026-09-22 修正)**:`verify:false` / `VERIFY_FAILED` **视为真实未生效信号**(旧版「换行假阴性」已由 MCP 修复)。`applied:true`+`verified:false` 且报「新内容未出现」时,用 `get_form_fragment`/`get_model_logic` 实际回读确认,不要凭错误码重复提交;确需字节严格校验给 op 设 `matchMode:'exact'`。
|
|
250
|
+
- **导出列专用 ops**:`patch_model_logic` 改导出列(`actioncategory=5`)用 5 类导出 op;**切勿用 `set_meta` 碰 `bindjson`**(会整体覆盖顶层字段、丢失导出列定义),改用 `set_bind_meta`。
|
|
251
|
+
- **空串不是定位键**:自定义字段 `propertycode`/`objectcode` 恒为 `""`,传 `""` 安全且推荐(不会误判重复);但 `remove_property`/`replace_property` 只传空串 `propertyCode` 而无 `propertyName` 会报 `INVALID_PARAMS`。
|
|
252
|
+
- **表单 `initial` vs `handlers`**:页面初始类事件(如「页面初始-参数接收」)在 `presenter.initial[]`,**不在 `handlers[]`**;`patch_form` 的 `replace_script` 会在两块容器都检索,`handlerCode` 同名跨容器须加 `handlerScope` 消歧;`get_form_outline` 会一并列出并标 `scope='initial'`。
|
|
253
|
+
- **新建逻辑用官方 ID(且勿带 prefix)**:`modellogiccode`/`modellogicstepcode` 必须调 `generate_unique_code` 获取,**调用时不要传 `prefix`**(返回纯 18 位纯数字 ID),不要自写脚本按 epoch 算(官方现 18 位、旧「19 位」约定已过期,硬编码会落到平台雪花方案外)。带字母前缀的 `code` 会被后端 `saveModelLogic` 判 `modellogiccode不能为空` 拒绝。新建逻辑顶层对象**不要带** `metamodelcode`/`confsource`。
|
|
254
|
+
- **`run_sql` 只读**:仅 SELECT、一次一条;不支持 INSERT/UPDATE/DELETE。
|
|
255
|
+
- **数据字典文本**:实体字典字段存 `dickey`(bigint,全局唯一),翻译一律 `LEFT JOIN pl_dictionary d ON d.dickey=k.<col>`,不必拼 `dictionarycode`;字典编码 >2^53 用字符串常量。
|
|
256
|
+
- **事务性兜底**:五个写工具(`str_replace_flycode`/`patch_model_logic`/`patch_form`/`create_entity`/`patch_entity`)均事务性——整批 ops 预演失败则整批中止、零写入,无「写一半」中间态;不确定时先 `dryRun:true`。
|
|
257
|
+
- **实体写工具双闸**:`create_entity` / `patch_entity` 默认 `confirm:false` 且 `dryRun:true`(默认只预览不落库);真正保存须显式 `confirm:true`+`dryRun:false`,fail-closed。改前先 `dryRun:true` 预览 diff 并让用户确认。
|
|
258
|
+
- **实体未发布态**:保存后 `publishstatus=2`(数据字典=1),仅更新元数据、**未发布到数据库**,需用户手动去平台点「发布」才真正建表生效;AI 不可替用户发布,保存后建议 `get_entity` 回读核对。
|
|
259
|
+
- **实体级 vs 字段级 `publishstatus` 类型不同**:实体级是 **number** `2`(数据字典 `1`);字段(properties 内)级是 **string** `"2"`,由 MCP 组装时补,调用方不要混用类型。
|
|
260
|
+
- **数据字典特殊约束**:`objecttypecode=3` 时只能整张 `create_entity` 新建(`properties=[]`、`publishstatus=1`),任何字段 op(`add_field`/`modify`/`delete`)均失败并提示「数据字典不允许调整字段」;其余类型 `create_entity` 至少带主键 + 名称 + 备注等必要字段。
|
|
261
|
+
- **新建实体必填口径**:`objecttypecode` 必填(1 基本/2 树型/3 数据字典/4 关联);`tablename` 必填且 `objectmark` 复制同值;`add_field` 必填 `columnname`(数据库字段名);`datapattern` 固定 `"1"` 非必填;`objectcode` 用 `generate_unique_code` **无 prefix**(纯 18 位数字,与逻辑 code 同口径)。
|
|
262
|
+
- **客户端未重拉清单**:`mcp_activate` 后仍 `tool not found` → 提示设 `PROGRESSIVE_DISCLOSURE=off`。
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
## 七、与其他 Skill 的边界
|
|
266
|
+
|
|
267
|
+
- 编写/审计 FlyCode 脚本语言本身(FlyQL、DB/SQL API、UIFlyCode、安全审计)→ 见 **`flycode`** Skill。
|
|
268
|
+
- 本 Skill 只负责「在哪个 MCP 工具上发起操作、走什么流程」——是 flycode-mcp 的**路由层**,不展开脚本语法。
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## 八、超大 JSON 全量保存的官方脚本(真·MCP 客户端,零依赖零凭据)
|
|
273
|
+
|
|
274
|
+
> 本章是「零、红线」中**唯一被许可的脚本例外**的具体落地:当必须用 `save_model_logic` / `save_form` 全量保存、且完整 JSON 大到无法经「AI → MCP 工具调用」内联传递、局部工具又无法满足时,用本脚本。它**是一个真正的 MCP 客户端**,不绕过 MCP,也绝不直接打平台 REST 接口。
|
|
275
|
+
|
|
276
|
+
### 8.1 脚本文件与运行方式
|
|
277
|
+
|
|
278
|
+
脚本路径:`scripts/save_full_via_mcp.mjs`(与本 SKILL.md 同目录)。
|
|
279
|
+
零第三方依赖:只用 Node.js 内置 `node:child_process` / `node:fs`,不依赖 `@modelcontextprotocol/sdk`,避免「用户没装依赖就跑不起来」。
|
|
280
|
+
零硬编码凭据:连接串**只**从环境变量 `FLYCODE_MCP_ARGS` 读取,脚本内不出现任何真实账号/密码/租户码,不打印、不落盘。
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
# 1) 设置连接串(用你自己的,切勿提交进脚本/仓库)
|
|
284
|
+
export FLYCODE_MCP_ARGS='username=...&password=...&tenantCode=...&envName=...'
|
|
285
|
+
|
|
286
|
+
# 2) 运行(logic=保存业务逻辑,form=保存表单;JSON 从文件读,避免内联截断)
|
|
287
|
+
node scripts/save_full_via_mcp.mjs <logic|form> <完整JSON文件路径> [提交说明]
|
|
288
|
+
|
|
289
|
+
# 可选环境变量(均无需改脚本):
|
|
290
|
+
# FLYCODE_MCP_BIN 启动命令,默认 npx
|
|
291
|
+
# FLYCODE_MCP_PKG 包名,默认 flycode-mcp
|
|
292
|
+
# FLYCODE_MCP_ARGS 连接串(必填,无默认值、无真实凭据)
|
|
293
|
+
# DEBUG=1 把收发的原始字节落盘 /tmp/raw_in.log、/tmp/raw_out.log 便于排查
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### 8.2 线路协议(关键!本服务用「换行分隔 JSON」而非 Content-Length 分帧)
|
|
297
|
+
|
|
298
|
+
flycode-mcp 的 stdio 传输使用 **「换行分隔 JSON」(newline-delimited JSON)** 线路格式(参考
|
|
299
|
+
`@modelcontextprotocol/sdk` 的 `stdio.js`:`serializeMessage = JSON.stringify(msg) + '\n'`,
|
|
300
|
+
`ReadBuffer` 按 `\n` 切分):**每条消息是一行独立的 JSON,以单个 `\n` 结尾**。这与官方 MCP 规范的
|
|
301
|
+
LSP 风格 `Content-Length:` 分帧**不同**,是本服务的实际实现,脚本必须匹配,否则握手会静默无响应
|
|
302
|
+
(服务端收不到可解析的帧,stdout 零字节回包)。
|
|
303
|
+
|
|
304
|
+
握手顺序:`initialize` → `notifications/initialized` → `mcp_activate`(按需) → `tools/call`(save_model_logic/save_form)。
|
|
305
|
+
`protocolVersion` 用 `2025-11-25`(与 SDK 一致)。
|
|
306
|
+
|
|
307
|
+
### 8.3 新建逻辑 / 表单时的硬性前置(否则保存被后端拒绝)
|
|
308
|
+
|
|
309
|
+
- **`modellogiccode` / `modellogicstepcode` 必须是纯数字 ID**:调用 `generate_unique_code` 时**不要传 `prefix`**
|
|
310
|
+
(无 prefix 返回纯 18 位数字,如 `893714100482347008`)。带字母前缀的 `code`(如 `AIT893713469948432384`)
|
|
311
|
+
会被后端 `saveModelLogic` 判 `modellogiccode不能为空` 而拒绝。
|
|
312
|
+
- 新建逻辑顶层对象**不要带** `metamodelcode` / `confsource`(详情接口不返回这两个字段,带上易触发 schema 报错)。
|
|
313
|
+
- `commitMessage` 必须是对象 `{ committype, committypetext, msgbody }`,与写工具要求一致
|
|
314
|
+
(`saverecord` 的 `msgbody` 由脚本写入当前时间)。
|
|
315
|
+
- 拿不准完整结构时,先用 `get_model_logic` 取一条同类型真实逻辑作模板,只替换官方 `code`、名称与 `flycode`,
|
|
316
|
+
其余字段原样保留,可最大化后端接受概率。
|
|
317
|
+
|
|
318
|
+
### 8.4 红线重申
|
|
319
|
+
|
|
320
|
+
本脚本**仅**用于「零、红线」列明的三条件同时满足的场景。除此之外任何自写脚本(含在脚本里直接打平台 REST、
|
|
321
|
+
或自己实现 MCP 工具 HTTP 客户端)一律禁止。脚本内也不得写入任何真实凭据——连接串永远来自运行环境的环境变量。
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// 官方标准脚本:超大 JSON 全量保存(flycode-mcp 例外场景专用)
|
|
4
|
+
// ----------------------------------------------------------------------------
|
|
5
|
+
// 适用场景(必须同时满足,否则不要用本脚本):
|
|
6
|
+
// 1) 确实必须使用完整保存工具 save_model_logic / save_form(如新建逻辑/表单、
|
|
7
|
+
// 或已有逻辑 JSON 巨大且局部工具无法满足需求);
|
|
8
|
+
// 2) 完整的 LogicResult / FormResult JSON 过大,无法经「AI → MCP 工具调用」内联
|
|
9
|
+
// 传递(会出现超长截断 / 上下文预算耗尽);
|
|
10
|
+
// 3) 局部工具 str_replace_flycode / patch_model_logic / patch_form 确实无法满足需求。
|
|
11
|
+
//
|
|
12
|
+
// 关键性质(不违反红线):
|
|
13
|
+
// - 本脚本是一个「真正的 MCP 客户端」:通过 npx 启动 flycode-mcp 服务,并**按 MCP
|
|
14
|
+
// 协议(stdio + JSON-RPC,换行分隔 JSON 线路格式)手写客户端**调用官方
|
|
15
|
+
// save_model_logic / save_form 工具完成全量保存。它不绕过 MCP,也绝不直接打
|
|
16
|
+
// 平台 REST 接口(直接打 REST 仍属红线,禁止)。
|
|
17
|
+
// - **零第三方依赖**:只用 Node.js 内置模块(child_process / fs),不依赖
|
|
18
|
+
// @modelcontextprotocol/sdk,避免「用户没装依赖就跑不起来」。
|
|
19
|
+
// - **零硬编码凭据**:脚本内不出现任何真实账号/密码/租户码。连接参数只从运行环境
|
|
20
|
+
// 的环境变量 FLYCODE_MCP_ARGS 读取(与你 MCP 客户端所用连接串一致),由调用方在
|
|
21
|
+
// 自己的 shell / MCP 配置里维护;本文件不写入、不打印、不落盘任何凭据。
|
|
22
|
+
//
|
|
23
|
+
// 用法:
|
|
24
|
+
// export FLYCODE_MCP_ARGS='username=...&password=...&tenantCode=...&envName=...' # 用你自己的,切勿提交
|
|
25
|
+
// node save_full_via_mcp.mjs <logic|form> <完整JSON文件路径> [提交说明]
|
|
26
|
+
//
|
|
27
|
+
// 可选环境变量(均无需改本脚本):
|
|
28
|
+
// FLYCODE_MCP_BIN 启动命令,默认 npx
|
|
29
|
+
// FLYCODE_MCP_PKG 包名,默认 flycode-mcp(即 npx flycode-mcp)
|
|
30
|
+
// FLYCODE_MCP_ARGS 连接串(必填,无默认值、无真实凭据)
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
import { spawn } from 'node:child_process'
|
|
34
|
+
import { readFileSync, appendFileSync, writeFileSync } from 'node:fs'
|
|
35
|
+
|
|
36
|
+
const BIN = process.env.FLYCODE_MCP_BIN || 'npx'
|
|
37
|
+
const PKG = process.env.FLYCODE_MCP_PKG || 'flycode-mcp'
|
|
38
|
+
const CONN_ARGS = process.env.FLYCODE_MCP_ARGS
|
|
39
|
+
|
|
40
|
+
// 诊断日志走 stderr(与 MCP 的 stdout 帧分离,不污染结果输出)
|
|
41
|
+
const dbg = (...a) => process.stderr.write('[script] ' + a.join(' ') + '\n')
|
|
42
|
+
|
|
43
|
+
if (!CONN_ARGS) {
|
|
44
|
+
console.error(
|
|
45
|
+
'缺少连接参数:请先设置环境变量 FLYCODE_MCP_ARGS 为你 MCP 客户端所用的连接串\n' +
|
|
46
|
+
'(形如 username=...&password=...&tenantCode=...&envName=...),切勿把真实凭据写进本脚本文件。'
|
|
47
|
+
)
|
|
48
|
+
process.exit(1)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const MODE = process.argv[2] // 'logic' | 'form'
|
|
52
|
+
const JSON_PATH = process.argv[3] // 完整 LogicResult / FormResult JSON 文件路径
|
|
53
|
+
const COMMIT_BODY = process.argv[4] || '全量保存(官方 MCP 客户端脚本)'
|
|
54
|
+
|
|
55
|
+
if (!['logic', 'form'].includes(MODE) || !JSON_PATH) {
|
|
56
|
+
console.error('用法: node save_full_via_mcp.mjs <logic|form> <完整JSON文件路径> [提交说明]')
|
|
57
|
+
process.exit(1)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 读取完整 JSON(从文件读取,避免经 AI 工具调用内联传递导致截断)
|
|
61
|
+
let payload
|
|
62
|
+
try {
|
|
63
|
+
payload = JSON.parse(readFileSync(JSON_PATH, 'utf8'))
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.error('读取/解析 JSON 失败:', e.message)
|
|
66
|
+
process.exit(1)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ---- 启动 flycode-mcp 服务(npx,继承当前环境;绝不打印/落盘任何凭据)----
|
|
70
|
+
dbg('spawn:', BIN, PKG, '<connArgs 已隐藏>')
|
|
71
|
+
if (process.env.DEBUG) writeFileSync('/tmp/raw_out.log', '')
|
|
72
|
+
const child = spawn(BIN, [PKG, CONN_ARGS], {
|
|
73
|
+
stdio: ['pipe', 'pipe', 'inherit'],
|
|
74
|
+
env: { ...process.env, npm_config_yes: 'true' },
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
child.on('error', (err) => {
|
|
78
|
+
console.error('启动 flycode-mcp 失败:', err.message)
|
|
79
|
+
process.exit(1)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// ---- 极简 MCP stdio 客户端(JSON-RPC 2.0 + 换行分隔 JSON 线路格式)----
|
|
83
|
+
let buf = Buffer.alloc(0)
|
|
84
|
+
let nextId = 1
|
|
85
|
+
const pending = new Map()
|
|
86
|
+
|
|
87
|
+
// 本服务(flycode-mcp)的 stdio 传输使用「换行分隔 JSON」线路格式:
|
|
88
|
+
// 每条消息是一行独立的 JSON,以单个 \n 结尾(参考 @modelcontextprotocol/sdk 的
|
|
89
|
+
// stdio.js:serializeMessage = JSON.stringify(msg) + '\n',ReadBuffer 按 '\n' 切分)。
|
|
90
|
+
// 注意:这与官方 MCP 规范的 Content-Length 分帧不同,是本服务的实际实现,必须匹配。
|
|
91
|
+
// 实现见下方 writeMessage(JSON + '\n')与 parseFrames(按 '\n' 逐行切分)。
|
|
92
|
+
function writeMessage(obj) {
|
|
93
|
+
const line = JSON.stringify(obj) + '\n'
|
|
94
|
+
const body = Buffer.from(line, 'utf8')
|
|
95
|
+
if (process.env.DEBUG) appendFileSync('/tmp/raw_in.log', body)
|
|
96
|
+
child.stdin.write(body)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function request(method, params) {
|
|
100
|
+
const id = nextId++
|
|
101
|
+
dbg('→ request id=' + id, method)
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
pending.set(id, { resolve, reject })
|
|
104
|
+
writeMessage({ jsonrpc: '2.0', id, method, params })
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function notify(method, params) {
|
|
109
|
+
dbg('→ notify', method)
|
|
110
|
+
writeMessage({ jsonrpc: '2.0', method, params })
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function handleMessage(msg) {
|
|
114
|
+
if (msg.id !== undefined && (msg.result !== undefined || msg.error !== undefined)) {
|
|
115
|
+
dbg('← response id=' + msg.id, msg.error ? 'ERROR' : 'ok')
|
|
116
|
+
const p = pending.get(msg.id)
|
|
117
|
+
if (p) {
|
|
118
|
+
pending.delete(msg.id)
|
|
119
|
+
if (msg.error) p.reject(new Error('MCP error: ' + JSON.stringify(msg.error)))
|
|
120
|
+
else p.resolve(msg.result)
|
|
121
|
+
}
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
// 服务端主动发来的请求(带 method+id)或通知(无 id):本脚本无需响应,忽略
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 解析 stdout:换行分隔 JSON 线路格式。缓冲区里逐行切分,每行是一条完整 JSON-RPC 消息。
|
|
128
|
+
function parseFrames() {
|
|
129
|
+
for (;;) {
|
|
130
|
+
const idx = buf.indexOf('\n')
|
|
131
|
+
if (idx === -1) break // 还没有完整的一行,等更多数据
|
|
132
|
+
const line = buf.subarray(0, idx)
|
|
133
|
+
buf = buf.subarray(idx + 1)
|
|
134
|
+
const s = line.toString('utf8').trim()
|
|
135
|
+
if (!s) continue // 跳过空行
|
|
136
|
+
try { handleMessage(JSON.parse(s)) } catch (_) { /* 跳过坏行 */ }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
child.stdout.on('data', (chunk) => {
|
|
141
|
+
if (process.env.DEBUG) {
|
|
142
|
+
appendFileSync('/tmp/raw_out.log', chunk)
|
|
143
|
+
process.stderr.write('[raw ' + chunk.length + 'B]\n')
|
|
144
|
+
}
|
|
145
|
+
buf = Buffer.concat([buf, chunk])
|
|
146
|
+
parseFrames()
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
child.stdout.on('end', () => {
|
|
150
|
+
if (pending.size > 0) {
|
|
151
|
+
console.error('连接意外中断,未收到工具响应')
|
|
152
|
+
process.exit(1)
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
process.on('unhandledRejection', (e) => {
|
|
157
|
+
console.error('未处理错误:', e && e.message ? e.message : e)
|
|
158
|
+
child.kill()
|
|
159
|
+
process.exit(1)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
// ---- 握手 + 调用 ----
|
|
163
|
+
;(async () => {
|
|
164
|
+
try {
|
|
165
|
+
await request('initialize', {
|
|
166
|
+
protocolVersion: '2025-11-25',
|
|
167
|
+
capabilities: {},
|
|
168
|
+
clientInfo: { name: 'flycode-full-save', version: '1.0.0' },
|
|
169
|
+
})
|
|
170
|
+
notify('notifications/initialized', {})
|
|
171
|
+
|
|
172
|
+
// 渐进式披露下对应域可能默认隐藏:先启用(服务端 enable 立即生效)
|
|
173
|
+
await request('tools/call', {
|
|
174
|
+
name: 'mcp_activate',
|
|
175
|
+
arguments: { domain: MODE === 'logic' ? 'logic' : 'form' },
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
const commitMessage = {
|
|
179
|
+
committype: 'saverecord',
|
|
180
|
+
committypetext: '保存记录',
|
|
181
|
+
msgbody: COMMIT_BODY,
|
|
182
|
+
}
|
|
183
|
+
const toolName = MODE === 'logic' ? 'save_model_logic' : 'save_form'
|
|
184
|
+
const argKey = MODE === 'logic' ? 'logic' : 'form'
|
|
185
|
+
|
|
186
|
+
const result = await request('tools/call', {
|
|
187
|
+
name: toolName,
|
|
188
|
+
arguments: { [argKey]: payload, commitMessage },
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
console.log(JSON.stringify(result, null, 2))
|
|
192
|
+
child.kill()
|
|
193
|
+
process.exit(0)
|
|
194
|
+
} catch (err) {
|
|
195
|
+
console.error('调用失败:', err.message)
|
|
196
|
+
child.kill()
|
|
197
|
+
process.exit(1)
|
|
198
|
+
}
|
|
199
|
+
})()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# 核心工具手册:run_sql / generate_unique_code
|
|
2
|
+
|
|
3
|
+
> 属 **core** 域,连接后**始终可见**,无需 `mcp_activate` 激活。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## `run_sql` — 执行 SQL 查询(只读)
|
|
8
|
+
|
|
9
|
+
在业务库(PostgreSQL)中执行一条 SELECT 查询。适用于直接探查业务表数据、核对字段值、验证同步结果等只读场景。
|
|
10
|
+
|
|
11
|
+
### 输入
|
|
12
|
+
|
|
13
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
14
|
+
| --- | --- | --- | --- |
|
|
15
|
+
| `sql` | string | 是 | 要执行的 SQL 语句,**只支持 SELECT,一次只能一条** |
|
|
16
|
+
|
|
17
|
+
### 输出
|
|
18
|
+
|
|
19
|
+
| 字段 | 类型 | 说明 |
|
|
20
|
+
| --- | --- | --- |
|
|
21
|
+
| `data` | array \| null | 查询结果数据,数组中每一项为一条记录(对象,列名 → 值) |
|
|
22
|
+
| `influenceCount` | number \| null | 查询结果总数 |
|
|
23
|
+
|
|
24
|
+
> 顶层为宽松对象(looseObject):服务端偶发返回的额外字段原样透传,不会被 schema 校验剥离。
|
|
25
|
+
|
|
26
|
+
### 限制与坑
|
|
27
|
+
|
|
28
|
+
- **仅 SELECT、一次一条**;INSERT / UPDATE / DELETE / DDL 一律不支持。
|
|
29
|
+
- `run_sql` 是**唯一** sanctioned 的查库通道:严禁另开 psql / pg 驱动 / ORM 直连业务库。
|
|
30
|
+
- 查表字段结构不要用 SQL 猜(如 `information_schema`),直接用 `get_entity`。
|
|
31
|
+
- 出错时返回 `isError:true`,content 为错误体原文 JSON(如 `{"error_type":600,"error_code":"token无效,请重新登录","error_params":null}`),不做二次包装。
|
|
32
|
+
- 数据字典文本翻译:`LEFT JOIN pl_dictionary d ON d.dickey = k.<列名>` 即可,不必拼 `dictionarycode`;字典编码超过 2^53 时在 SQL 里用字符串常量比较,避免精度丢失。
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## `generate_unique_code` — 生成唯一编码(本地)
|
|
37
|
+
|
|
38
|
+
通过雪花算法生成全局唯一 ID(code),以**字符串**形式返回以避免超过 `Number.MAX_SAFE_INTEGER` 的大整数精度丢失。新建实体 / 业务逻辑 / 表单等内容时 JSON 需携带唯一 code,一律用本工具获取,**不要自己按 epoch 拼 ID**。
|
|
39
|
+
|
|
40
|
+
### 输入
|
|
41
|
+
|
|
42
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
43
|
+
| --- | --- | --- | --- |
|
|
44
|
+
| `prefix` | string | 否 | 可选前缀,拼接到生成的唯一 ID 之前(例如实体编码前缀);不传则只返回纯数字 ID 字符串 |
|
|
45
|
+
|
|
46
|
+
### 输出
|
|
47
|
+
|
|
48
|
+
| 字段 | 类型 | 说明 |
|
|
49
|
+
| --- | --- | --- |
|
|
50
|
+
| `code` | string | 生成的唯一编码,如 `893714100482347008`(18 位纯数字) |
|
|
51
|
+
|
|
52
|
+
### 坑(重要)
|
|
53
|
+
|
|
54
|
+
- **新建业务逻辑**时 `modellogiccode` / `modellogicstepcode` 调用本工具**不要传 `prefix`**:无 prefix 返回纯 18 位数字 ID,后端 `saveModelLogic` 要求 `modellogiccode` 为纯数字;带字母前缀(如 `AIT893713469948432384`)会被后端按数字解析失败、判 `modellogiccode不能为空` 而拒绝保存。
|
|
55
|
+
- 实体 `objectcode` 同样用无 prefix 口径(纯 18 位数字,与逻辑 code 同口径);表单 / 其他场景可视情况带 prefix。
|
|
56
|
+
- 本工具仅在本地生成 ID,不改任何服务端数据;时钟回拨过大等异常时返回 `isError:true` 与错误信息。
|