qingflow-mcp 0.3.18 → 0.3.20
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 +63 -1
- package/dist/qingflow-client.js +36 -0
- package/dist/server.js +871 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
This MCP server wraps Qingflow OpenAPI for:
|
|
4
4
|
|
|
5
5
|
- `qf_apps_list`
|
|
6
|
+
- `qf_apps_info_list`
|
|
7
|
+
- `qf_app_info_get`
|
|
8
|
+
- `qf_app_packages_list`
|
|
6
9
|
- `qf_departments_list`
|
|
7
10
|
- `qf_department_users_list`
|
|
8
11
|
- `qf_users_list`
|
|
@@ -17,6 +20,8 @@ This MCP server wraps Qingflow OpenAPI for:
|
|
|
17
20
|
- `qf_export_json`
|
|
18
21
|
- `qf_query` (unified read entry: list / record / summary)
|
|
19
22
|
- `qf_records_aggregate` (deterministic grouped metrics)
|
|
23
|
+
- `qf_apply_audit_records_list`
|
|
24
|
+
- `qf_apply_audit_record_get`
|
|
20
25
|
- `qf_record_create`
|
|
21
26
|
- `qf_record_update`
|
|
22
27
|
- `qf_operation_get`
|
|
@@ -112,7 +117,7 @@ npm i -g git+https://github.com/853046310/qingflow-mcp.git
|
|
|
112
117
|
Install from npm (pinned version):
|
|
113
118
|
|
|
114
119
|
```bash
|
|
115
|
-
npm i -g qingflow-mcp@0.3.
|
|
120
|
+
npm i -g qingflow-mcp@0.3.20
|
|
116
121
|
```
|
|
117
122
|
|
|
118
123
|
Or one-click installer:
|
|
@@ -159,6 +164,18 @@ Directory / org flow:
|
|
|
159
164
|
3. `qf_users_list` for workspace-wide pagination.
|
|
160
165
|
4. `qf_user_get` for one exact user.
|
|
161
166
|
|
|
167
|
+
Admin app flow:
|
|
168
|
+
|
|
169
|
+
1. `qf_apps_list` for lightweight visible app listing.
|
|
170
|
+
2. `qf_apps_info_list` for admin-level app detail listing.
|
|
171
|
+
3. `qf_app_info_get` for one exact app.
|
|
172
|
+
4. `qf_app_packages_list` for user-visible app packages.
|
|
173
|
+
|
|
174
|
+
Audit flow:
|
|
175
|
+
|
|
176
|
+
1. `qf_apply_audit_records_list` to inspect one record's workflow history.
|
|
177
|
+
2. `qf_apply_audit_record_get` to inspect one audit record's field modifications.
|
|
178
|
+
|
|
162
179
|
Full calling contract (Chinese):
|
|
163
180
|
|
|
164
181
|
- [MCP 调用规范](./docs/MCP_CALLING_SPEC.md)
|
|
@@ -215,6 +232,51 @@ qingflow-mcp cli call qf_users_list --args '{"pageNum":1,"pageSize":100}'
|
|
|
215
232
|
qingflow-mcp cli call qf_user_get --args '{"userId":"u_123"}'
|
|
216
233
|
```
|
|
217
234
|
|
|
235
|
+
## Admin App Tools
|
|
236
|
+
|
|
237
|
+
These tools expose admin-facing app and package metadata without routing through `qf_query`:
|
|
238
|
+
|
|
239
|
+
1. `qf_apps_info_list`
|
|
240
|
+
- required `page_num`, `page_size`
|
|
241
|
+
- optional `app_key`
|
|
242
|
+
- aliases: `pageNum`, `pageSize`, `appKey`
|
|
243
|
+
2. `qf_app_info_get`
|
|
244
|
+
- required `app_key`
|
|
245
|
+
- alias: `appKey`
|
|
246
|
+
3. `qf_app_packages_list`
|
|
247
|
+
- required `user_id`
|
|
248
|
+
- optional `tag_id`, `keyword`, `limit`, `offset`
|
|
249
|
+
- aliases: `userId`, `tagId`
|
|
250
|
+
|
|
251
|
+
CLI examples:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
qingflow-mcp cli call qf_apps_info_list --args '{"pageNum":1,"pageSize":50}'
|
|
255
|
+
|
|
256
|
+
qingflow-mcp cli call qf_app_info_get --args '{"appKey":"app_demo"}'
|
|
257
|
+
|
|
258
|
+
qingflow-mcp cli call qf_app_packages_list --args '{"userId":"u_123","tagId":1001}'
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Audit Tools
|
|
262
|
+
|
|
263
|
+
These tools expose workflow log history as read-only MCP tools:
|
|
264
|
+
|
|
265
|
+
1. `qf_apply_audit_records_list`
|
|
266
|
+
- required `apply_id`
|
|
267
|
+
- alias: `applyId`
|
|
268
|
+
2. `qf_apply_audit_record_get`
|
|
269
|
+
- required `apply_id`, `audit_rcd_id`
|
|
270
|
+
- aliases: `applyId`, `auditRcdId`
|
|
271
|
+
|
|
272
|
+
CLI examples:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
qingflow-mcp cli call qf_apply_audit_records_list --args '{"applyId":"50001234"}'
|
|
276
|
+
|
|
277
|
+
qingflow-mcp cli call qf_apply_audit_record_get --args '{"applyId":"50001234","auditRcdId":"1111"}'
|
|
278
|
+
```
|
|
279
|
+
|
|
218
280
|
Return shape:
|
|
219
281
|
|
|
220
282
|
1. success: structured payload `{ "ok": true, "data": ... }` (`meta` only in `output_profile=verbose`)
|
package/dist/qingflow-client.js
CHANGED
|
@@ -33,6 +33,42 @@ export class QingflowClient {
|
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
|
+
listAppsInfo(options) {
|
|
37
|
+
return this.request({
|
|
38
|
+
method: "GET",
|
|
39
|
+
path: "/apps",
|
|
40
|
+
options: {
|
|
41
|
+
query: {
|
|
42
|
+
appKey: options.appKey,
|
|
43
|
+
pageNum: options.pageNum,
|
|
44
|
+
pageSize: options.pageSize
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
listAppPackages(options) {
|
|
50
|
+
return this.request({
|
|
51
|
+
method: "GET",
|
|
52
|
+
path: "/tags",
|
|
53
|
+
options: {
|
|
54
|
+
query: {
|
|
55
|
+
userId: options.userId
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
listApplyAuditRecords(applyId) {
|
|
61
|
+
return this.request({
|
|
62
|
+
method: "GET",
|
|
63
|
+
path: `/apply/${encodeURIComponent(applyId)}/auditRecord`
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
getApplyAuditRecord(applyId, auditRcdId) {
|
|
67
|
+
return this.request({
|
|
68
|
+
method: "GET",
|
|
69
|
+
path: `/apply/${encodeURIComponent(applyId)}/auditRecord/${encodeURIComponent(auditRcdId)}`
|
|
70
|
+
});
|
|
71
|
+
}
|
|
36
72
|
listDepartments(options = {}) {
|
|
37
73
|
return this.request({
|
|
38
74
|
method: "GET",
|
package/dist/server.js
CHANGED
|
@@ -65,7 +65,7 @@ const REQUEST_TIMEOUT_MS = toPositiveInt(process.env.QINGFLOW_REQUEST_TIMEOUT_MS
|
|
|
65
65
|
const EXECUTION_BUDGET_MS = toPositiveInt(process.env.QINGFLOW_EXECUTION_BUDGET_MS) ?? 20000;
|
|
66
66
|
const WAIT_RESULT_DEFAULT_TIMEOUT_MS = toPositiveInt(process.env.QINGFLOW_WAIT_RESULT_TIMEOUT_MS) ?? 5000;
|
|
67
67
|
const WAIT_RESULT_POLL_INTERVAL_MS = toPositiveInt(process.env.QINGFLOW_WAIT_RESULT_POLL_INTERVAL_MS) ?? 500;
|
|
68
|
-
const SERVER_VERSION = "0.3.
|
|
68
|
+
const SERVER_VERSION = "0.3.20";
|
|
69
69
|
const accessToken = process.env.QINGFLOW_ACCESS_TOKEN;
|
|
70
70
|
const baseUrl = process.env.QINGFLOW_BASE_URL;
|
|
71
71
|
if (!accessToken) {
|
|
@@ -353,6 +353,186 @@ const formSuccessOutputSchema = z.object({
|
|
|
353
353
|
const formOutputSchema = formSuccessOutputSchema;
|
|
354
354
|
const publicStringSchema = z.string().min(1);
|
|
355
355
|
const publicFieldSelectorSchema = z.union([publicStringSchema, z.number().int()]);
|
|
356
|
+
const appAuthUserSchema = z.object({
|
|
357
|
+
user_id: z.string().nullable(),
|
|
358
|
+
user_name: z.string().nullable()
|
|
359
|
+
});
|
|
360
|
+
const appAuthDepartmentSchema = z.object({
|
|
361
|
+
dept_id: z.number().int().nullable(),
|
|
362
|
+
dept_name: z.string().nullable()
|
|
363
|
+
});
|
|
364
|
+
const appAuthRoleSchema = z.object({
|
|
365
|
+
role_id: z.number().int().nullable(),
|
|
366
|
+
role_name: z.string().nullable()
|
|
367
|
+
});
|
|
368
|
+
const appAuthMembersSchema = z.object({
|
|
369
|
+
users: z.array(appAuthUserSchema),
|
|
370
|
+
departments: z.array(appAuthDepartmentSchema),
|
|
371
|
+
roles: z.array(appAuthRoleSchema)
|
|
372
|
+
});
|
|
373
|
+
const appCreatorSchema = z.object({
|
|
374
|
+
user_id: z.string().nullable(),
|
|
375
|
+
nick_name: z.string().nullable(),
|
|
376
|
+
head_img: z.string().nullable()
|
|
377
|
+
});
|
|
378
|
+
const appTagRefSchema = z.object({
|
|
379
|
+
tag_id: z.number().int().nullable(),
|
|
380
|
+
tag_name: z.string().nullable()
|
|
381
|
+
});
|
|
382
|
+
const appInfoSchema = z.object({
|
|
383
|
+
app_key: z.string().nullable(),
|
|
384
|
+
app_name: z.string().nullable(),
|
|
385
|
+
app_auth: z.number().int().nullable(),
|
|
386
|
+
app_icon: z.string().nullable(),
|
|
387
|
+
auth_members: appAuthMembersSchema,
|
|
388
|
+
creator: appCreatorSchema,
|
|
389
|
+
create_time: z.string().nullable(),
|
|
390
|
+
tags: z.array(appTagRefSchema),
|
|
391
|
+
app_publish_status: z.number().int().nullable()
|
|
392
|
+
});
|
|
393
|
+
const appInfoListInputPublicSchema = z.object({
|
|
394
|
+
page_num: z.number().int().positive().optional(),
|
|
395
|
+
pageNum: z.number().int().positive().optional(),
|
|
396
|
+
page_size: z.number().int().positive().optional(),
|
|
397
|
+
pageSize: z.number().int().positive().optional(),
|
|
398
|
+
app_key: publicStringSchema.optional(),
|
|
399
|
+
appKey: publicStringSchema.optional()
|
|
400
|
+
});
|
|
401
|
+
const appInfoListInputSchema = z.preprocess(normalizeAppInfoListInput, z.object({
|
|
402
|
+
page_num: z.number().int().positive().optional(),
|
|
403
|
+
page_size: z.number().int().positive().optional(),
|
|
404
|
+
app_key: z.string().min(1).optional()
|
|
405
|
+
}));
|
|
406
|
+
const appInfoListOutputSchema = z.object({
|
|
407
|
+
ok: z.literal(true),
|
|
408
|
+
data: z.object({
|
|
409
|
+
pagination: z.object({
|
|
410
|
+
page_num: z.number().int().positive(),
|
|
411
|
+
page_size: z.number().int().positive(),
|
|
412
|
+
page_amount: z.number().int().nonnegative(),
|
|
413
|
+
result_amount: z.number().int().nonnegative()
|
|
414
|
+
}),
|
|
415
|
+
apps: z.array(appInfoSchema)
|
|
416
|
+
}),
|
|
417
|
+
meta: apiMetaSchema
|
|
418
|
+
});
|
|
419
|
+
const appInfoGetInputPublicSchema = z.object({
|
|
420
|
+
app_key: publicStringSchema.optional(),
|
|
421
|
+
appKey: publicStringSchema.optional()
|
|
422
|
+
});
|
|
423
|
+
const appInfoGetInputSchema = z.preprocess(normalizeAppInfoGetInput, z.object({
|
|
424
|
+
app_key: z.string().min(1).optional()
|
|
425
|
+
}));
|
|
426
|
+
const appInfoGetOutputSchema = z.object({
|
|
427
|
+
ok: z.literal(true),
|
|
428
|
+
data: z.object({
|
|
429
|
+
app: appInfoSchema
|
|
430
|
+
}),
|
|
431
|
+
meta: apiMetaSchema
|
|
432
|
+
});
|
|
433
|
+
const appPackageAppSchema = z.object({
|
|
434
|
+
app_key: z.string().nullable(),
|
|
435
|
+
app_name: z.string().nullable()
|
|
436
|
+
});
|
|
437
|
+
const dashboardRefSchema = z.object({
|
|
438
|
+
dash_key: z.string().nullable(),
|
|
439
|
+
dash_name: z.string().nullable()
|
|
440
|
+
});
|
|
441
|
+
const appPackageSchema = z.object({
|
|
442
|
+
tag_id: z.number().int().nullable(),
|
|
443
|
+
tag_name: z.string().nullable(),
|
|
444
|
+
tag_icon: z.string().nullable(),
|
|
445
|
+
apps: z.array(appPackageAppSchema),
|
|
446
|
+
dashboards: z.array(dashboardRefSchema)
|
|
447
|
+
});
|
|
448
|
+
const appPackageListInputPublicSchema = z.object({
|
|
449
|
+
user_id: publicStringSchema.optional(),
|
|
450
|
+
userId: publicStringSchema.optional(),
|
|
451
|
+
tag_id: publicDirectorySelectorSchema.optional(),
|
|
452
|
+
tagId: publicDirectorySelectorSchema.optional(),
|
|
453
|
+
keyword: publicStringSchema.optional(),
|
|
454
|
+
limit: z.number().int().positive().max(500).optional(),
|
|
455
|
+
offset: z.number().int().nonnegative().optional()
|
|
456
|
+
});
|
|
457
|
+
const appPackageListInputSchema = z.preprocess(normalizeAppPackageListInput, z.object({
|
|
458
|
+
user_id: z.string().min(1).optional(),
|
|
459
|
+
tag_id: z.union([z.string().min(1), z.number().int()]).optional(),
|
|
460
|
+
keyword: z.string().min(1).optional(),
|
|
461
|
+
limit: z.number().int().positive().max(500).optional(),
|
|
462
|
+
offset: z.number().int().nonnegative().optional()
|
|
463
|
+
}));
|
|
464
|
+
const appPackageListOutputSchema = z.object({
|
|
465
|
+
ok: z.literal(true),
|
|
466
|
+
data: z.object({
|
|
467
|
+
user_id: z.string(),
|
|
468
|
+
tag_id_filter: z.union([z.string(), z.number(), z.null()]),
|
|
469
|
+
total_packages: z.number().int().nonnegative(),
|
|
470
|
+
returned_packages: z.number().int().nonnegative(),
|
|
471
|
+
limit: z.number().int().positive(),
|
|
472
|
+
offset: z.number().int().nonnegative(),
|
|
473
|
+
packages: z.array(appPackageSchema)
|
|
474
|
+
}),
|
|
475
|
+
meta: apiMetaSchema
|
|
476
|
+
});
|
|
477
|
+
const auditUserInfoSchema = z.object({
|
|
478
|
+
user_id: z.string().nullable(),
|
|
479
|
+
user_name: z.string().nullable(),
|
|
480
|
+
nick_name: z.string().nullable(),
|
|
481
|
+
head_img: z.string().nullable()
|
|
482
|
+
});
|
|
483
|
+
const auditRecordSummarySchema = z.object({
|
|
484
|
+
audit_rcd_id: z.number().int().nullable(),
|
|
485
|
+
audit_node_id: z.number().int().nullable(),
|
|
486
|
+
audit_node_name: z.string().nullable(),
|
|
487
|
+
audit_time_ms: z.number().int().nullable(),
|
|
488
|
+
audit_result: z.unknown().nullable(),
|
|
489
|
+
audit_feedback: z.string().nullable(),
|
|
490
|
+
audit_user: auditUserInfoSchema.nullable(),
|
|
491
|
+
wait_audit_users: z.array(auditUserInfoSchema)
|
|
492
|
+
});
|
|
493
|
+
const applyAuditRecordsInputPublicSchema = z.object({
|
|
494
|
+
apply_id: publicStringSchema.optional(),
|
|
495
|
+
applyId: publicStringSchema.optional()
|
|
496
|
+
});
|
|
497
|
+
const applyAuditRecordsInputSchema = z.preprocess(normalizeApplyAuditRecordsInput, z.object({
|
|
498
|
+
apply_id: z.string().min(1).optional()
|
|
499
|
+
}));
|
|
500
|
+
const applyAuditRecordsOutputSchema = z.object({
|
|
501
|
+
ok: z.literal(true),
|
|
502
|
+
data: z.object({
|
|
503
|
+
apply_id: z.string(),
|
|
504
|
+
apply_status: z.unknown().nullable(),
|
|
505
|
+
audit_records: z.array(auditRecordSummarySchema),
|
|
506
|
+
current_nodes: z.array(auditRecordSummarySchema)
|
|
507
|
+
}),
|
|
508
|
+
meta: apiMetaSchema
|
|
509
|
+
});
|
|
510
|
+
const auditModifySchema = z.object({
|
|
511
|
+
que_id: z.union([z.number(), z.string(), z.null()]),
|
|
512
|
+
que_title: z.string().nullable(),
|
|
513
|
+
que_type: z.unknown().nullable(),
|
|
514
|
+
before_answer: z.array(z.unknown()),
|
|
515
|
+
after_answer: z.array(z.unknown())
|
|
516
|
+
});
|
|
517
|
+
const applyAuditRecordGetInputPublicSchema = z.object({
|
|
518
|
+
apply_id: publicStringSchema.optional(),
|
|
519
|
+
applyId: publicStringSchema.optional(),
|
|
520
|
+
audit_rcd_id: publicStringSchema.optional(),
|
|
521
|
+
auditRcdId: publicStringSchema.optional()
|
|
522
|
+
});
|
|
523
|
+
const applyAuditRecordGetInputSchema = z.preprocess(normalizeApplyAuditRecordGetInput, z.object({
|
|
524
|
+
apply_id: z.string().min(1).optional(),
|
|
525
|
+
audit_rcd_id: z.string().min(1).optional()
|
|
526
|
+
}));
|
|
527
|
+
const applyAuditRecordGetOutputSchema = z.object({
|
|
528
|
+
ok: z.literal(true),
|
|
529
|
+
data: z.object({
|
|
530
|
+
apply_id: z.string(),
|
|
531
|
+
audit_rcd_id: z.union([z.string(), z.number()]),
|
|
532
|
+
modifies: z.array(auditModifySchema)
|
|
533
|
+
}),
|
|
534
|
+
meta: apiMetaSchema
|
|
535
|
+
});
|
|
356
536
|
const publicSortItemSchema = z.object({
|
|
357
537
|
que_id: publicFieldSelectorSchema,
|
|
358
538
|
ascend: z.boolean().optional()
|
|
@@ -565,12 +745,14 @@ const listOutputSchema = listSuccessOutputSchema;
|
|
|
565
745
|
const recordGetInputPublicSchema = z
|
|
566
746
|
.object({
|
|
567
747
|
apply_id: publicFieldSelectorSchema,
|
|
748
|
+
app_key: publicStringSchema.optional(),
|
|
568
749
|
max_columns: z.number().int().positive().max(MAX_COLUMN_LIMIT).optional(),
|
|
569
750
|
select_columns: z.array(publicFieldSelectorSchema).min(1).max(MAX_COLUMN_LIMIT),
|
|
570
751
|
output_profile: outputProfileSchema.optional()
|
|
571
752
|
});
|
|
572
753
|
const recordGetInputSchema = z.preprocess(normalizeRecordGetInput, z.object({
|
|
573
754
|
apply_id: z.union([z.string().min(1), z.number().int()]),
|
|
755
|
+
app_key: z.string().min(1).optional(),
|
|
574
756
|
max_columns: z.number().int().positive().max(MAX_COLUMN_LIMIT).optional(),
|
|
575
757
|
select_columns: z
|
|
576
758
|
.array(z.union([z.string().min(1), z.number().int()]))
|
|
@@ -1418,6 +1600,300 @@ server.registerTool("qf_apps_list", {
|
|
|
1418
1600
|
return errorResult(error);
|
|
1419
1601
|
}
|
|
1420
1602
|
});
|
|
1603
|
+
server.registerTool("qf_apps_info_list", {
|
|
1604
|
+
title: "Qingflow Apps Info List",
|
|
1605
|
+
description: "List app admin info with explicit pagination. Requires admin-level visibility.",
|
|
1606
|
+
inputSchema: appInfoListInputPublicSchema,
|
|
1607
|
+
outputSchema: appInfoListOutputSchema,
|
|
1608
|
+
annotations: {
|
|
1609
|
+
readOnlyHint: true,
|
|
1610
|
+
idempotentHint: true
|
|
1611
|
+
}
|
|
1612
|
+
}, async (args) => {
|
|
1613
|
+
try {
|
|
1614
|
+
const parsedArgs = appInfoListInputSchema.parse(args);
|
|
1615
|
+
if (parsedArgs.page_num === undefined) {
|
|
1616
|
+
throw missingRequiredFieldError({
|
|
1617
|
+
field: "page_num",
|
|
1618
|
+
tool: "qf_apps_info_list",
|
|
1619
|
+
fixHint: "Provide page_num (or pageNum), for example: {\"page_num\":1,\"page_size\":50}."
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
if (parsedArgs.page_size === undefined) {
|
|
1623
|
+
throw missingRequiredFieldError({
|
|
1624
|
+
field: "page_size",
|
|
1625
|
+
tool: "qf_apps_info_list",
|
|
1626
|
+
fixHint: "Provide page_size (or pageSize), for example: {\"page_num\":1,\"page_size\":50}."
|
|
1627
|
+
});
|
|
1628
|
+
}
|
|
1629
|
+
let response;
|
|
1630
|
+
try {
|
|
1631
|
+
response = await client.listAppsInfo({
|
|
1632
|
+
appKey: parsedArgs.app_key,
|
|
1633
|
+
pageNum: parsedArgs.page_num,
|
|
1634
|
+
pageSize: parsedArgs.page_size
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
1637
|
+
catch (error) {
|
|
1638
|
+
if (parsedArgs.app_key) {
|
|
1639
|
+
throw translateAdminApiError(error, {
|
|
1640
|
+
tool: "qf_apps_info_list",
|
|
1641
|
+
entity: "app",
|
|
1642
|
+
appKey: parsedArgs.app_key
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
throw error;
|
|
1646
|
+
}
|
|
1647
|
+
const result = asObject(response.result);
|
|
1648
|
+
const apps = asArray(result?.apps).map((item) => normalizeAppInfo(item));
|
|
1649
|
+
return okResult({
|
|
1650
|
+
ok: true,
|
|
1651
|
+
data: {
|
|
1652
|
+
pagination: {
|
|
1653
|
+
page_num: toPositiveInt(result?.pageNum) ?? parsedArgs.page_num,
|
|
1654
|
+
page_size: toPositiveInt(result?.pageSize) ?? parsedArgs.page_size,
|
|
1655
|
+
page_amount: toNonNegativeInt(result?.pageAmount) ?? 0,
|
|
1656
|
+
result_amount: toNonNegativeInt(result?.resultAmount) ?? apps.length
|
|
1657
|
+
},
|
|
1658
|
+
apps
|
|
1659
|
+
},
|
|
1660
|
+
meta: buildMeta(response)
|
|
1661
|
+
}, `Returned ${apps.length} app info rows`);
|
|
1662
|
+
}
|
|
1663
|
+
catch (error) {
|
|
1664
|
+
return errorResult(error);
|
|
1665
|
+
}
|
|
1666
|
+
});
|
|
1667
|
+
server.registerTool("qf_app_info_get", {
|
|
1668
|
+
title: "Qingflow App Info Get",
|
|
1669
|
+
description: "Get one app's admin info by app_key. Requires admin-level visibility.",
|
|
1670
|
+
inputSchema: appInfoGetInputPublicSchema,
|
|
1671
|
+
outputSchema: appInfoGetOutputSchema,
|
|
1672
|
+
annotations: {
|
|
1673
|
+
readOnlyHint: true,
|
|
1674
|
+
idempotentHint: true
|
|
1675
|
+
}
|
|
1676
|
+
}, async (args) => {
|
|
1677
|
+
try {
|
|
1678
|
+
const parsedArgs = appInfoGetInputSchema.parse(args);
|
|
1679
|
+
if (!parsedArgs.app_key) {
|
|
1680
|
+
throw missingRequiredFieldError({
|
|
1681
|
+
field: "app_key",
|
|
1682
|
+
tool: "qf_app_info_get",
|
|
1683
|
+
fixHint: "Provide app_key (or appKey), for example: {\"app_key\":\"21b3d559\"}."
|
|
1684
|
+
});
|
|
1685
|
+
}
|
|
1686
|
+
let response;
|
|
1687
|
+
try {
|
|
1688
|
+
response = await client.listAppsInfo({
|
|
1689
|
+
appKey: parsedArgs.app_key,
|
|
1690
|
+
pageNum: 1,
|
|
1691
|
+
pageSize: 1
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
catch (error) {
|
|
1695
|
+
throw translateAdminApiError(error, {
|
|
1696
|
+
tool: "qf_app_info_get",
|
|
1697
|
+
entity: "app",
|
|
1698
|
+
appKey: parsedArgs.app_key
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
const result = asObject(response.result);
|
|
1702
|
+
const app = asArray(result?.apps)
|
|
1703
|
+
.map((item) => normalizeAppInfo(item))
|
|
1704
|
+
.find((item) => item.app_key === parsedArgs.app_key);
|
|
1705
|
+
if (!app) {
|
|
1706
|
+
throw new InputValidationError({
|
|
1707
|
+
message: `App \"${parsedArgs.app_key}\" not found`,
|
|
1708
|
+
errorCode: "APP_NOT_FOUND",
|
|
1709
|
+
fixHint: "Call qf_apps_list or qf_apps_info_list first to confirm the exact app_key.",
|
|
1710
|
+
details: {
|
|
1711
|
+
tool: "qf_app_info_get",
|
|
1712
|
+
app_key: parsedArgs.app_key
|
|
1713
|
+
}
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
return okResult({
|
|
1717
|
+
ok: true,
|
|
1718
|
+
data: {
|
|
1719
|
+
app
|
|
1720
|
+
},
|
|
1721
|
+
meta: buildMeta(response)
|
|
1722
|
+
}, `Fetched app ${parsedArgs.app_key}`);
|
|
1723
|
+
}
|
|
1724
|
+
catch (error) {
|
|
1725
|
+
return errorResult(error);
|
|
1726
|
+
}
|
|
1727
|
+
});
|
|
1728
|
+
server.registerTool("qf_app_packages_list", {
|
|
1729
|
+
title: "Qingflow App Packages List",
|
|
1730
|
+
description: "List app packages visible to one user, with optional local tag/keyword slicing.",
|
|
1731
|
+
inputSchema: appPackageListInputPublicSchema,
|
|
1732
|
+
outputSchema: appPackageListOutputSchema,
|
|
1733
|
+
annotations: {
|
|
1734
|
+
readOnlyHint: true,
|
|
1735
|
+
idempotentHint: true
|
|
1736
|
+
}
|
|
1737
|
+
}, async (args) => {
|
|
1738
|
+
try {
|
|
1739
|
+
const parsedArgs = appPackageListInputSchema.parse(args);
|
|
1740
|
+
if (!parsedArgs.user_id) {
|
|
1741
|
+
throw missingRequiredFieldError({
|
|
1742
|
+
field: "user_id",
|
|
1743
|
+
tool: "qf_app_packages_list",
|
|
1744
|
+
fixHint: "Provide user_id (or userId), for example: {\"user_id\":\"u_123\"}."
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1747
|
+
const response = await client.listAppPackages({
|
|
1748
|
+
userId: parsedArgs.user_id
|
|
1749
|
+
});
|
|
1750
|
+
const keyword = parsedArgs.keyword?.trim().toLowerCase() ?? null;
|
|
1751
|
+
const limit = parsedArgs.limit ?? 50;
|
|
1752
|
+
const offset = parsedArgs.offset ?? 0;
|
|
1753
|
+
let packages = asArray(asObject(response.result)?.tagList).map((item) => normalizeAppPackage(item));
|
|
1754
|
+
if (parsedArgs.tag_id !== undefined) {
|
|
1755
|
+
packages = packages.filter((item) => String(item.tag_id ?? "") === String(parsedArgs.tag_id));
|
|
1756
|
+
if (packages.length === 0) {
|
|
1757
|
+
throw new InputValidationError({
|
|
1758
|
+
message: `App package \"${String(parsedArgs.tag_id)}\" not found`,
|
|
1759
|
+
errorCode: "APP_PACKAGE_NOT_FOUND",
|
|
1760
|
+
fixHint: "Call qf_app_packages_list without tag_id first to confirm the exact package id.",
|
|
1761
|
+
details: {
|
|
1762
|
+
tool: "qf_app_packages_list",
|
|
1763
|
+
tag_id: parsedArgs.tag_id,
|
|
1764
|
+
user_id: parsedArgs.user_id
|
|
1765
|
+
}
|
|
1766
|
+
});
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
const filtered = keyword
|
|
1770
|
+
? packages.filter((item) => {
|
|
1771
|
+
const values = [
|
|
1772
|
+
item.tag_name ?? "",
|
|
1773
|
+
...item.apps.map((app) => app.app_name ?? ""),
|
|
1774
|
+
...item.dashboards.map((dash) => dash.dash_name ?? "")
|
|
1775
|
+
];
|
|
1776
|
+
return values.some((value) => value.toLowerCase().includes(keyword));
|
|
1777
|
+
})
|
|
1778
|
+
: packages;
|
|
1779
|
+
const sliced = filtered.slice(offset, offset + limit);
|
|
1780
|
+
return okResult({
|
|
1781
|
+
ok: true,
|
|
1782
|
+
data: {
|
|
1783
|
+
user_id: parsedArgs.user_id,
|
|
1784
|
+
tag_id_filter: parsedArgs.tag_id ?? null,
|
|
1785
|
+
total_packages: filtered.length,
|
|
1786
|
+
returned_packages: sliced.length,
|
|
1787
|
+
limit,
|
|
1788
|
+
offset,
|
|
1789
|
+
packages: sliced
|
|
1790
|
+
},
|
|
1791
|
+
meta: buildMeta(response)
|
|
1792
|
+
}, `Returned ${sliced.length}/${filtered.length} app packages`);
|
|
1793
|
+
}
|
|
1794
|
+
catch (error) {
|
|
1795
|
+
return errorResult(error);
|
|
1796
|
+
}
|
|
1797
|
+
});
|
|
1798
|
+
server.registerTool("qf_apply_audit_records_list", {
|
|
1799
|
+
title: "Qingflow Apply Audit Records List",
|
|
1800
|
+
description: "List one record's workflow audit log and current pending nodes.",
|
|
1801
|
+
inputSchema: applyAuditRecordsInputPublicSchema,
|
|
1802
|
+
outputSchema: applyAuditRecordsOutputSchema,
|
|
1803
|
+
annotations: {
|
|
1804
|
+
readOnlyHint: true,
|
|
1805
|
+
idempotentHint: true
|
|
1806
|
+
}
|
|
1807
|
+
}, async (args) => {
|
|
1808
|
+
try {
|
|
1809
|
+
const parsedArgs = applyAuditRecordsInputSchema.parse(args);
|
|
1810
|
+
if (!parsedArgs.apply_id) {
|
|
1811
|
+
throw missingRequiredFieldError({
|
|
1812
|
+
field: "apply_id",
|
|
1813
|
+
tool: "qf_apply_audit_records_list",
|
|
1814
|
+
fixHint: "Provide apply_id (or applyId), for example: {\"apply_id\":\"50001234\"}."
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
let response;
|
|
1818
|
+
try {
|
|
1819
|
+
response = await client.listApplyAuditRecords(parsedArgs.apply_id);
|
|
1820
|
+
}
|
|
1821
|
+
catch (error) {
|
|
1822
|
+
throw translateAdminApiError(error, {
|
|
1823
|
+
tool: "qf_apply_audit_records_list",
|
|
1824
|
+
entity: "apply",
|
|
1825
|
+
applyId: parsedArgs.apply_id
|
|
1826
|
+
});
|
|
1827
|
+
}
|
|
1828
|
+
const result = asObject(response.result);
|
|
1829
|
+
return okResult({
|
|
1830
|
+
ok: true,
|
|
1831
|
+
data: {
|
|
1832
|
+
apply_id: parsedArgs.apply_id,
|
|
1833
|
+
apply_status: result?.applyStatus ?? null,
|
|
1834
|
+
audit_records: asArray(result?.auditRecords).map((item) => normalizeAuditRecordSummary(item)),
|
|
1835
|
+
current_nodes: asArray(result?.currentNodes).map((item) => normalizeAuditRecordSummary(item))
|
|
1836
|
+
},
|
|
1837
|
+
meta: buildMeta(response)
|
|
1838
|
+
}, `Fetched audit records for apply ${parsedArgs.apply_id}`);
|
|
1839
|
+
}
|
|
1840
|
+
catch (error) {
|
|
1841
|
+
return errorResult(error);
|
|
1842
|
+
}
|
|
1843
|
+
});
|
|
1844
|
+
server.registerTool("qf_apply_audit_record_get", {
|
|
1845
|
+
title: "Qingflow Apply Audit Record Get",
|
|
1846
|
+
description: "Get one workflow audit record detail by apply_id and audit_rcd_id.",
|
|
1847
|
+
inputSchema: applyAuditRecordGetInputPublicSchema,
|
|
1848
|
+
outputSchema: applyAuditRecordGetOutputSchema,
|
|
1849
|
+
annotations: {
|
|
1850
|
+
readOnlyHint: true,
|
|
1851
|
+
idempotentHint: true
|
|
1852
|
+
}
|
|
1853
|
+
}, async (args) => {
|
|
1854
|
+
try {
|
|
1855
|
+
const parsedArgs = applyAuditRecordGetInputSchema.parse(args);
|
|
1856
|
+
if (!parsedArgs.apply_id) {
|
|
1857
|
+
throw missingRequiredFieldError({
|
|
1858
|
+
field: "apply_id",
|
|
1859
|
+
tool: "qf_apply_audit_record_get",
|
|
1860
|
+
fixHint: "Provide apply_id (or applyId), for example: {\"apply_id\":\"50001234\",\"audit_rcd_id\":\"1111\"}."
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
if (!parsedArgs.audit_rcd_id) {
|
|
1864
|
+
throw missingRequiredFieldError({
|
|
1865
|
+
field: "audit_rcd_id",
|
|
1866
|
+
tool: "qf_apply_audit_record_get",
|
|
1867
|
+
fixHint: "Provide audit_rcd_id (or auditRcdId), for example: {\"apply_id\":\"50001234\",\"audit_rcd_id\":\"1111\"}."
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
let response;
|
|
1871
|
+
try {
|
|
1872
|
+
response = await client.getApplyAuditRecord(parsedArgs.apply_id, parsedArgs.audit_rcd_id);
|
|
1873
|
+
}
|
|
1874
|
+
catch (error) {
|
|
1875
|
+
throw translateAdminApiError(error, {
|
|
1876
|
+
tool: "qf_apply_audit_record_get",
|
|
1877
|
+
entity: "audit_record",
|
|
1878
|
+
applyId: parsedArgs.apply_id,
|
|
1879
|
+
auditRcdId: parsedArgs.audit_rcd_id
|
|
1880
|
+
});
|
|
1881
|
+
}
|
|
1882
|
+
const result = asObject(response.result);
|
|
1883
|
+
return okResult({
|
|
1884
|
+
ok: true,
|
|
1885
|
+
data: {
|
|
1886
|
+
apply_id: parsedArgs.apply_id,
|
|
1887
|
+
audit_rcd_id: result?.auditRcdId ?? parsedArgs.audit_rcd_id,
|
|
1888
|
+
modifies: normalizeAuditModifies(result?.auditModifies)
|
|
1889
|
+
},
|
|
1890
|
+
meta: buildMeta(response)
|
|
1891
|
+
}, `Fetched audit record ${parsedArgs.audit_rcd_id} for apply ${parsedArgs.apply_id}`);
|
|
1892
|
+
}
|
|
1893
|
+
catch (error) {
|
|
1894
|
+
return errorResult(error);
|
|
1895
|
+
}
|
|
1896
|
+
});
|
|
1421
1897
|
server.registerTool("qf_departments_list", {
|
|
1422
1898
|
title: "Qingflow Departments List",
|
|
1423
1899
|
description: "List departments with optional dept_id filter and local keyword slicing.",
|
|
@@ -2358,6 +2834,17 @@ function buildMeta(response) {
|
|
|
2358
2834
|
base_url: baseUrl
|
|
2359
2835
|
};
|
|
2360
2836
|
}
|
|
2837
|
+
async function fetchRecordsByApplyIds(params) {
|
|
2838
|
+
const response = await client.listRecords(params.appKey, buildListPayload({
|
|
2839
|
+
pageNum: 1,
|
|
2840
|
+
pageSize: Math.min(Math.max(params.applyIds.length, 1), 200),
|
|
2841
|
+
applyIds: params.applyIds
|
|
2842
|
+
}), { userId: params.userId });
|
|
2843
|
+
const records = asArray(asObject(response.result)?.result)
|
|
2844
|
+
.map((item) => asObject(item))
|
|
2845
|
+
.filter((item) => Boolean(item));
|
|
2846
|
+
return { response, records };
|
|
2847
|
+
}
|
|
2361
2848
|
function normalizeStringArray(value) {
|
|
2362
2849
|
return uniqueStringList(asArray(value)
|
|
2363
2850
|
.map((item) => asNullableString(item)?.trim() ?? "")
|
|
@@ -2391,6 +2878,174 @@ function normalizeUser(raw) {
|
|
|
2391
2878
|
superior_id: asNullableString(obj.superiorId)
|
|
2392
2879
|
};
|
|
2393
2880
|
}
|
|
2881
|
+
function normalizeAppAuthMembers(raw) {
|
|
2882
|
+
const obj = asObject(raw) ?? {};
|
|
2883
|
+
return {
|
|
2884
|
+
users: asArray(obj.users).map((item) => {
|
|
2885
|
+
const entry = asObject(item) ?? {};
|
|
2886
|
+
return {
|
|
2887
|
+
user_id: asNullableString(entry.userId),
|
|
2888
|
+
user_name: asNullableString(entry.userName)
|
|
2889
|
+
};
|
|
2890
|
+
}),
|
|
2891
|
+
departments: asArray(obj.depts).map((item) => {
|
|
2892
|
+
const entry = asObject(item) ?? {};
|
|
2893
|
+
return {
|
|
2894
|
+
dept_id: toNonNegativeInt(entry.deptId),
|
|
2895
|
+
dept_name: asNullableString(entry.deptName)
|
|
2896
|
+
};
|
|
2897
|
+
}),
|
|
2898
|
+
roles: asArray(obj.roles).map((item) => {
|
|
2899
|
+
const entry = asObject(item) ?? {};
|
|
2900
|
+
return {
|
|
2901
|
+
role_id: toNonNegativeInt(entry.roleId),
|
|
2902
|
+
role_name: asNullableString(entry.roleName)
|
|
2903
|
+
};
|
|
2904
|
+
})
|
|
2905
|
+
};
|
|
2906
|
+
}
|
|
2907
|
+
function normalizeAppInfo(raw) {
|
|
2908
|
+
const obj = asObject(raw) ?? {};
|
|
2909
|
+
const creator = asObject(obj.creator) ?? {};
|
|
2910
|
+
return {
|
|
2911
|
+
app_key: asNullableString(obj.appKey),
|
|
2912
|
+
app_name: asNullableString(obj.appName),
|
|
2913
|
+
app_auth: toNonNegativeInt(obj.appAuth),
|
|
2914
|
+
app_icon: asNullableString(obj.appIcon),
|
|
2915
|
+
auth_members: normalizeAppAuthMembers(obj.authmembers ?? obj.authMembers),
|
|
2916
|
+
creator: {
|
|
2917
|
+
user_id: asNullableString(creator.userId),
|
|
2918
|
+
nick_name: asNullableString(creator.nickName),
|
|
2919
|
+
head_img: asNullableString(creator.headImg)
|
|
2920
|
+
},
|
|
2921
|
+
create_time: asNullableString(obj.createTime),
|
|
2922
|
+
tags: asArray(obj.tags).map((item) => {
|
|
2923
|
+
const entry = asObject(item) ?? {};
|
|
2924
|
+
return {
|
|
2925
|
+
tag_id: toNonNegativeInt(entry.tagId),
|
|
2926
|
+
tag_name: asNullableString(entry.tagName)
|
|
2927
|
+
};
|
|
2928
|
+
}),
|
|
2929
|
+
app_publish_status: toNonNegativeInt(obj.appPublishStatus)
|
|
2930
|
+
};
|
|
2931
|
+
}
|
|
2932
|
+
function normalizeAppPackage(raw) {
|
|
2933
|
+
const obj = asObject(raw) ?? {};
|
|
2934
|
+
return {
|
|
2935
|
+
tag_id: toNonNegativeInt(obj.tagId),
|
|
2936
|
+
tag_name: asNullableString(obj.tagName),
|
|
2937
|
+
tag_icon: asNullableString(obj.tagIcon),
|
|
2938
|
+
apps: asArray(obj.appList).map((item) => {
|
|
2939
|
+
const entry = asObject(item) ?? {};
|
|
2940
|
+
return {
|
|
2941
|
+
app_key: asNullableString(entry.appKey),
|
|
2942
|
+
app_name: asNullableString(entry.appName)
|
|
2943
|
+
};
|
|
2944
|
+
}),
|
|
2945
|
+
dashboards: asArray(obj.dashList).map((item) => {
|
|
2946
|
+
const entry = asObject(item) ?? {};
|
|
2947
|
+
return {
|
|
2948
|
+
dash_key: asNullableString(entry.dashKey),
|
|
2949
|
+
dash_name: asNullableString(entry.dashName)
|
|
2950
|
+
};
|
|
2951
|
+
})
|
|
2952
|
+
};
|
|
2953
|
+
}
|
|
2954
|
+
function normalizeAuditUserInfo(raw) {
|
|
2955
|
+
const obj = asObject(raw) ?? {};
|
|
2956
|
+
return {
|
|
2957
|
+
user_id: asNullableString(obj.userId),
|
|
2958
|
+
user_name: asNullableString(obj.userName),
|
|
2959
|
+
nick_name: asNullableString(obj.nickName),
|
|
2960
|
+
head_img: asNullableString(obj.headImg)
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2963
|
+
function toNullableSpecialId(value) {
|
|
2964
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2965
|
+
return Math.trunc(value);
|
|
2966
|
+
}
|
|
2967
|
+
if (typeof value === "string" && value.trim()) {
|
|
2968
|
+
const normalized = value.trim();
|
|
2969
|
+
if (/^\d+$/.test(normalized)) {
|
|
2970
|
+
return Number(normalized);
|
|
2971
|
+
}
|
|
2972
|
+
return normalized;
|
|
2973
|
+
}
|
|
2974
|
+
const obj = asObject(value);
|
|
2975
|
+
if (!obj) {
|
|
2976
|
+
return null;
|
|
2977
|
+
}
|
|
2978
|
+
const candidate = obj.queId ?? obj.id ?? obj.value ?? obj.dataValue ?? null;
|
|
2979
|
+
return candidate === null ? null : toNullableSpecialId(candidate);
|
|
2980
|
+
}
|
|
2981
|
+
function normalizeAuditRecordSummary(raw) {
|
|
2982
|
+
const obj = asObject(raw) ?? {};
|
|
2983
|
+
return {
|
|
2984
|
+
audit_rcd_id: toNonNegativeInt(obj.auditRcdId),
|
|
2985
|
+
audit_node_id: toNonNegativeInt(obj.auditNodeId),
|
|
2986
|
+
audit_node_name: asNullableString(obj.auditNodeName),
|
|
2987
|
+
audit_time_ms: toNonNegativeInt(obj.auditTime),
|
|
2988
|
+
audit_result: obj.auditResult ?? null,
|
|
2989
|
+
audit_feedback: asNullableString(obj.auditFeedback),
|
|
2990
|
+
audit_user: obj.auditUser ? normalizeAuditUserInfo(obj.auditUser) : null,
|
|
2991
|
+
wait_audit_users: asArray(obj.waitAuditUserList).map((item) => normalizeAuditUserInfo(item))
|
|
2992
|
+
};
|
|
2993
|
+
}
|
|
2994
|
+
function normalizeAuditModifies(raw) {
|
|
2995
|
+
const source = Array.isArray(raw)
|
|
2996
|
+
? raw
|
|
2997
|
+
: asObject(raw)
|
|
2998
|
+
? Object.values(raw)
|
|
2999
|
+
: [];
|
|
3000
|
+
return source.map((item) => {
|
|
3001
|
+
const entry = asObject(item) ?? {};
|
|
3002
|
+
return {
|
|
3003
|
+
que_id: toNullableSpecialId(entry.queId),
|
|
3004
|
+
que_title: asNullableString(entry.queTitle),
|
|
3005
|
+
que_type: entry.queType ?? null,
|
|
3006
|
+
before_answer: asArray(entry.beforeAnswer),
|
|
3007
|
+
after_answer: asArray(entry.afterAnswer)
|
|
3008
|
+
};
|
|
3009
|
+
});
|
|
3010
|
+
}
|
|
3011
|
+
function translateAdminApiError(error, params) {
|
|
3012
|
+
if (error instanceof QingflowApiError &&
|
|
3013
|
+
(error.httpStatus === 404 || error.errCode === 404)) {
|
|
3014
|
+
if (params.entity === "app") {
|
|
3015
|
+
return new InputValidationError({
|
|
3016
|
+
message: `App \"${params.appKey}\" not found`,
|
|
3017
|
+
errorCode: "APP_NOT_FOUND",
|
|
3018
|
+
fixHint: "Call qf_apps_list or qf_apps_info_list first to confirm the exact app_key.",
|
|
3019
|
+
details: {
|
|
3020
|
+
tool: params.tool,
|
|
3021
|
+
app_key: params.appKey
|
|
3022
|
+
}
|
|
3023
|
+
});
|
|
3024
|
+
}
|
|
3025
|
+
if (params.entity === "apply") {
|
|
3026
|
+
return new InputValidationError({
|
|
3027
|
+
message: `Apply \"${params.applyId}\" not found`,
|
|
3028
|
+
errorCode: "APPLY_NOT_FOUND",
|
|
3029
|
+
fixHint: "Use qf_record_get or your record query tools first to confirm the exact apply_id.",
|
|
3030
|
+
details: {
|
|
3031
|
+
tool: params.tool,
|
|
3032
|
+
apply_id: params.applyId
|
|
3033
|
+
}
|
|
3034
|
+
});
|
|
3035
|
+
}
|
|
3036
|
+
return new InputValidationError({
|
|
3037
|
+
message: `Audit record \"${params.auditRcdId}\" for apply \"${params.applyId}\" not found`,
|
|
3038
|
+
errorCode: "AUDIT_RECORD_NOT_FOUND",
|
|
3039
|
+
fixHint: "Call qf_apply_audit_records_list first to confirm the exact audit_rcd_id.",
|
|
3040
|
+
details: {
|
|
3041
|
+
tool: params.tool,
|
|
3042
|
+
apply_id: params.applyId,
|
|
3043
|
+
audit_rcd_id: params.auditRcdId
|
|
3044
|
+
}
|
|
3045
|
+
});
|
|
3046
|
+
}
|
|
3047
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
3048
|
+
}
|
|
2394
3049
|
function translateDirectoryApiError(error, params) {
|
|
2395
3050
|
if (error instanceof QingflowApiError &&
|
|
2396
3051
|
(error.httpStatus === 404 || error.errCode === 404)) {
|
|
@@ -2437,6 +3092,7 @@ function missingRequiredFieldError(params) {
|
|
|
2437
3092
|
const COMMON_INPUT_ALIASES = {
|
|
2438
3093
|
appKey: "app_key",
|
|
2439
3094
|
userId: "user_id",
|
|
3095
|
+
tagId: "tag_id",
|
|
2440
3096
|
pageNum: "page_num",
|
|
2441
3097
|
pageSize: "page_size",
|
|
2442
3098
|
pageToken: "page_token",
|
|
@@ -2448,6 +3104,7 @@ const COMMON_INPUT_ALIASES = {
|
|
|
2448
3104
|
queryMode: "query_mode",
|
|
2449
3105
|
queryLogic: "query_logic",
|
|
2450
3106
|
applyId: "apply_id",
|
|
3107
|
+
auditRcdId: "audit_rcd_id",
|
|
2451
3108
|
applyIds: "apply_ids",
|
|
2452
3109
|
maxRows: "max_rows",
|
|
2453
3110
|
rowLimit: "max_rows",
|
|
@@ -2582,6 +3239,87 @@ function normalizeUserGetInput(raw) {
|
|
|
2582
3239
|
user_id: coerceStringLike(normalizedObj.user_id)
|
|
2583
3240
|
};
|
|
2584
3241
|
}
|
|
3242
|
+
function normalizeAppInfoListInput(raw) {
|
|
3243
|
+
const parsedRoot = parseJsonLikeDeep(raw);
|
|
3244
|
+
const obj = asObject(parsedRoot);
|
|
3245
|
+
if (!obj) {
|
|
3246
|
+
return parsedRoot;
|
|
3247
|
+
}
|
|
3248
|
+
const normalizedObj = applyAliases(obj, {
|
|
3249
|
+
appKey: "app_key",
|
|
3250
|
+
pageNum: "page_num",
|
|
3251
|
+
pageSize: "page_size"
|
|
3252
|
+
});
|
|
3253
|
+
return {
|
|
3254
|
+
...normalizedObj,
|
|
3255
|
+
app_key: coerceStringLike(normalizedObj.app_key),
|
|
3256
|
+
page_num: coerceNumberLike(normalizedObj.page_num),
|
|
3257
|
+
page_size: coerceNumberLike(normalizedObj.page_size)
|
|
3258
|
+
};
|
|
3259
|
+
}
|
|
3260
|
+
function normalizeAppInfoGetInput(raw) {
|
|
3261
|
+
const parsedRoot = parseJsonLikeDeep(raw);
|
|
3262
|
+
const obj = asObject(parsedRoot);
|
|
3263
|
+
if (!obj) {
|
|
3264
|
+
return parsedRoot;
|
|
3265
|
+
}
|
|
3266
|
+
const normalizedObj = applyAliases(obj, {
|
|
3267
|
+
appKey: "app_key"
|
|
3268
|
+
});
|
|
3269
|
+
return {
|
|
3270
|
+
...normalizedObj,
|
|
3271
|
+
app_key: coerceStringLike(normalizedObj.app_key)
|
|
3272
|
+
};
|
|
3273
|
+
}
|
|
3274
|
+
function normalizeAppPackageListInput(raw) {
|
|
3275
|
+
const parsedRoot = parseJsonLikeDeep(raw);
|
|
3276
|
+
const obj = asObject(parsedRoot);
|
|
3277
|
+
if (!obj) {
|
|
3278
|
+
return parsedRoot;
|
|
3279
|
+
}
|
|
3280
|
+
const normalizedObj = applyAliases(obj, {
|
|
3281
|
+
userId: "user_id",
|
|
3282
|
+
tagId: "tag_id"
|
|
3283
|
+
});
|
|
3284
|
+
return {
|
|
3285
|
+
...normalizedObj,
|
|
3286
|
+
user_id: coerceStringLike(normalizedObj.user_id),
|
|
3287
|
+
tag_id: coerceNumberLike(normalizeSelectorInputValue(normalizedObj.tag_id)),
|
|
3288
|
+
keyword: coerceStringLike(normalizedObj.keyword),
|
|
3289
|
+
limit: coerceNumberLike(normalizedObj.limit),
|
|
3290
|
+
offset: coerceNumberLike(normalizedObj.offset)
|
|
3291
|
+
};
|
|
3292
|
+
}
|
|
3293
|
+
function normalizeApplyAuditRecordsInput(raw) {
|
|
3294
|
+
const parsedRoot = parseJsonLikeDeep(raw);
|
|
3295
|
+
const obj = asObject(parsedRoot);
|
|
3296
|
+
if (!obj) {
|
|
3297
|
+
return parsedRoot;
|
|
3298
|
+
}
|
|
3299
|
+
const normalizedObj = applyAliases(obj, {
|
|
3300
|
+
applyId: "apply_id"
|
|
3301
|
+
});
|
|
3302
|
+
return {
|
|
3303
|
+
...normalizedObj,
|
|
3304
|
+
apply_id: coerceStringLike(normalizedObj.apply_id)
|
|
3305
|
+
};
|
|
3306
|
+
}
|
|
3307
|
+
function normalizeApplyAuditRecordGetInput(raw) {
|
|
3308
|
+
const parsedRoot = parseJsonLikeDeep(raw);
|
|
3309
|
+
const obj = asObject(parsedRoot);
|
|
3310
|
+
if (!obj) {
|
|
3311
|
+
return parsedRoot;
|
|
3312
|
+
}
|
|
3313
|
+
const normalizedObj = applyAliases(obj, {
|
|
3314
|
+
applyId: "apply_id",
|
|
3315
|
+
auditRcdId: "audit_rcd_id"
|
|
3316
|
+
});
|
|
3317
|
+
return {
|
|
3318
|
+
...normalizedObj,
|
|
3319
|
+
apply_id: coerceStringLike(normalizedObj.apply_id),
|
|
3320
|
+
audit_rcd_id: coerceStringLike(normalizedObj.audit_rcd_id)
|
|
3321
|
+
};
|
|
3322
|
+
}
|
|
2585
3323
|
function buildToolSpecCatalog() {
|
|
2586
3324
|
return [
|
|
2587
3325
|
{
|
|
@@ -2611,6 +3349,67 @@ function buildToolSpecCatalog() {
|
|
|
2611
3349
|
limit: 20
|
|
2612
3350
|
}
|
|
2613
3351
|
},
|
|
3352
|
+
{
|
|
3353
|
+
tool: "qf_apps_info_list",
|
|
3354
|
+
required: ["page_num", "page_size"],
|
|
3355
|
+
limits: {
|
|
3356
|
+
input_contract: "strict JSON only; page_num/page_size must be native JSON numbers"
|
|
3357
|
+
},
|
|
3358
|
+
aliases: collectAliasHints(["page_num", "page_size", "app_key"], {}),
|
|
3359
|
+
minimal_example: {
|
|
3360
|
+
page_num: 1,
|
|
3361
|
+
page_size: 50,
|
|
3362
|
+
app_key: "21b3d559"
|
|
3363
|
+
}
|
|
3364
|
+
},
|
|
3365
|
+
{
|
|
3366
|
+
tool: "qf_app_info_get",
|
|
3367
|
+
required: ["app_key"],
|
|
3368
|
+
limits: {
|
|
3369
|
+
input_contract: "strict JSON only; app_key must be a native JSON string"
|
|
3370
|
+
},
|
|
3371
|
+
aliases: collectAliasHints(["app_key"], {}),
|
|
3372
|
+
minimal_example: {
|
|
3373
|
+
app_key: "21b3d559"
|
|
3374
|
+
}
|
|
3375
|
+
},
|
|
3376
|
+
{
|
|
3377
|
+
tool: "qf_app_packages_list",
|
|
3378
|
+
required: ["user_id"],
|
|
3379
|
+
limits: {
|
|
3380
|
+
limit_max: 500,
|
|
3381
|
+
offset_min: 0,
|
|
3382
|
+
input_contract: "strict JSON only; user_id must be a native JSON string when provided"
|
|
3383
|
+
},
|
|
3384
|
+
aliases: collectAliasHints(["user_id", "tag_id"], {}),
|
|
3385
|
+
minimal_example: {
|
|
3386
|
+
user_id: "u_123",
|
|
3387
|
+
limit: 20
|
|
3388
|
+
}
|
|
3389
|
+
},
|
|
3390
|
+
{
|
|
3391
|
+
tool: "qf_apply_audit_records_list",
|
|
3392
|
+
required: ["apply_id"],
|
|
3393
|
+
limits: {
|
|
3394
|
+
input_contract: "strict JSON only; apply_id must be a native JSON string"
|
|
3395
|
+
},
|
|
3396
|
+
aliases: collectAliasHints(["apply_id"], {}),
|
|
3397
|
+
minimal_example: {
|
|
3398
|
+
apply_id: "50001234"
|
|
3399
|
+
}
|
|
3400
|
+
},
|
|
3401
|
+
{
|
|
3402
|
+
tool: "qf_apply_audit_record_get",
|
|
3403
|
+
required: ["apply_id", "audit_rcd_id"],
|
|
3404
|
+
limits: {
|
|
3405
|
+
input_contract: "strict JSON only; apply_id/audit_rcd_id must be native JSON strings"
|
|
3406
|
+
},
|
|
3407
|
+
aliases: collectAliasHints(["apply_id", "audit_rcd_id"], {}),
|
|
3408
|
+
minimal_example: {
|
|
3409
|
+
apply_id: "50001234",
|
|
3410
|
+
audit_rcd_id: "1111"
|
|
3411
|
+
}
|
|
3412
|
+
},
|
|
2614
3413
|
{
|
|
2615
3414
|
tool: "qf_departments_list",
|
|
2616
3415
|
required: [],
|
|
@@ -2997,6 +3796,7 @@ function normalizeRecordGetInput(raw) {
|
|
|
2997
3796
|
return {
|
|
2998
3797
|
...normalizedObj,
|
|
2999
3798
|
apply_id: coerceNumberLike(normalizedObj.apply_id),
|
|
3799
|
+
app_key: coerceStringLike(normalizedObj.app_key),
|
|
3000
3800
|
max_columns: coerceNumberLike(normalizedObj.max_columns),
|
|
3001
3801
|
select_columns: normalizeSelectorListInput(selectColumns),
|
|
3002
3802
|
output_profile: normalizeOutputProfileInput(normalizedObj.output_profile)
|
|
@@ -3117,6 +3917,7 @@ function normalizeBatchGetInput(raw) {
|
|
|
3117
3917
|
const selectColumns = normalizedObj.select_columns ?? normalizedObj.keep_columns;
|
|
3118
3918
|
return {
|
|
3119
3919
|
...normalizedObj,
|
|
3920
|
+
app_key: coerceStringLike(normalizedObj.app_key),
|
|
3120
3921
|
apply_ids: normalizeIdArrayInput(normalizedObj.apply_ids),
|
|
3121
3922
|
max_columns: coerceNumberLike(normalizedObj.max_columns),
|
|
3122
3923
|
select_columns: normalizeSelectorListInput(selectColumns),
|
|
@@ -4717,6 +5518,7 @@ function buildRecordGetArgsFromQuery(args) {
|
|
|
4717
5518
|
}
|
|
4718
5519
|
return recordGetInputSchema.parse({
|
|
4719
5520
|
apply_id: args.apply_id,
|
|
5521
|
+
app_key: args.app_key,
|
|
4720
5522
|
max_columns: args.max_columns,
|
|
4721
5523
|
select_columns: args.select_columns,
|
|
4722
5524
|
output_profile: args.output_profile
|
|
@@ -4802,6 +5604,13 @@ async function executeQueryPlan(args) {
|
|
|
4802
5604
|
};
|
|
4803
5605
|
}
|
|
4804
5606
|
async function executeRecordsBatchGet(args) {
|
|
5607
|
+
if (!args.app_key) {
|
|
5608
|
+
throw missingRequiredFieldError({
|
|
5609
|
+
field: "app_key",
|
|
5610
|
+
tool: "qf_records_batch_get",
|
|
5611
|
+
fixHint: "Provide app_key, for example: {\"app_key\":\"21b3d559\",\"apply_ids\":[\"...\"],\"select_columns\":[0]}."
|
|
5612
|
+
});
|
|
5613
|
+
}
|
|
4805
5614
|
if (!args.select_columns?.length) {
|
|
4806
5615
|
throw missingRequiredFieldError({
|
|
4807
5616
|
field: "select_columns",
|
|
@@ -4819,25 +5628,23 @@ async function executeRecordsBatchGet(args) {
|
|
|
4819
5628
|
const rows = [];
|
|
4820
5629
|
const missingApplyIds = [];
|
|
4821
5630
|
let metaResponse = null;
|
|
5631
|
+
const { response, records } = await fetchRecordsByApplyIds({
|
|
5632
|
+
appKey: args.app_key,
|
|
5633
|
+
applyIds: requestedApplyIds
|
|
5634
|
+
});
|
|
5635
|
+
metaResponse = buildMeta(response);
|
|
5636
|
+
const byApplyId = new Map(records.map((record) => [String(record.applyId ?? ""), record]));
|
|
4822
5637
|
for (const applyId of requestedApplyIds) {
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
rows.push(buildFlatRowFromAnswers({
|
|
4828
|
-
applyId: record.applyId ?? applyId,
|
|
4829
|
-
answers: asArray(record.answers),
|
|
4830
|
-
selectedColumns: selectedColumnsForRow
|
|
4831
|
-
}));
|
|
4832
|
-
}
|
|
4833
|
-
catch (error) {
|
|
4834
|
-
if (error instanceof QingflowApiError &&
|
|
4835
|
-
(error.httpStatus === 404 || error.errCode === 404)) {
|
|
4836
|
-
missingApplyIds.push(applyId);
|
|
4837
|
-
continue;
|
|
4838
|
-
}
|
|
4839
|
-
throw error;
|
|
5638
|
+
const record = byApplyId.get(String(applyId));
|
|
5639
|
+
if (!record) {
|
|
5640
|
+
missingApplyIds.push(applyId);
|
|
5641
|
+
continue;
|
|
4840
5642
|
}
|
|
5643
|
+
rows.push(buildFlatRowFromAnswers({
|
|
5644
|
+
applyId: record.applyId ?? applyId,
|
|
5645
|
+
answers: asArray(record.answers),
|
|
5646
|
+
selectedColumns: selectedColumnsForRow
|
|
5647
|
+
}));
|
|
4841
5648
|
}
|
|
4842
5649
|
const completeness = {
|
|
4843
5650
|
result_amount: requestedApplyIds.length,
|
|
@@ -5400,10 +6207,50 @@ async function executeRecordGet(args) {
|
|
|
5400
6207
|
}
|
|
5401
6208
|
const outputProfile = resolveOutputProfile(args.output_profile);
|
|
5402
6209
|
const queryId = randomUUID();
|
|
5403
|
-
|
|
5404
|
-
|
|
6210
|
+
let response;
|
|
6211
|
+
let record = null;
|
|
6212
|
+
try {
|
|
6213
|
+
response = await client.getRecord(String(args.apply_id));
|
|
6214
|
+
record = asObject(response.result) ?? {};
|
|
6215
|
+
}
|
|
6216
|
+
catch (error) {
|
|
6217
|
+
const providerError = error instanceof QingflowApiError ? error : null;
|
|
6218
|
+
const shouldFallback = providerError && (providerError.httpStatus === 404 || providerError.errCode === 404 || providerError.errCode === 49304);
|
|
6219
|
+
if (!shouldFallback) {
|
|
6220
|
+
throw error;
|
|
6221
|
+
}
|
|
6222
|
+
if (!args.app_key) {
|
|
6223
|
+
throw new InputValidationError({
|
|
6224
|
+
message: `qf_record_get could not read apply_id \"${String(args.apply_id)}\" through the direct record endpoint`,
|
|
6225
|
+
errorCode: "APP_KEY_REQUIRED_FOR_RECORD_GET",
|
|
6226
|
+
fixHint: "Retry qf_record_get with app_key, or use qf_query/qf_records_batch_get with explicit app_key for this record.",
|
|
6227
|
+
details: {
|
|
6228
|
+
apply_id: String(args.apply_id),
|
|
6229
|
+
provider_err_code: providerError?.errCode ?? null,
|
|
6230
|
+
provider_err_msg: providerError?.errMsg ?? null
|
|
6231
|
+
}
|
|
6232
|
+
});
|
|
6233
|
+
}
|
|
6234
|
+
const fallback = await fetchRecordsByApplyIds({
|
|
6235
|
+
appKey: args.app_key,
|
|
6236
|
+
applyIds: [String(args.apply_id)]
|
|
6237
|
+
});
|
|
6238
|
+
response = fallback.response;
|
|
6239
|
+
record = fallback.records.find((item) => String(item.applyId ?? "") === String(args.apply_id)) ?? null;
|
|
6240
|
+
if (!record) {
|
|
6241
|
+
throw new InputValidationError({
|
|
6242
|
+
message: `Record \"${String(args.apply_id)}\" not found in app \"${args.app_key}\"`,
|
|
6243
|
+
errorCode: "RECORD_NOT_FOUND",
|
|
6244
|
+
fixHint: "Confirm apply_id and app_key, or fetch the row via qf_records_list/qf_query first.",
|
|
6245
|
+
details: {
|
|
6246
|
+
apply_id: String(args.apply_id),
|
|
6247
|
+
app_key: args.app_key
|
|
6248
|
+
}
|
|
6249
|
+
});
|
|
6250
|
+
}
|
|
6251
|
+
}
|
|
5405
6252
|
const projection = projectAnswersForOutput({
|
|
5406
|
-
answers: asArray(record
|
|
6253
|
+
answers: asArray(record?.answers),
|
|
5407
6254
|
maxColumns: args.max_columns,
|
|
5408
6255
|
selectColumns: args.select_columns
|
|
5409
6256
|
});
|
|
@@ -5411,8 +6258,8 @@ async function executeRecordGet(args) {
|
|
|
5411
6258
|
? (projection.selectedColumns ?? []).slice(0, args.max_columns)
|
|
5412
6259
|
: projection.selectedColumns ?? [];
|
|
5413
6260
|
const row = buildFlatRowFromAnswers({
|
|
5414
|
-
applyId: record
|
|
5415
|
-
answers: asArray(record
|
|
6261
|
+
applyId: record?.applyId ?? null,
|
|
6262
|
+
answers: asArray(record?.answers),
|
|
5416
6263
|
selectedColumns: selectedColumnsForRow
|
|
5417
6264
|
});
|
|
5418
6265
|
const completeness = {
|
|
@@ -5437,7 +6284,7 @@ async function executeRecordGet(args) {
|
|
|
5437
6284
|
payload: {
|
|
5438
6285
|
ok: true,
|
|
5439
6286
|
data: {
|
|
5440
|
-
apply_id: record
|
|
6287
|
+
apply_id: record?.applyId ?? null,
|
|
5441
6288
|
row,
|
|
5442
6289
|
applied_limits: {
|
|
5443
6290
|
column_cap: args.max_columns ?? null,
|