@wukongcrm/mcp-server 0.2.0 → 0.2.1

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 CHANGED
@@ -198,6 +198,12 @@ wukong-mcp
198
198
  - 打印与消息:`nocode_query_printing`、`nocode_write_printing`、`nocode_query_messages`、`nocode_write_message`
199
199
  - 开放能力与应用市场:`nocode_query_integrations`、`nocode_write_integration`、`nocode_query_marketplace`、`nocode_write_marketplace`
200
200
 
201
+ 常见保存动作必须使用旧前端对应的聚合 BO,而不是把单个字段或单个值直接作为 `payload`:
202
+
203
+ - 字段保存:`ModuleFieldSaveBO`,至少包含 `moduleId`、实际 `version`、`mainFieldId`/`tempMainFieldId` 和非空 `moduleFieldList`。
204
+ - 记录保存:`ModuleFieldDataSaveBO`,至少包含 `moduleId`、实际 `version` 和非空 `fieldDataList`;字段值使用 `fieldId`、`fieldName`、`name`、`type`、`value`,修改时增加 `dataId`。
205
+ - 新建模块的实际版本通常从 `0` 开始,应先用 `nocode_get_module` 读取并显式传入,不能根据已发布老模块猜测为 `1`。
206
+
201
207
  文件流仍有意不经 MCP 代理,因此应用导入/导出、Excel 导入/导出、跟进导出、打印 PDF/Word 下载不在工具中;打印内容生成和预览 JSON 接口仍可使用。内部测试接口 `/moduleMQ/send` 也不会暴露。
202
208
 
203
209
  ### CRM、OA、HRM、财务、进销存与工单工具
package/dist/nocode.js CHANGED
@@ -62,7 +62,7 @@ export const nocodeToolDefinitions = [
62
62
  },
63
63
  {
64
64
  name: "nocode_get_module_schema",
65
- description: "一次获取无代码模块字段、表单布局、列表头、页面布局、选项、公式、标签和序列号规则;写入记录前应先调用;只读。",
65
+ description: "一次获取无代码模块字段、表单布局、列表头、页面布局、选项、公式、标签和序列号规则;写入记录前应先调用,并优先使用 nocode_get_module 返回的实际 version(新建模块通常为 0);只读。",
66
66
  schema: z.object({ ...moduleContextShape, includeExtended: z.boolean().optional() }),
67
67
  access: "read"
68
68
  },
@@ -243,7 +243,7 @@ export const nocodeToolDefinitions = [
243
243
  },
