@wukongcrm/mcp-server 0.2.0 → 0.2.2
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 +8 -2
- package/dist/client.d.ts +1 -0
- package/dist/client.js +9 -1
- package/dist/nocode.d.ts +2 -2
- package/dist/nocode.js +124 -17
- package/dist/server.js +5 -3
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -147,7 +147,7 @@ npm pack
|
|
|
147
147
|
生成文件示例:
|
|
148
148
|
|
|
149
149
|
```text
|
|
150
|
-
wukongcrm-mcp-server-0.2.
|
|
150
|
+
wukongcrm-mcp-server-0.2.2.tgz
|
|
151
151
|
```
|
|
152
152
|
|
|
153
153
|
这个包只包含运行所需的 `dist`、`README.md` 和 `package.json`,不会携带源码、测试文件或用户 API Key。
|
|
@@ -155,7 +155,7 @@ wukongcrm-mcp-server-0.2.0.tgz
|
|
|
155
155
|
使用者拿到 `.tgz` 后安装:
|
|
156
156
|
|
|
157
157
|
```powershell
|
|
158
|
-
npm install -g .\wukongcrm-mcp-server-0.2.
|
|
158
|
+
npm install -g .\wukongcrm-mcp-server-0.2.2.tgz
|
|
159
159
|
```
|
|
160
160
|
|
|
161
161
|
安装后可以直接用命令启动:
|
|
@@ -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/client.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export declare class CrmClient {
|
|
|
19
19
|
private readonly apiKey;
|
|
20
20
|
private readonly fetchImpl;
|
|
21
21
|
private adminToken;
|
|
22
|
+
private loginPromise;
|
|
22
23
|
constructor(config?: CrmClientConfig);
|
|
23
24
|
authStatus(): Promise<AuthStatusResult>;
|
|
24
25
|
call<T = unknown>(method: "GET" | "POST", path: string, body?: unknown, query?: Record<string, unknown>): Promise<T>;
|
package/dist/client.js
CHANGED
|
@@ -5,6 +5,7 @@ export class CrmClient {
|
|
|
5
5
|
apiKey;
|
|
6
6
|
fetchImpl;
|
|
7
7
|
adminToken = "";
|
|
8
|
+
loginPromise;
|
|
8
9
|
constructor(config = {}) {
|
|
9
10
|
this.baseUrl = normalizeBaseUrl(config.baseUrl ?? process.env.CRM_BASE_URL ?? "https://www.72crm.com/api-11/");
|
|
10
11
|
this.apiKey = config.apiKey ?? process.env.CRM_API_KEY ?? "";
|
|
@@ -51,7 +52,14 @@ export class CrmClient {
|
|
|
51
52
|
if (!force && this.adminToken.trim()) {
|
|
52
53
|
return;
|
|
53
54
|
}
|
|
54
|
-
|
|
55
|
+
// Schema 聚合会并发请求多个只读接口。首次调用时必须把 API Key 登录合并成一次,
|
|
56
|
+
// 否则旧 CRM 会在短时间内收到多次登录并让部分请求鉴权失败。
|
|
57
|
+
if (!this.loginPromise) {
|
|
58
|
+
this.loginPromise = this.loginWithApiKey().finally(() => {
|
|
59
|
+
this.loginPromise = undefined;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
this.adminToken = await this.loginPromise;
|
|
55
63
|
}
|
|
56
64
|
async loginWithApiKey() {
|
|
57
65
|
try {
|
package/dist/nocode.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { type CrmClientConfig } from "./client.js";
|
|
2
|
+
import { CrmClient, type CrmClientConfig } from "./client.js";
|
|
3
3
|
export type NocodeToolAccess = "read" | "write";
|
|
4
4
|
export interface NocodeToolDefinition {
|
|
5
5
|
name: string;
|
|
@@ -12,5 +12,5 @@ type AnyArgs = Record<string, any>;
|
|
|
12
12
|
type Handler = (args: AnyArgs) => Promise<any> | any;
|
|
13
13
|
export type NocodeToolHandlers = Record<string, Handler>;
|
|
14
14
|
export declare const nocodeToolDefinitions: NocodeToolDefinition[];
|
|
15
|
-
export declare function createNocodeToolHandlers(config?: CrmClientConfig): NocodeToolHandlers;
|
|
15
|
+
export declare function createNocodeToolHandlers(config?: CrmClientConfig, sharedClient?: CrmClient): NocodeToolHandlers;
|
|
16
16
|
export {};
|
package/dist/nocode.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { CrmClient } from "./client.js";
|
|
3
|
-
const idSchema = z.union([
|
|
3
|
+
const idSchema = z.union([
|
|
4
|
+
z.string().regex(/^\d+$/, "ID 必须是数字字符串。"),
|
|
5
|
+
z.number().int().safe("超过 JavaScript 安全整数范围的 ID 必须以字符串传入。")
|
|
6
|
+
]);
|
|
4
7
|
const payloadSchema = z.record(z.unknown());
|
|
5
8
|
const confirmSchema = z.boolean().optional();
|
|
6
9
|
const versionSchema = z.number().int().nonnegative().optional();
|
|
@@ -62,7 +65,7 @@ export const nocodeToolDefinitions = [
|
|
|
62
65
|
},
|
|
63
66
|
{
|
|
64
67
|
name: "nocode_get_module_schema",
|
|
65
|
-
description: "
|
|
68
|
+
description: "一次获取无代码模块字段、表单布局、列表头、页面布局、选项、公式、标签和序列号规则;写入记录前应先调用,并优先使用 nocode_get_module 返回的实际 version(新建模块通常为 0);雪花 moduleId 必须以字符串传入,避免 JavaScript 数字精度丢失;只读。",
|
|
66
69
|
schema: z.object({ ...moduleContextShape, includeExtended: z.boolean().optional() }),
|
|
67
70
|
access: "read"
|
|
68
71
|
},
|
|
@@ -193,7 +196,7 @@ export const nocodeToolDefinitions = [
|
|
|
193
196
|
},
|
|
194
197
|
{
|
|
195
198
|
name: "nocode_query_printing",
|
|
196
|
-
description: "
|
|
199
|
+
description: "查询无代码打印模板字段、模板列表/详情和打印记录;只读。records 会先确认当前模块存在打印模板,规避旧后端在空模板集合上生成无效 SQL;打印、预览和模板变更请使用 nocode_write_printing。",
|
|
197
200
|
schema: z.object({
|
|
198
201
|
operation: z.enum(["fields", "templates", "template", "records", "record"]),
|
|
199
202
|
...extendedContextShape
|
|
@@ -243,7 +246,7 @@ export const nocodeToolDefinitions = [
|
|
|
243
246
|
},
|
|
244
247
|
{
|
|
245
248
|
name: "nocode_write_module",
|
|
246
|
-
description: "保存、启停、复制、发布、丢弃草稿、删除模块、保存页面布局或维护 BI Dashboard
|
|
249
|
+
description: "保存、启停、复制、发布、丢弃草稿、删除模块、保存页面布局或维护 BI Dashboard 模块;action=save 的 payload 使用旧 CRM ModuleSaveBO(常见普通模块包含 applicationId、name、moduleType=1、icon);必须 confirm=true。",
|
|
247
250
|
schema: z.object({
|
|
248
251
|
action: z.enum(["save", "status", "copy", "publish", "discardDraft", "delete", "layout", "deleteCancelledCompanyData", "dashboardSave", "dashboardRemove"]),
|
|
249
252
|
moduleId: idSchema.optional(),
|
|
@@ -257,14 +260,14 @@ export const nocodeToolDefinitions = [
|
|
|
257
260
|
},
|
|
258
261
|
{
|
|
259
262
|
name: "nocode_write_field",
|
|
260
|
-
description: "
|
|
263
|
+
description: "保存字段,或修改普通/固定模块列表字段排序和样式;action=save 的 payload 必须是聚合 ModuleFieldSaveBO,包含 moduleId、实际 version、mainFieldId 或 tempMainFieldId、moduleFieldList,不能直接传单个字段;必须 confirm=true,否则只返回固定接口和请求预览。",
|
|
261
264
|
schema: z.object({ action: z.enum(["save", "sort", "style", "fixedSort", "fixedStyle"]), ...payloadWriteShape }),
|
|
262
265
|
access: "write",
|
|
263
266
|
destructive: true
|
|
264
267
|
},
|
|
265
268
|
{
|
|
266
269
|
name: "nocode_write_record",
|
|
267
|
-
description: "新增/修改、删除、转移负责人或设置分组;payload
|
|
270
|
+
description: "新增/修改、删除、转移负责人或设置分组;action=save 的 payload 必须是 ModuleFieldDataSaveBO,包含 moduleId、实际 version、fieldDataList;每个字段值应包含 fieldId、fieldName、name、type、value,修改时再带 dataId;必须 confirm=true,否则只返回预览。",
|
|
268
271
|
schema: z.object({ action: z.enum(["save", "delete", "transfer", "setCategory"]), ...payloadWriteShape }),
|
|
269
272
|
access: "write",
|
|
270
273
|
destructive: true
|
|
@@ -395,8 +398,8 @@ export const nocodeToolDefinitions = [
|
|
|
395
398
|
destructive: true
|
|
396
399
|
}
|
|
397
400
|
];
|
|
398
|
-
export function createNocodeToolHandlers(config = {}) {
|
|
399
|
-
const client = new CrmClient(config);
|
|
401
|
+
export function createNocodeToolHandlers(config = {}, sharedClient) {
|
|
402
|
+
const client = sharedClient ?? new CrmClient(config);
|
|
400
403
|
return {
|
|
401
404
|
nocode_list_applications: async () => readonlyResult("/moduleMetadata/customAppList", await client.post("/moduleMetadata/customAppList")),
|
|
402
405
|
nocode_get_application: async (args) => {
|
|
@@ -430,10 +433,24 @@ export function createNocodeToolHandlers(config = {}) {
|
|
|
430
433
|
const moduleId = requiredId(args.moduleId, "moduleId");
|
|
431
434
|
const version = requiredVersion(args.version);
|
|
432
435
|
const query = cleanObject({ moduleId: numericValue(moduleId), version, categoryId: args.categoryId });
|
|
433
|
-
|
|
436
|
+
// 旧后端会直接拆箱 filterHidden;显式传 false 与无代码前端的字段配置请求保持一致,避免空值触发 500。
|
|
437
|
+
const formQuery = { ...query, filterHidden: false };
|
|
438
|
+
const [fields, form, listHeadOutcome, layout, unionModules] = await Promise.all([
|
|
434
439
|
client.post("/moduleField/queryList", query),
|
|
435
|
-
client.post("/moduleField/formList",
|
|
436
|
-
client.post("/moduleField/queryListHead", query)
|
|
440
|
+
client.post("/moduleField/formList", formQuery),
|
|
441
|
+
client.post("/moduleField/queryListHead", query)
|
|
442
|
+
.then((data) => ({ data, warning: undefined }))
|
|
443
|
+
.catch((error) => {
|
|
444
|
+
// 旧端的列表头服务只读取“已发布激活”的模块。新建草稿在字段已经存在时仍会返回 4303,
|
|
445
|
+
// 但字段列表和表单结构本身可正常读取;不要让该可预期边界拖垮整个聚合 Schema。
|
|
446
|
+
if (isModuleNotFoundError(error)) {
|
|
447
|
+
return {
|
|
448
|
+
data: [],
|
|
449
|
+
warning: "当前模块尚未发布,旧 CRM 不提供列表头;fields/form 仍是有效的草稿 Schema。"
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
throw error;
|
|
453
|
+
}),
|
|
437
454
|
client.post(`/moduleM/pageLayout/${moduleId}/${version}`),
|
|
438
455
|
client.post(`/moduleM/unionModules/${moduleId}/${version}`)
|
|
439
456
|
]);
|
|
@@ -451,9 +468,10 @@ export function createNocodeToolHandlers(config = {}) {
|
|
|
451
468
|
version,
|
|
452
469
|
fields,
|
|
453
470
|
form,
|
|
454
|
-
listHead,
|
|
471
|
+
listHead: listHeadOutcome.data,
|
|
455
472
|
layout,
|
|
456
473
|
unionModules,
|
|
474
|
+
...(listHeadOutcome.warning ? { warnings: [listHeadOutcome.warning] } : {}),
|
|
457
475
|
...extended
|
|
458
476
|
};
|
|
459
477
|
},
|
|
@@ -511,20 +529,20 @@ export function createNocodeToolHandlers(config = {}) {
|
|
|
511
529
|
nocode_query_workflow: async (args) => executeNocodeRead(client, queryWorkflowRoute(args)),
|
|
512
530
|
nocode_query_followups: async (args) => executeNocodeRead(client, queryFollowUpRoute(args)),
|
|
513
531
|
nocode_query_configuration: async (args) => executeNocodeRead(client, queryConfigurationRoute(args)),
|
|
514
|
-
nocode_query_printing: async (args) =>
|
|
532
|
+
nocode_query_printing: async (args) => executePrintingRead(client, args),
|
|
515
533
|
nocode_query_messages: async (args) => executeNocodeRead(client, queryMessageRoute(args)),
|
|
516
534
|
nocode_query_integrations: async (args) => executeNocodeRead(client, queryIntegrationRoute(args)),
|
|
517
535
|
nocode_query_marketplace: async (args) => executeNocodeRead(client, queryMarketplaceRoute(args)),
|
|
518
536
|
nocode_write_application: async (args) => executeApplicationWrite(client, args),
|
|
519
537
|
nocode_write_module: async (args) => executeModuleWrite(client, args),
|
|
520
|
-
nocode_write_field: async (args) =>
|
|
538
|
+
nocode_write_field: async (args) => executeFieldWrite(client, args, {
|
|
521
539
|
save: "/moduleField/save",
|
|
522
540
|
sort: "/moduleField/setFieldSort",
|
|
523
541
|
style: "/moduleField/setFieldStyle",
|
|
524
542
|
fixedSort: "/moduleField/fixed/setFieldSort",
|
|
525
543
|
fixedStyle: "/moduleField/fixed/setFieldStyle"
|
|
526
544
|
}),
|
|
527
|
-
nocode_write_record: async (args) =>
|
|
545
|
+
nocode_write_record: async (args) => executeRecordWrite(client, args, {
|
|
528
546
|
save: "/moduleFieldData/save",
|
|
529
547
|
delete: "/moduleFieldData/delete",
|
|
530
548
|
transfer: "/moduleFieldData/transfer",
|
|
@@ -564,6 +582,41 @@ export function createNocodeToolHandlers(config = {}) {
|
|
|
564
582
|
async function executeNocodeRead(client, route) {
|
|
565
583
|
return readonlyResult(route.targetPath, await client.post(route.targetPath, route.body, route.query), cleanObject({ operation: route.operation, body: route.body, query: route.query }));
|
|
566
584
|
}
|
|
585
|
+
async function executePrintingRead(client, args) {
|
|
586
|
+
const route = queryPrintingRoute(args);
|
|
587
|
+
if (route.operation !== "records") {
|
|
588
|
+
return executeNocodeRead(client, route);
|
|
589
|
+
}
|
|
590
|
+
const moduleId = requiredId(args.moduleId, "moduleId");
|
|
591
|
+
let version;
|
|
592
|
+
if (args.version !== undefined) {
|
|
593
|
+
version = requiredVersion(args.version);
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
const moduleDetail = await client.post(`/moduleM/queryById/${moduleId}`, undefined, { isLatest: true });
|
|
597
|
+
version = requiredVersion(moduleDetail?.version);
|
|
598
|
+
}
|
|
599
|
+
// 旧后端 queryPrintRecord 会对空模板 ID 集合直接拼接 IN (),最终只返回笼统的
|
|
600
|
+
// “Network Busy”。先用模板列表判断空集,空时按真实业务语义返回无打印记录。
|
|
601
|
+
const templatePath = `/modulePrintTemplate/list/${moduleId}/${version}`;
|
|
602
|
+
const templatePage = await client.post(templatePath, { page: 1, limit: 1 });
|
|
603
|
+
const templateRows = listRows(templatePage);
|
|
604
|
+
if (templateRows?.length === 0) {
|
|
605
|
+
return readonlyResult(route.targetPath, [], {
|
|
606
|
+
operation: route.operation,
|
|
607
|
+
preflight: { targetPath: templatePath, version, emptyTemplates: true }
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
return executeNocodeRead(client, route);
|
|
611
|
+
}
|
|
612
|
+
function listRows(value) {
|
|
613
|
+
if (Array.isArray(value))
|
|
614
|
+
return value;
|
|
615
|
+
if (!value || typeof value !== "object")
|
|
616
|
+
return undefined;
|
|
617
|
+
const list = value.list;
|
|
618
|
+
return Array.isArray(list) ? list : undefined;
|
|
619
|
+
}
|
|
567
620
|
function executeNocodeWrite(client, args, route) {
|
|
568
621
|
return confirmedPost(client, args, route.operation, route.targetPath, route.body, route.query);
|
|
569
622
|
}
|
|
@@ -874,11 +927,11 @@ async function executeApplicationWrite(client, args) {
|
|
|
874
927
|
switch (action) {
|
|
875
928
|
case "create":
|
|
876
929
|
targetPath = "/moduleMetadata/save";
|
|
877
|
-
body =
|
|
930
|
+
body = applicationPayload(args.payload, action);
|
|
878
931
|
break;
|
|
879
932
|
case "update":
|
|
880
933
|
targetPath = "/moduleMetadata/update";
|
|
881
|
-
body =
|
|
934
|
+
body = applicationPayload(args.payload, action);
|
|
882
935
|
break;
|
|
883
936
|
case "status":
|
|
884
937
|
targetPath = `/moduleMetadata/updateStatus/${requiredId(args.applicationId, "applicationId")}`;
|
|
@@ -954,6 +1007,20 @@ async function executePayloadWrite(client, args, paths) {
|
|
|
954
1007
|
}
|
|
955
1008
|
return confirmedPost(client, args, action, targetPath, safePayload(args.payload));
|
|
956
1009
|
}
|
|
1010
|
+
async function executeFieldWrite(client, args, paths) {
|
|
1011
|
+
const action = String(args.action);
|
|
1012
|
+
if (action === "save") {
|
|
1013
|
+
validateAggregateSavePayload(args.payload, "ModuleFieldSaveBO", "moduleFieldList");
|
|
1014
|
+
}
|
|
1015
|
+
return executePayloadWrite(client, args, paths);
|
|
1016
|
+
}
|
|
1017
|
+
async function executeRecordWrite(client, args, paths) {
|
|
1018
|
+
const action = String(args.action);
|
|
1019
|
+
if (action === "save") {
|
|
1020
|
+
validateAggregateSavePayload(args.payload, "ModuleFieldDataSaveBO", "fieldDataList");
|
|
1021
|
+
}
|
|
1022
|
+
return executePayloadWrite(client, args, paths);
|
|
1023
|
+
}
|
|
957
1024
|
async function executeSceneWrite(client, args) {
|
|
958
1025
|
const action = String(args.action);
|
|
959
1026
|
const targetPath = action === "delete" || action === "default"
|
|
@@ -1026,12 +1093,18 @@ function readonlyResult(targetPath, data, extra = {}) {
|
|
|
1026
1093
|
return { readonly: true, targetPath, ...extra, data };
|
|
1027
1094
|
}
|
|
1028
1095
|
function requiredId(value, field) {
|
|
1096
|
+
if (typeof value === "number" && !Number.isSafeInteger(value)) {
|
|
1097
|
+
throw new Error(`${field} 超过 JavaScript 安全整数范围,必须以字符串传入。`);
|
|
1098
|
+
}
|
|
1029
1099
|
const normalized = String(value ?? "").trim();
|
|
1030
1100
|
if (!/^\d+$/.test(normalized)) {
|
|
1031
1101
|
throw new Error(`${field} 必须是数字 ID。`);
|
|
1032
1102
|
}
|
|
1033
1103
|
return normalized;
|
|
1034
1104
|
}
|
|
1105
|
+
function isModuleNotFoundError(error) {
|
|
1106
|
+
return error instanceof Error && error.message.includes("模块未找到");
|
|
1107
|
+
}
|
|
1035
1108
|
function numericValue(value) {
|
|
1036
1109
|
const numeric = Number(value);
|
|
1037
1110
|
return Number.isSafeInteger(numeric) ? numeric : value;
|
|
@@ -1075,6 +1148,40 @@ function safePayload(value) {
|
|
|
1075
1148
|
}
|
|
1076
1149
|
return value;
|
|
1077
1150
|
}
|
|
1151
|
+
function applicationPayload(value, action) {
|
|
1152
|
+
const payload = safePayload(value);
|
|
1153
|
+
const name = typeof payload.name === "string" ? payload.name.trim() : "";
|
|
1154
|
+
if (!name) {
|
|
1155
|
+
throw new Error("无代码应用名称不能为空。");
|
|
1156
|
+
}
|
|
1157
|
+
if (Array.from(name).length > 20) {
|
|
1158
|
+
throw new Error("无代码应用名称不能超过 20 个字符。");
|
|
1159
|
+
}
|
|
1160
|
+
const description = payload.description;
|
|
1161
|
+
if (description !== undefined && (typeof description !== "string" || Array.from(description).length > 50)) {
|
|
1162
|
+
throw new Error("无代码应用描述必须是字符串且不能超过 50 个字符。");
|
|
1163
|
+
}
|
|
1164
|
+
const icon = typeof payload.icon === "string" ? payload.icon.trim() : "";
|
|
1165
|
+
if (!icon) {
|
|
1166
|
+
throw new Error("无代码应用图标不能为空。");
|
|
1167
|
+
}
|
|
1168
|
+
// 创建时补齐旧前端提交的非业务默认值;更新仍保留调用方提供的完整应用配置。
|
|
1169
|
+
return action === "create"
|
|
1170
|
+
? { ...payload, name, icon, description: description ?? "", iconColor: payload.iconColor ?? "#EBECF0", manageUserIds: payload.manageUserIds ?? [] }
|
|
1171
|
+
: { ...payload, name, icon };
|
|
1172
|
+
}
|
|
1173
|
+
function validateAggregateSavePayload(value, boName, listField) {
|
|
1174
|
+
const payload = safePayload(value);
|
|
1175
|
+
requiredId(payload.moduleId, "payload.moduleId");
|
|
1176
|
+
// 新建模块的有效版本通常为 0;不能用 requiredVersion 的默认值掩盖调用方漏传版本。
|
|
1177
|
+
if (payload.version === undefined) {
|
|
1178
|
+
throw new Error(`${boName} 必须包含实际 version;请先读取模块详情。`);
|
|
1179
|
+
}
|
|
1180
|
+
requiredVersion(payload.version);
|
|
1181
|
+
if (!Array.isArray(payload[listField]) || payload[listField].length === 0) {
|
|
1182
|
+
throw new Error(`${boName} 必须包含非空 ${listField}。`);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1078
1185
|
function cleanObject(input) {
|
|
1079
1186
|
const output = {};
|
|
1080
1187
|
for (const [key, value] of Object.entries(input)) {
|
package/dist/server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { CrmClient } from "./client.js";
|
|
3
4
|
import { createNocodeToolHandlers, nocodeToolDefinitions } from "./nocode.js";
|
|
4
5
|
import { createToolHandlers } from "./tools.js";
|
|
5
6
|
export const CRM_READ_SCOPE = "crm.read";
|
|
@@ -181,13 +182,14 @@ export function createWukongMcpServer(config = {}) {
|
|
|
181
182
|
const { oauth, ...crmConfig } = config;
|
|
182
183
|
const server = new McpServer({
|
|
183
184
|
name: "wukong-mcp",
|
|
184
|
-
version: "0.2.
|
|
185
|
+
version: "0.2.2"
|
|
185
186
|
}, {
|
|
186
187
|
instructions: "先使用只读工具确认模块、记录和字段,再执行写入。所有写入必须显式传 confirm=true;不要尝试任意 URL 请求。"
|
|
187
188
|
});
|
|
189
|
+
const sharedClient = new CrmClient(crmConfig);
|
|
188
190
|
const handlers = {
|
|
189
|
-
...createToolHandlers(crmConfig),
|
|
190
|
-
...createNocodeToolHandlers(crmConfig)
|
|
191
|
+
...createToolHandlers(crmConfig, sharedClient),
|
|
192
|
+
...createNocodeToolHandlers(crmConfig, sharedClient)
|
|
191
193
|
};
|
|
192
194
|
const registeredTools = [
|
|
193
195
|
...toolDefinitions.map(([name, description, schema]) => ({
|
package/dist/tools.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { CrmClientConfig } from "./client.js";
|
|
1
|
+
import { CrmClient, CrmClientConfig } from "./client.js";
|
|
2
2
|
type AnyArgs = Record<string, any>;
|
|
3
3
|
type Handler = (args: AnyArgs) => Promise<any> | any;
|
|
4
4
|
export interface ToolHandlers {
|
|
5
5
|
[name: string]: Handler;
|
|
6
6
|
}
|
|
7
|
-
export declare function createToolHandlers(config?: CrmClientConfig): ToolHandlers;
|
|
7
|
+
export declare function createToolHandlers(config?: CrmClientConfig, sharedClient?: CrmClient): ToolHandlers;
|
|
8
8
|
export {};
|
package/dist/tools.js
CHANGED
|
@@ -332,8 +332,8 @@ const WRITE_DOMAINS = {
|
|
|
332
332
|
allowedPrefixes: ["/finance"]
|
|
333
333
|
}
|
|
334
334
|
};
|
|
335
|
-
export function createToolHandlers(config = {}) {
|
|
336
|
-
const client = new CrmClient(config);
|
|
335
|
+
export function createToolHandlers(config = {}, sharedClient) {
|
|
336
|
+
const client = sharedClient ?? new CrmClient(config);
|
|
337
337
|
return {
|
|
338
338
|
crm_auth_status: async () => client.authStatus(),
|
|
339
339
|
crm_list_modules: () => ({
|