mes-mcp 0.1.0 → 0.2.0
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 +14 -18
- package/dist/clients/mes-agent-api.client.js +11 -92
- package/dist/index.js +3 -3
- package/dist/prompts/register-prompts.js +3 -2
- package/dist/resources/register-resources.js +5 -5
- package/dist/tools/register-tools.js +157 -16
- package/package.json +2 -2
- package/dist/tools/agent-tools.js +0 -209
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MES MCP
|
|
2
2
|
|
|
3
|
-
MES MCP 是 MES 开放集成体系的 MCP 协议适配层,用于 Marvis 或其他 AI Agent 调用 MES
|
|
3
|
+
MES MCP 是 MES 开放集成体系的 MCP 协议适配层,用于 Marvis 或其他 AI Agent 调用 MES 的后端动态业务动作目录。
|
|
4
4
|
|
|
5
5
|
## 边界
|
|
6
6
|
|
|
@@ -30,26 +30,22 @@ npm run dev
|
|
|
30
30
|
|
|
31
31
|
## 工具
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- `
|
|
36
|
-
- `
|
|
37
|
-
- `
|
|
38
|
-
- `sale_order.create
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- `
|
|
43
|
-
- `sale_order.query`
|
|
44
|
-
- `work_order.query`
|
|
45
|
-
- `inventory.query`
|
|
46
|
-
- `mes_api.read`:按当前 MES 账号权限读取普通业务接口,例如 `/sale-order/list`、`/material/detail`。后端只放行只读类接口,写入动作必须使用专门 Agent 工具;读取调用会在 MES AI 调用日志里记录路径、请求摘要和结果摘要。
|
|
33
|
+
使用后端动态业务动作:
|
|
34
|
+
|
|
35
|
+
- `mes_action.list`:查询当前凭证和当前账号权限允许调用的 MES 页面业务动作。
|
|
36
|
+
- `mes_action.detail`:查看某个业务动作的路径、权限、DTO 名称和输入 schema。
|
|
37
|
+
- `mes_action.execute`:按动作编码执行业务动作。
|
|
38
|
+
- `mes.<动作编码>`:启动时从 `mes-node` 自动发现并注册的动态工具,例如 `mes.sale_order.create`、`mes.approval.handle`、`mes.quality_inspection.submit`、`mes.sale_delivery.confirm`。
|
|
39
|
+
|
|
40
|
+
动态工具只调用 `mes-node` 的 `/integration/agent/actions/*` 接口。MCP 不保存字段 schema,不复制状态机,不做业务默认值;后端会复用页面 Controller、DTO、权限、租户、状态、审批、库存、质量和 AI 调用日志。
|
|
41
|
+
|
|
42
|
+
- `mes_api.read`:按当前 MES 账号权限读取普通业务接口,例如 `/sale-order/list`、`/material/detail`。后端只放行只读类接口,写入动作必须使用动态业务动作;读取调用会在 MES AI 调用日志里记录路径、请求摘要和结果摘要。
|
|
47
43
|
|
|
48
44
|
写入类工具支持 `executionMode`:
|
|
49
45
|
|
|
50
46
|
- `preview`:只预览,不写入。
|
|
51
|
-
- `confirm_required
|
|
52
|
-
- `commit`:提交到 MES
|
|
47
|
+
- `confirm_required`:生成待确认单,不写入;必须传 `idempotencyKey`。
|
|
48
|
+
- `commit`:提交到 MES,按当前账号权限、工具白名单、业务规则和幂等键执行;必须传 `idempotencyKey`。
|
|
53
49
|
|
|
54
50
|
## Marvis 接入建议
|
|
55
51
|
|
|
@@ -57,4 +53,4 @@ npm run dev
|
|
|
57
53
|
2. `agentClientId` 建议填写 `marvis` 或具体渠道名。
|
|
58
54
|
3. 授权范围建议先选“跟随账号权限”,最高执行模式建议先用 `confirm_required`;如果只想开放少量动作,再切换为“仅允许指定工具”。只读型 AI 凭证可以只选择 `mes_api.read`。
|
|
59
55
|
4. 将返回的 JWT 配置为 `MES_AGENT_TOKEN`。
|
|
60
|
-
5. Marvis 侧通过 MCP 调用工具;图片、PDF、Excel 可先由 Marvis
|
|
56
|
+
5. Marvis 侧通过 MCP 调用工具;图片、PDF、Excel 可先由 Marvis 识别成结构化草稿,再用 `mes_api.read` 或动态列表动作查询客户、物料等主数据,最后通过 `mes.<动作编码>` 或 `mes_action.execute` 执行创建、审批、确认、排产、质检、出库等页面业务动作。
|
|
@@ -1,81 +1,5 @@
|
|
|
1
1
|
export class MesAgentApiClient {
|
|
2
2
|
config;
|
|
3
|
-
static WRITE_PATH_TOKENS = new Set([
|
|
4
|
-
"create",
|
|
5
|
-
"update",
|
|
6
|
-
"delete",
|
|
7
|
-
"remove",
|
|
8
|
-
"revoke",
|
|
9
|
-
"toggle",
|
|
10
|
-
"confirm",
|
|
11
|
-
"cancel",
|
|
12
|
-
"approve",
|
|
13
|
-
"reject",
|
|
14
|
-
"release",
|
|
15
|
-
"start",
|
|
16
|
-
"complete",
|
|
17
|
-
"close",
|
|
18
|
-
"submit",
|
|
19
|
-
"import",
|
|
20
|
-
"run",
|
|
21
|
-
"cleanup",
|
|
22
|
-
"rotate",
|
|
23
|
-
"test",
|
|
24
|
-
"retry",
|
|
25
|
-
"allocate",
|
|
26
|
-
"unallocate",
|
|
27
|
-
"reverse",
|
|
28
|
-
"convert",
|
|
29
|
-
"send",
|
|
30
|
-
"recall",
|
|
31
|
-
"scrap",
|
|
32
|
-
"activate",
|
|
33
|
-
"deprecate",
|
|
34
|
-
"purge",
|
|
35
|
-
"adjust",
|
|
36
|
-
"freeze",
|
|
37
|
-
"end",
|
|
38
|
-
"configure",
|
|
39
|
-
"generate",
|
|
40
|
-
"save",
|
|
41
|
-
"copy",
|
|
42
|
-
"archive",
|
|
43
|
-
"publish",
|
|
44
|
-
"restore",
|
|
45
|
-
"execute",
|
|
46
|
-
"handle",
|
|
47
|
-
"verify",
|
|
48
|
-
"pass",
|
|
49
|
-
"fail",
|
|
50
|
-
"lock",
|
|
51
|
-
"unlock",
|
|
52
|
-
"enable",
|
|
53
|
-
"disable",
|
|
54
|
-
"bind",
|
|
55
|
-
"unbind",
|
|
56
|
-
"link",
|
|
57
|
-
"unlink",
|
|
58
|
-
"upload",
|
|
59
|
-
"assign",
|
|
60
|
-
"claim",
|
|
61
|
-
"dispatch",
|
|
62
|
-
"receive",
|
|
63
|
-
"issue",
|
|
64
|
-
"return",
|
|
65
|
-
"split",
|
|
66
|
-
"mark",
|
|
67
|
-
"change",
|
|
68
|
-
"transfer",
|
|
69
|
-
"in",
|
|
70
|
-
"out",
|
|
71
|
-
]);
|
|
72
|
-
static WRITE_PATH_SEGMENTS = new Set([
|
|
73
|
-
"report",
|
|
74
|
-
"quick-report",
|
|
75
|
-
"report-operation",
|
|
76
|
-
"report-direct",
|
|
77
|
-
"batch-report",
|
|
78
|
-
]);
|
|
79
3
|
constructor(config) {
|
|
80
4
|
this.config = config;
|
|
81
5
|
}
|
|
@@ -115,25 +39,20 @@ export class MesAgentApiClient {
|
|
|
115
39
|
if (/^\/integration\/agent(\/|$)/i.test(normalized)) {
|
|
116
40
|
throw new Error("Use dedicated Agent tools for /integration/agent paths");
|
|
117
41
|
}
|
|
118
|
-
if (this.isWriteLikePath(normalized)) {
|
|
119
|
-
throw new Error("mes_api.read only supports read-only MES API paths");
|
|
120
|
-
}
|
|
121
42
|
return this.post(normalized, payload);
|
|
122
43
|
}
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
.
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
44
|
+
async listBusinessActions() {
|
|
45
|
+
const pageSize = 100;
|
|
46
|
+
const firstPage = await this.post("/integration/agent/actions/list", { page: 1, pageSize });
|
|
47
|
+
if (firstPage.total <= firstPage.list.length)
|
|
48
|
+
return firstPage.list;
|
|
49
|
+
const actions = [...firstPage.list];
|
|
50
|
+
const pageCount = Math.ceil(firstPage.total / pageSize);
|
|
51
|
+
for (let page = 2; page <= pageCount; page += 1) {
|
|
52
|
+
const nextPage = await this.post("/integration/agent/actions/list", { page, pageSize });
|
|
53
|
+
actions.push(...nextPage.list);
|
|
132
54
|
}
|
|
133
|
-
return
|
|
134
|
-
.split(/[._-]+/)
|
|
135
|
-
.filter(Boolean)
|
|
136
|
-
.some((token) => MesAgentApiClient.WRITE_PATH_TOKENS.has(token));
|
|
55
|
+
return actions;
|
|
137
56
|
}
|
|
138
57
|
buildUrl(path) {
|
|
139
58
|
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
package/dist/index.js
CHANGED
|
@@ -5,15 +5,15 @@ import { loadConfig } from "./config.js";
|
|
|
5
5
|
import { MesAgentApiClient } from "./clients/mes-agent-api.client.js";
|
|
6
6
|
import { registerMesPrompts } from "./prompts/register-prompts.js";
|
|
7
7
|
import { registerMesResources } from "./resources/register-resources.js";
|
|
8
|
-
import {
|
|
8
|
+
import { registerAllAgentTools } from "./tools/register-tools.js";
|
|
9
9
|
async function main() {
|
|
10
10
|
const config = loadConfig();
|
|
11
11
|
const client = new MesAgentApiClient(config);
|
|
12
12
|
const server = new McpServer({
|
|
13
13
|
name: "mes-mcp",
|
|
14
|
-
version: "0.
|
|
14
|
+
version: "0.2.0",
|
|
15
15
|
});
|
|
16
|
-
|
|
16
|
+
await registerAllAgentTools(server, client);
|
|
17
17
|
registerMesResources(server, client);
|
|
18
18
|
registerMesPrompts(server);
|
|
19
19
|
await server.connect(new StdioServerTransport());
|
|
@@ -15,8 +15,9 @@ export function registerMesPrompts(server) {
|
|
|
15
15
|
text: [
|
|
16
16
|
"你是 MES 业务助手,必须按当前账号权限和工具返回结果执行。",
|
|
17
17
|
"不要猜测客户、物料、BOM、工艺路线、仓库等关键主数据;缺失时先查询或要求用户确认。",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
18
|
+
"先用 mes_action.list 或 mes://agent/actions 发现当前账号能做的页面业务动作;发现到动态工具 mes.<动作编码> 时,直接调用对应工具。",
|
|
19
|
+
"销售到出库的流程也走动态动作:先查客户和物料,再创建销售单、处理审批、确认销售单、转生产计划、排产或生成工单、报工、质检、发货出库。",
|
|
20
|
+
"写入类动作默认先使用 confirm_required 或 preview;confirm_required 和 commit 都必须传 idempotencyKey,preview 不写入。",
|
|
20
21
|
context ? `当前上下文:${context}` : "",
|
|
21
22
|
]
|
|
22
23
|
.filter(Boolean)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export function registerMesResources(server, client) {
|
|
2
|
-
server.registerResource("
|
|
3
|
-
title: "MES
|
|
4
|
-
description: "当前 AI
|
|
2
|
+
server.registerResource("mes_business_actions", "mes://agent/actions", {
|
|
3
|
+
title: "MES Business Actions",
|
|
4
|
+
description: "当前 AI 凭证和账号权限可调用的 MES 页面业务动作目录,来源于 mes-node 后端 Controller、权限和 DTO 元数据。",
|
|
5
5
|
mimeType: "application/json",
|
|
6
6
|
}, async (uri) => {
|
|
7
|
-
const
|
|
7
|
+
const actions = await client.post("/integration/agent/actions/list", {
|
|
8
8
|
page: 1,
|
|
9
9
|
pageSize: 100,
|
|
10
10
|
});
|
|
@@ -13,7 +13,7 @@ export function registerMesResources(server, client) {
|
|
|
13
13
|
{
|
|
14
14
|
uri: uri.href,
|
|
15
15
|
mimeType: "application/json",
|
|
16
|
-
text: JSON.stringify(
|
|
16
|
+
text: JSON.stringify(actions, null, 2),
|
|
17
17
|
},
|
|
18
18
|
],
|
|
19
19
|
};
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
|
|
2
|
+
const ExecutionModeSchema = z
|
|
3
|
+
.enum(["preview", "confirm_required", "commit"])
|
|
4
|
+
.optional();
|
|
5
|
+
const DynamicActionSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
executionMode: ExecutionModeSchema.describe("写入动作执行模式;只读动作固定按 preview 执行。"),
|
|
8
|
+
idempotencyKey: z
|
|
9
|
+
.string()
|
|
10
|
+
.min(1)
|
|
11
|
+
.max(160)
|
|
12
|
+
.optional()
|
|
13
|
+
.describe("写入动作 confirm_required 或 commit 时必填,用于后端幂等。"),
|
|
14
|
+
payload: z
|
|
15
|
+
.record(z.string(), z.unknown())
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("MES 页面业务动作请求体;也可以直接把 DTO 字段放在顶层。"),
|
|
18
|
+
})
|
|
19
|
+
.passthrough();
|
|
3
20
|
function formatToolResult(result) {
|
|
4
21
|
return {
|
|
5
22
|
structuredContent: result && typeof result === "object" && !Array.isArray(result)
|
|
@@ -14,21 +31,6 @@ function formatToolResult(result) {
|
|
|
14
31
|
};
|
|
15
32
|
}
|
|
16
33
|
export function registerAgentTools(server, client) {
|
|
17
|
-
for (const spec of AGENT_TOOL_SPECS) {
|
|
18
|
-
server.registerTool(spec.name, {
|
|
19
|
-
title: spec.title,
|
|
20
|
-
description: spec.description,
|
|
21
|
-
inputSchema: spec.schema,
|
|
22
|
-
annotations: {
|
|
23
|
-
readOnlyHint: spec.readOnlyHint ?? false,
|
|
24
|
-
destructiveHint: spec.destructiveHint ?? false,
|
|
25
|
-
idempotentHint: true,
|
|
26
|
-
},
|
|
27
|
-
}, async (args) => {
|
|
28
|
-
const result = await client.post(spec.path, args ?? {});
|
|
29
|
-
return formatToolResult(result);
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
34
|
server.registerTool("mes_api.read", {
|
|
33
35
|
title: "Read MES API",
|
|
34
36
|
description: "按当前 MES 账号权限读取系统内业务信息。仅用于 list/detail/view/read/export/stats/options 等只读类 POST 接口;创建、更新、删除、确认、下达等写入动作必须使用专门的 Agent 工具。",
|
|
@@ -60,3 +62,142 @@ export function registerAgentTools(server, client) {
|
|
|
60
62
|
return formatToolResult(result);
|
|
61
63
|
});
|
|
62
64
|
}
|
|
65
|
+
function normalizeDynamicArgs(args) {
|
|
66
|
+
const parsed = DynamicActionSchema.parse(args ?? {});
|
|
67
|
+
const { executionMode, idempotencyKey, payload, ...rest } = parsed;
|
|
68
|
+
return {
|
|
69
|
+
executionMode,
|
|
70
|
+
idempotencyKey,
|
|
71
|
+
payload: payload ?? rest,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function actionDescription(action) {
|
|
75
|
+
const permissionText = action.permissions.length > 0
|
|
76
|
+
? `需要权限:${action.permissions.join("、")}。`
|
|
77
|
+
: "按后端业务方法自行校验权限。";
|
|
78
|
+
const modeText = action.isWrite
|
|
79
|
+
? "写入动作,confirm_required 会生成待确认单,commit 会真实写入 MES;两种模式都必须提供 idempotencyKey,preview 不写入。"
|
|
80
|
+
: "只读动作,固定按 preview 执行。";
|
|
81
|
+
return [
|
|
82
|
+
action.description,
|
|
83
|
+
`页面接口:POST ${action.path}。`,
|
|
84
|
+
action.inputDto ? `请求 DTO:${action.inputDto}。` : undefined,
|
|
85
|
+
permissionText,
|
|
86
|
+
modeText,
|
|
87
|
+
]
|
|
88
|
+
.filter(Boolean)
|
|
89
|
+
.join("\n");
|
|
90
|
+
}
|
|
91
|
+
function registerGenericActionTools(server, client) {
|
|
92
|
+
server.registerTool("mes_action.list", {
|
|
93
|
+
title: "List MES business actions",
|
|
94
|
+
description: "动态查询当前 AI 凭证和当前账号权限允许调用的 MES 页面业务动作。新业务页面只要后端有权限和 DTO 元数据,就会出现在这里。",
|
|
95
|
+
inputSchema: z
|
|
96
|
+
.object({
|
|
97
|
+
page: z.number().int().positive().optional(),
|
|
98
|
+
pageSize: z.number().int().positive().max(100).optional(),
|
|
99
|
+
keyword: z.string().optional(),
|
|
100
|
+
module: z.string().optional(),
|
|
101
|
+
resource: z.string().optional(),
|
|
102
|
+
operation: z.string().optional(),
|
|
103
|
+
write: z.boolean().optional(),
|
|
104
|
+
})
|
|
105
|
+
.passthrough(),
|
|
106
|
+
annotations: {
|
|
107
|
+
readOnlyHint: true,
|
|
108
|
+
destructiveHint: false,
|
|
109
|
+
idempotentHint: true,
|
|
110
|
+
},
|
|
111
|
+
}, async (args) => {
|
|
112
|
+
const result = await client.post("/integration/agent/actions/list", args ?? {});
|
|
113
|
+
return formatToolResult(result);
|
|
114
|
+
});
|
|
115
|
+
server.registerTool("mes_action.detail", {
|
|
116
|
+
title: "Get MES business action detail",
|
|
117
|
+
description: "查询单个 MES 页面业务动作的路径、权限、DTO 名称和后端提供的输入 schema。",
|
|
118
|
+
inputSchema: z.object({
|
|
119
|
+
actionCode: z.string().min(1),
|
|
120
|
+
}),
|
|
121
|
+
annotations: {
|
|
122
|
+
readOnlyHint: true,
|
|
123
|
+
destructiveHint: false,
|
|
124
|
+
idempotentHint: true,
|
|
125
|
+
},
|
|
126
|
+
}, async (args) => {
|
|
127
|
+
const result = await client.post("/integration/agent/actions/detail", args ?? {});
|
|
128
|
+
return formatToolResult(result);
|
|
129
|
+
});
|
|
130
|
+
server.registerTool("mes_action.execute", {
|
|
131
|
+
title: "Execute MES business action",
|
|
132
|
+
description: "执行一个由 mes_action.list 发现的 MES 页面业务动作。后端复用页面 Controller、DTO、权限、租户、状态机和调用日志。",
|
|
133
|
+
inputSchema: z
|
|
134
|
+
.object({
|
|
135
|
+
actionCode: z.string().min(1),
|
|
136
|
+
executionMode: ExecutionModeSchema,
|
|
137
|
+
idempotencyKey: z.string().min(1).max(160).optional(),
|
|
138
|
+
payload: z.record(z.string(), z.unknown()).optional(),
|
|
139
|
+
})
|
|
140
|
+
.passthrough(),
|
|
141
|
+
annotations: {
|
|
142
|
+
readOnlyHint: false,
|
|
143
|
+
destructiveHint: true,
|
|
144
|
+
idempotentHint: true,
|
|
145
|
+
},
|
|
146
|
+
}, async (args) => {
|
|
147
|
+
const parsed = z
|
|
148
|
+
.object({
|
|
149
|
+
actionCode: z.string().min(1),
|
|
150
|
+
executionMode: ExecutionModeSchema,
|
|
151
|
+
idempotencyKey: z.string().min(1).max(160).optional(),
|
|
152
|
+
payload: z.record(z.string(), z.unknown()).optional(),
|
|
153
|
+
})
|
|
154
|
+
.passthrough()
|
|
155
|
+
.parse(args ?? {});
|
|
156
|
+
const { actionCode, executionMode, idempotencyKey, payload, ...rest } = parsed;
|
|
157
|
+
const result = await client.post("/integration/agent/actions/execute", {
|
|
158
|
+
actionCode,
|
|
159
|
+
executionMode,
|
|
160
|
+
idempotencyKey,
|
|
161
|
+
payload: payload ?? rest,
|
|
162
|
+
});
|
|
163
|
+
return formatToolResult(result);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
async function registerDynamicBusinessActionTools(server, client) {
|
|
167
|
+
const actions = await client.listBusinessActions();
|
|
168
|
+
const registeredNames = new Set();
|
|
169
|
+
for (const action of actions) {
|
|
170
|
+
const toolName = `mes.${action.actionCode}`;
|
|
171
|
+
if (registeredNames.has(toolName))
|
|
172
|
+
continue;
|
|
173
|
+
registeredNames.add(toolName);
|
|
174
|
+
server.registerTool(toolName, {
|
|
175
|
+
title: action.title,
|
|
176
|
+
description: actionDescription(action),
|
|
177
|
+
inputSchema: DynamicActionSchema,
|
|
178
|
+
annotations: {
|
|
179
|
+
readOnlyHint: !action.isWrite,
|
|
180
|
+
destructiveHint: action.isWrite,
|
|
181
|
+
idempotentHint: true,
|
|
182
|
+
},
|
|
183
|
+
}, async (args) => {
|
|
184
|
+
const normalized = normalizeDynamicArgs(args);
|
|
185
|
+
const result = await client.post("/integration/agent/actions/execute", {
|
|
186
|
+
actionCode: action.actionCode,
|
|
187
|
+
...normalized,
|
|
188
|
+
});
|
|
189
|
+
return formatToolResult(result);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
export async function registerAllAgentTools(server, client) {
|
|
194
|
+
registerAgentTools(server, client);
|
|
195
|
+
registerGenericActionTools(server, client);
|
|
196
|
+
try {
|
|
197
|
+
await registerDynamicBusinessActionTools(server, client);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
201
|
+
console.error(`MES dynamic business actions unavailable: ${message}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mes-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MES MCP adapter for Marvis and AI agents",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
".env.example"
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
|
-
"build": "tsc -p tsconfig.json",
|
|
17
|
+
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
18
18
|
"dev": "tsx src/index.ts",
|
|
19
19
|
"prepublishOnly": "npm run build",
|
|
20
20
|
"start": "node dist/index.js"
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
const ExecutionModeSchema = z
|
|
3
|
-
.enum(["preview", "confirm_required", "commit"])
|
|
4
|
-
.optional();
|
|
5
|
-
const IdempotencySchema = z.string().min(1).max(160).optional();
|
|
6
|
-
const QuerySchema = z
|
|
7
|
-
.object({
|
|
8
|
-
page: z.number().int().positive().optional(),
|
|
9
|
-
pageSize: z.number().int().positive().max(100).optional(),
|
|
10
|
-
keyword: z.string().optional(),
|
|
11
|
-
})
|
|
12
|
-
.passthrough();
|
|
13
|
-
export const AGENT_TOOL_SPECS = [
|
|
14
|
-
{
|
|
15
|
-
name: "document_intake.parse",
|
|
16
|
-
title: "Parse order document",
|
|
17
|
-
description: "接收 Marvis/上游 AI 从图片、PDF、Excel、文本中识别出的订单资料,返回可确认的销售订单草稿。",
|
|
18
|
-
path: "/integration/agent/document-intake/parse",
|
|
19
|
-
readOnlyHint: true,
|
|
20
|
-
schema: z
|
|
21
|
-
.object({
|
|
22
|
-
executionMode: ExecutionModeSchema,
|
|
23
|
-
idempotencyKey: IdempotencySchema,
|
|
24
|
-
rawText: z.string().optional(),
|
|
25
|
-
imageUrl: z.string().url().optional(),
|
|
26
|
-
fileUrl: z.string().url().optional(),
|
|
27
|
-
extractedOrder: z
|
|
28
|
-
.object({
|
|
29
|
-
orderNo: z.string().optional(),
|
|
30
|
-
customerId: z.string().uuid().optional(),
|
|
31
|
-
customerName: z.string().optional(),
|
|
32
|
-
deliveryDate: z.string().optional(),
|
|
33
|
-
remarks: z.string().optional(),
|
|
34
|
-
items: z
|
|
35
|
-
.array(z
|
|
36
|
-
.object({
|
|
37
|
-
materialId: z.string().uuid().optional(),
|
|
38
|
-
materialCode: z.string().optional(),
|
|
39
|
-
materialName: z.string().optional(),
|
|
40
|
-
quantity: z.number().positive().optional(),
|
|
41
|
-
unitPrice: z.number().min(0).optional(),
|
|
42
|
-
remarks: z.string().optional(),
|
|
43
|
-
})
|
|
44
|
-
.passthrough())
|
|
45
|
-
.optional(),
|
|
46
|
-
})
|
|
47
|
-
.passthrough()
|
|
48
|
-
.optional(),
|
|
49
|
-
sourceMeta: z.record(z.string(), z.unknown()).optional(),
|
|
50
|
-
})
|
|
51
|
-
.passthrough(),
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
name: "customer.match",
|
|
55
|
-
title: "Match customer",
|
|
56
|
-
description: "按当前 MES 账号权限匹配启用中的客户主数据;创建销售单前用于确认客户 ID,避免 AI 直接猜测客户。",
|
|
57
|
-
path: "/integration/agent/customer/match",
|
|
58
|
-
readOnlyHint: true,
|
|
59
|
-
schema: QuerySchema.extend({
|
|
60
|
-
customerCode: z.string().optional(),
|
|
61
|
-
name: z.string().optional(),
|
|
62
|
-
contactName: z.string().optional(),
|
|
63
|
-
contactPhone: z.string().optional(),
|
|
64
|
-
}).passthrough(),
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
name: "material.match",
|
|
68
|
-
title: "Match material",
|
|
69
|
-
description: "按当前 MES 账号权限匹配启用中的物料主数据;创建销售单前用于确认物料 ID,避免用图片识别文本直接写入。",
|
|
70
|
-
path: "/integration/agent/material/match",
|
|
71
|
-
readOnlyHint: true,
|
|
72
|
-
schema: QuerySchema.extend({
|
|
73
|
-
code: z.string().optional(),
|
|
74
|
-
name: z.string().optional(),
|
|
75
|
-
spec: z.string().optional(),
|
|
76
|
-
barcode: z.string().optional(),
|
|
77
|
-
}).passthrough(),
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
name: "sale_order.create",
|
|
81
|
-
title: "Create sale order",
|
|
82
|
-
description: "按当前 MES 账号权限创建销售订单;commit 才写入,preview/confirm_required 只返回待确认内容。",
|
|
83
|
-
path: "/integration/agent/sale-order/create",
|
|
84
|
-
destructiveHint: true,
|
|
85
|
-
schema: z
|
|
86
|
-
.object({
|
|
87
|
-
executionMode: ExecutionModeSchema,
|
|
88
|
-
idempotencyKey: IdempotencySchema,
|
|
89
|
-
orderNo: z.string().optional(),
|
|
90
|
-
customerId: z.string().uuid().optional(),
|
|
91
|
-
salesRepId: z.string().uuid().optional(),
|
|
92
|
-
deliveryDate: z.string().optional(),
|
|
93
|
-
paymentTerms: z.string().optional(),
|
|
94
|
-
remarks: z.string().optional(),
|
|
95
|
-
items: z.array(z
|
|
96
|
-
.object({
|
|
97
|
-
materialId: z.string().uuid(),
|
|
98
|
-
quantity: z.number().positive(),
|
|
99
|
-
unitPrice: z.number().min(0).optional(),
|
|
100
|
-
customerMaterialCode: z.string().optional(),
|
|
101
|
-
customerMaterialName: z.string().optional(),
|
|
102
|
-
customerMaterialSpec: z.string().optional(),
|
|
103
|
-
remarks: z.string().optional(),
|
|
104
|
-
})
|
|
105
|
-
.passthrough()),
|
|
106
|
-
})
|
|
107
|
-
.passthrough(),
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
name: "sale_order.confirm",
|
|
111
|
-
title: "Confirm sale order",
|
|
112
|
-
description: "确认销售订单,复用 MES 的审批门控、状态规则和明细校验;销售订单转生产计划前必须先确认。",
|
|
113
|
-
path: "/integration/agent/sale-order/confirm",
|
|
114
|
-
destructiveHint: true,
|
|
115
|
-
schema: z
|
|
116
|
-
.object({
|
|
117
|
-
executionMode: ExecutionModeSchema,
|
|
118
|
-
idempotencyKey: IdempotencySchema,
|
|
119
|
-
id: z.string().uuid(),
|
|
120
|
-
})
|
|
121
|
-
.passthrough(),
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
name: "production_plan.create_from_sale_order",
|
|
125
|
-
title: "Create production plan from sale order",
|
|
126
|
-
description: "将已确认销售订单转为生产计划,复用 MES 原有数量、BOM 和工艺校验。",
|
|
127
|
-
path: "/integration/agent/production-plan/create-from-sale-order",
|
|
128
|
-
destructiveHint: true,
|
|
129
|
-
schema: z
|
|
130
|
-
.object({
|
|
131
|
-
executionMode: ExecutionModeSchema,
|
|
132
|
-
idempotencyKey: IdempotencySchema,
|
|
133
|
-
saleOrderId: z.string().uuid(),
|
|
134
|
-
planStartDate: z.string(),
|
|
135
|
-
planEndDate: z.string(),
|
|
136
|
-
priority: z.number().int().min(1).max(10).optional(),
|
|
137
|
-
description: z.string().optional(),
|
|
138
|
-
items: z.array(z
|
|
139
|
-
.object({
|
|
140
|
-
saleOrderItemId: z.string().uuid(),
|
|
141
|
-
requiredQty: z.number().positive().optional(),
|
|
142
|
-
plannedLossRate: z.number().min(0).optional(),
|
|
143
|
-
plannedQty: z.number().positive().optional(),
|
|
144
|
-
bomHeaderId: z.string().uuid().optional(),
|
|
145
|
-
routeId: z.string().uuid().optional(),
|
|
146
|
-
})
|
|
147
|
-
.passthrough()),
|
|
148
|
-
})
|
|
149
|
-
.passthrough(),
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
name: "work_order.create_from_plan",
|
|
153
|
-
title: "Create work order from production plan",
|
|
154
|
-
description: "确认生产计划并自动创建工单,复用 MES 的 BOM 展开和工艺工序生成规则。",
|
|
155
|
-
path: "/integration/agent/work-order/create-from-plan",
|
|
156
|
-
destructiveHint: true,
|
|
157
|
-
schema: z
|
|
158
|
-
.object({
|
|
159
|
-
executionMode: ExecutionModeSchema,
|
|
160
|
-
idempotencyKey: IdempotencySchema,
|
|
161
|
-
planId: z.string().uuid(),
|
|
162
|
-
routeId: z.string().uuid().optional(),
|
|
163
|
-
bomHeaderId: z.string().uuid().optional(),
|
|
164
|
-
assigneeId: z.string().uuid().optional(),
|
|
165
|
-
receiptWarehouseId: z.string().uuid().optional(),
|
|
166
|
-
})
|
|
167
|
-
.passthrough(),
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
name: "work_order.release",
|
|
171
|
-
title: "Release work order",
|
|
172
|
-
description: "下达工单,复用 MES 状态机、审批门控、物料需求展开和事件推送。",
|
|
173
|
-
path: "/integration/agent/work-order/release",
|
|
174
|
-
destructiveHint: true,
|
|
175
|
-
schema: z
|
|
176
|
-
.object({
|
|
177
|
-
executionMode: ExecutionModeSchema,
|
|
178
|
-
idempotencyKey: IdempotencySchema,
|
|
179
|
-
id: z.string().uuid(),
|
|
180
|
-
assigneeId: z.string().uuid().optional(),
|
|
181
|
-
receiptWarehouseId: z.string().uuid().optional(),
|
|
182
|
-
})
|
|
183
|
-
.passthrough(),
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
name: "sale_order.query",
|
|
187
|
-
title: "Query sale orders",
|
|
188
|
-
description: "按当前 MES 账号权限查询销售订单列表。",
|
|
189
|
-
path: "/integration/agent/sale-order/query",
|
|
190
|
-
readOnlyHint: true,
|
|
191
|
-
schema: QuerySchema,
|
|
192
|
-
},
|
|
193
|
-
{
|
|
194
|
-
name: "work_order.query",
|
|
195
|
-
title: "Query work orders",
|
|
196
|
-
description: "按当前 MES 账号权限查询工单列表。",
|
|
197
|
-
path: "/integration/agent/work-order/query",
|
|
198
|
-
readOnlyHint: true,
|
|
199
|
-
schema: QuerySchema,
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
name: "inventory.query",
|
|
203
|
-
title: "Query inventory",
|
|
204
|
-
description: "按当前 MES 账号权限查询库存现有量和可用量。",
|
|
205
|
-
path: "/integration/agent/inventory/query",
|
|
206
|
-
readOnlyHint: true,
|
|
207
|
-
schema: QuerySchema,
|
|
208
|
-
},
|
|
209
|
-
];
|