244
244
  {
245
245
  name: "nocode_write_module",
246
- description: "保存、启停、复制、发布、丢弃草稿、删除模块、保存页面布局或维护 BI Dashboard 模块;必须 confirm=true。",
246
+ description: "保存、启停、复制、发布、丢弃草稿、删除模块、保存页面布局或维护 BI Dashboard 模块;action=save 的 payload 使用旧 CRM ModuleSaveBO(常见普通模块包含 applicationId、name、moduleType=1、icon);必须 confirm=true。",
247
247
  schema: z.object({
248
248
  action: z.enum(["save", "status", "copy", "publish", "discardDraft", "delete", "layout", "deleteCancelledCompanyData", "dashboardSave", "dashboardRemove"]),
249
249
  moduleId: idSchema.optional(),
@@ -257,14 +257,14 @@ export const nocodeToolDefinitions = [
257
257
  },
258
258
  {
259
259
  name: "nocode_write_field",
260
- description: "保存字段,或修改普通/固定模块列表字段排序和样式;必须 confirm=true,否则只返回固定接口和请求预览。",
260
+ description: "保存字段,或修改普通/固定模块列表字段排序和样式;action=save 的 payload 必须是聚合 ModuleFieldSaveBO,包含 moduleId、实际 version、mainFieldId 或 tempMainFieldId、moduleFieldList,不能直接传单个字段;必须 confirm=true,否则只返回固定接口和请求预览。",
261
261
  schema: z.object({ action: z.enum(["save", "sort", "style", "fixedSort", "fixedStyle"]), ...payloadWriteShape }),
262
262
  access: "write",
263
263
  destructive: true
264
264
  },
265
265
  {
266
266
  name: "nocode_write_record",
267
- description: "新增/修改、删除、转移负责人或设置分组;payload 使用对应旧 CRM BO;必须 confirm=true,否则只返回预览。",
267
+ description: "新增/修改、删除、转移负责人或设置分组;action=save 的 payload 必须是 ModuleFieldDataSaveBO,包含 moduleId、实际 version、fieldDataList;每个字段值应包含 fieldId、fieldName、name、type、value,修改时再带 dataId;必须 confirm=true,否则只返回预览。",
268
268
  schema: z.object({ action: z.enum(["save", "delete", "transfer", "setCategory"]), ...payloadWriteShape }),
269
269
  access: "write",
270
270
  destructive: true
@@ -430,9 +430,11 @@ export function createNocodeToolHandlers(config = {}) {
430
430
  const moduleId = requiredId(args.moduleId, "moduleId");
431
431
  const version = requiredVersion(args.version);
432
432
  const query = cleanObject({ moduleId: numericValue(moduleId), version, categoryId: args.categoryId });
433
+ // 旧后端会直接拆箱 filterHidden;显式传 false 与无代码前端的字段配置请求保持一致,避免空值触发 500。
434
+ const formQuery = { ...query, filterHidden: false };
433
435
  const [fields, form, listHead, layout, unionModules] = await Promise.all([
434
436
  client.post("/moduleField/queryList", query),
435
- client.post("/moduleField/formList", query),
437
+ client.post("/moduleField/formList", formQuery),
436
438
  client.post("/moduleField/queryListHead", query),
437
439
  client.post(`/moduleM/pageLayout/${moduleId}/${version}`),
438
440
  client.post(`/moduleM/unionModules/${moduleId}/${version}`)
@@ -517,14 +519,14 @@ export function createNocodeToolHandlers(config = {}) {
517
519
  nocode_query_marketplace: async (args) => executeNocodeRead(client, queryMarketplaceRoute(args)),
518
520
  nocode_write_application: async (args) => executeApplicationWrite(client, args),
519
521
  nocode_write_module: async (args) => executeModuleWrite(client, args),
520
- nocode_write_field: async (args) => executePayloadWrite(client, args, {
522
+ nocode_write_field: async (args) => executeFieldWrite(client, args, {
521
523
  save: "/moduleField/save",
522
524
  sort: "/moduleField/setFieldSort",
523
525
  style: "/moduleField/setFieldStyle",
524
526
  fixedSort: "/moduleField/fixed/setFieldSort",
525
527
  fixedStyle: "/moduleField/fixed/setFieldStyle"
526
528
  }),
527
- nocode_write_record: async (args) => executePayloadWrite(client, args, {
529
+ nocode_write_record: async (args) => executeRecordWrite(client, args, {
528
530
  save: "/moduleFieldData/save",
529
531
  delete: "/moduleFieldData/delete",
530
532
  transfer: "/moduleFieldData/transfer",
@@ -874,11 +876,11 @@ async function executeApplicationWrite(client, args) {
874
876
  switch (action) {
875
877
  case "create":
876
878
  targetPath = "/moduleMetadata/save";
877
- body = safePayload(args.payload);
879
+ body = applicationPayload(args.payload, action);
878
880
  break;
879
881
  case "update":
880
882
  targetPath = "/moduleMetadata/update";
881
- body = safePayload(args.payload);
883
+ body = applicationPayload(args.payload, action);
882
884
  break;
883
885
  case "status":
884
886
  targetPath = `/moduleMetadata/updateStatus/${requiredId(args.applicationId, "applicationId")}`;
@@ -954,6 +956,20 @@ async function executePayloadWrite(client, args, paths) {
954
956
  }
955
957
  return confirmedPost(client, args, action, targetPath, safePayload(args.payload));
956
958
  }
959
+ async function executeFieldWrite(client, args, paths) {
960
+ const action = String(args.action);
961
+ if (action === "save") {
962
+ validateAggregateSavePayload(args.payload, "ModuleFieldSaveBO", "moduleFieldList");
963
+ }
964
+ return executePayloadWrite(client, args, paths);
965
+ }
966
+ async function executeRecordWrite(client, args, paths) {
967
+ const action = String(args.action);
968
+ if (action === "save") {
969
+ validateAggregateSavePayload(args.payload, "ModuleFieldDataSaveBO", "fieldDataList");
970
+ }
971
+ return executePayloadWrite(client, args, paths);
972
+ }
957
973
  async function executeSceneWrite(client, args) {
958
974
  const action = String(args.action);
959
975
  const targetPath = action === "delete" || action === "default"
@@ -1075,6 +1091,40 @@ function safePayload(value) {
1075
1091
  }
1076
1092
  return value;
1077
1093
  }
1094
+ function applicationPayload(value, action) {
1095
+ const payload = safePayload(value);
1096
+ const name = typeof payload.name === "string" ? payload.name.trim() : "";
1097
+ if (!name) {
1098
+ throw new Error("无代码应用名称不能为空。");
1099
+ }
1100
+ if (Array.from(name).length > 20) {
1101
+ throw new Error("无代码应用名称不能超过 20 个字符。");
1102
+ }
1103
+ const description = payload.description;
1104
+ if (description !== undefined && (typeof description !== "string" || Array.from(description).length > 50)) {
1105
+ throw new Error("无代码应用描述必须是字符串且不能超过 50 个字符。");
1106
+ }
1107
+ const icon = typeof payload.icon === "string" ? payload.icon.trim() : "";
1108
+ if (!icon) {
1109
+ throw new Error("无代码应用图标不能为空。");
1110
+ }
1111
+ // 创建时补齐旧前端提交的非业务默认值;更新仍保留调用方提供的完整应用配置。
1112
+ return action === "create"
1113
+ ? { ...payload, name, icon, description: description ?? "", iconColor: payload.iconColor ?? "#EBECF0", manageUserIds: payload.manageUserIds ?? [] }
1114
+ : { ...payload, name, icon };
1115
+ }
1116
+ function validateAggregateSavePayload(value, boName, listField) {
1117
+ const payload = safePayload(value);
1118
+ requiredId(payload.moduleId, "payload.moduleId");
1119
+ // 新建模块的有效版本通常为 0;不能用 requiredVersion 的默认值掩盖调用方漏传版本。
1120
+ if (payload.version === undefined) {
1121
+ throw new Error(`${boName} 必须包含实际 version;请先读取模块详情。`);
1122
+ }
1123
+ requiredVersion(payload.version);
1124
+ if (!Array.isArray(payload[listField]) || payload[listField].length === 0) {
1125
+ throw new Error(`${boName} 必须包含非空 ${listField}。`);
1126
+ }
1127
+ }
1078
1128
  function cleanObject(input) {
1079
1129
  const output = {};
1080
1130
  for (const [key, value] of Object.entries(input)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wukongcrm/mcp-server",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Local and remote MCP server for fixed 72CRM API access.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",