qingflow-mcp 0.2.3 → 0.2.5
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 +16 -0
- package/dist/server.js +66 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -105,6 +105,12 @@ MCP client config example:
|
|
|
105
105
|
|
|
106
106
|
## List Query Tips
|
|
107
107
|
|
|
108
|
+
Strict mode (`qf_records_list`):
|
|
109
|
+
|
|
110
|
+
1. `select_columns` is required.
|
|
111
|
+
2. `include_answers=false` is not allowed.
|
|
112
|
+
3. Output `items[].answers` contains only selected columns, not full answers.
|
|
113
|
+
|
|
108
114
|
1. For `qf_records_list.sort[].que_id`, use a real field `que_id` (numeric) or exact field title from `qf_form_get`.
|
|
109
115
|
2. Avoid aliases like `create_time`; Qingflow often rejects them.
|
|
110
116
|
3. Use `max_rows` (or `max_items`) to cap returned rows.
|
|
@@ -126,6 +132,16 @@ Example:
|
|
|
126
132
|
}
|
|
127
133
|
```
|
|
128
134
|
|
|
135
|
+
For single record details (`qf_record_get`), the same column controls are supported:
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"apply_id": "497600278750478338",
|
|
140
|
+
"max_columns": 5,
|
|
141
|
+
"select_columns": [1, "客户名称"]
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
129
145
|
Optional env vars:
|
|
130
146
|
|
|
131
147
|
```bash
|
package/dist/server.js
CHANGED
|
@@ -147,7 +147,8 @@ const formOutputSchema = z.object({
|
|
|
147
147
|
}),
|
|
148
148
|
meta: apiMetaSchema
|
|
149
149
|
});
|
|
150
|
-
const listInputSchema = z
|
|
150
|
+
const listInputSchema = z
|
|
151
|
+
.object({
|
|
151
152
|
app_key: z.string().min(1),
|
|
152
153
|
user_id: z.string().min(1).optional(),
|
|
153
154
|
page_num: z.number().int().positive().optional(),
|
|
@@ -193,12 +194,12 @@ const listInputSchema = z.object({
|
|
|
193
194
|
max_rows: z.number().int().positive().max(200).optional(),
|
|
194
195
|
max_items: z.number().int().positive().max(200).optional(),
|
|
195
196
|
max_columns: z.number().int().positive().max(200).optional(),
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
.min(1)
|
|
199
|
-
.max(200)
|
|
200
|
-
.optional(),
|
|
197
|
+
// Strict mode: callers must explicitly choose columns.
|
|
198
|
+
select_columns: z.array(z.union([z.string().min(1), z.number().int()])).min(1).max(200),
|
|
201
199
|
include_answers: z.boolean().optional()
|
|
200
|
+
})
|
|
201
|
+
.refine((value) => value.include_answers !== false, {
|
|
202
|
+
message: "include_answers=false is not allowed in strict column mode"
|
|
202
203
|
});
|
|
203
204
|
const listOutputSchema = z.object({
|
|
204
205
|
ok: z.literal(true),
|
|
@@ -216,21 +217,33 @@ const listOutputSchema = z.object({
|
|
|
216
217
|
include_answers: z.boolean(),
|
|
217
218
|
row_cap: z.number().int().nonnegative(),
|
|
218
219
|
column_cap: z.number().int().positive().nullable(),
|
|
219
|
-
selected_columns: z.array(z.string())
|
|
220
|
+
selected_columns: z.array(z.string())
|
|
220
221
|
})
|
|
221
222
|
.optional()
|
|
222
223
|
}),
|
|
223
224
|
meta: apiMetaSchema
|
|
224
225
|
});
|
|
225
226
|
const recordGetInputSchema = z.object({
|
|
226
|
-
apply_id: z.union([z.string().min(1), z.number().int()])
|
|
227
|
+
apply_id: z.union([z.string().min(1), z.number().int()]),
|
|
228
|
+
max_columns: z.number().int().positive().max(200).optional(),
|
|
229
|
+
select_columns: z
|
|
230
|
+
.array(z.union([z.string().min(1), z.number().int()]))
|
|
231
|
+
.min(1)
|
|
232
|
+
.max(200)
|
|
233
|
+
.optional()
|
|
227
234
|
});
|
|
228
235
|
const recordGetOutputSchema = z.object({
|
|
229
236
|
ok: z.literal(true),
|
|
230
237
|
data: z.object({
|
|
231
238
|
apply_id: z.union([z.string(), z.number(), z.null()]),
|
|
232
239
|
answer_count: z.number().int().nonnegative(),
|
|
233
|
-
record: z.unknown()
|
|
240
|
+
record: z.unknown(),
|
|
241
|
+
applied_limits: z
|
|
242
|
+
.object({
|
|
243
|
+
column_cap: z.number().int().positive().nullable(),
|
|
244
|
+
selected_columns: z.array(z.string()).nullable()
|
|
245
|
+
})
|
|
246
|
+
.optional()
|
|
234
247
|
}),
|
|
235
248
|
meta: apiMetaSchema
|
|
236
249
|
});
|
|
@@ -380,7 +393,7 @@ server.registerTool("qf_records_list", {
|
|
|
380
393
|
const pageNum = args.page_num ?? 1;
|
|
381
394
|
const pageSize = args.page_size ?? DEFAULT_PAGE_SIZE;
|
|
382
395
|
const normalizedSort = await normalizeListSort(args.sort, args.app_key, args.user_id);
|
|
383
|
-
const includeAnswers =
|
|
396
|
+
const includeAnswers = true;
|
|
384
397
|
const payload = buildListPayload({
|
|
385
398
|
pageNum,
|
|
386
399
|
pageSize,
|
|
@@ -410,6 +423,11 @@ server.registerTool("qf_records_list", {
|
|
|
410
423
|
maxColumns: args.max_columns,
|
|
411
424
|
selectColumns: args.select_columns
|
|
412
425
|
});
|
|
426
|
+
if (items.length > 0 && columnProjection.matchedAnswersCount === 0) {
|
|
427
|
+
throw new Error(`No answers matched select_columns (${args.select_columns
|
|
428
|
+
.map((item) => String(item))
|
|
429
|
+
.join(", ")}). Check que_id/title from qf_form_get.`);
|
|
430
|
+
}
|
|
413
431
|
const fitted = fitListItemsWithinSize({
|
|
414
432
|
items: columnProjection.items,
|
|
415
433
|
limitBytes: MAX_LIST_ITEMS_BYTES
|
|
@@ -457,13 +475,26 @@ server.registerTool("qf_record_get", {
|
|
|
457
475
|
try {
|
|
458
476
|
const response = await client.getRecord(String(args.apply_id));
|
|
459
477
|
const record = asObject(response.result) ?? {};
|
|
460
|
-
const
|
|
478
|
+
const projection = projectAnswersForOutput({
|
|
479
|
+
answers: asArray(record.answers),
|
|
480
|
+
maxColumns: args.max_columns,
|
|
481
|
+
selectColumns: args.select_columns
|
|
482
|
+
});
|
|
483
|
+
const projectedRecord = {
|
|
484
|
+
...record,
|
|
485
|
+
answers: projection.answers
|
|
486
|
+
};
|
|
487
|
+
const answerCount = projection.answers.length;
|
|
461
488
|
return okResult({
|
|
462
489
|
ok: true,
|
|
463
490
|
data: {
|
|
464
491
|
apply_id: record.applyId ?? null,
|
|
465
492
|
answer_count: answerCount,
|
|
466
|
-
record:
|
|
493
|
+
record: projectedRecord,
|
|
494
|
+
applied_limits: {
|
|
495
|
+
column_cap: args.max_columns ?? null,
|
|
496
|
+
selected_columns: projection.selectedColumns
|
|
497
|
+
}
|
|
467
498
|
},
|
|
468
499
|
meta: buildMeta(response)
|
|
469
500
|
}, `Fetched record ${String(args.apply_id)}`);
|
|
@@ -901,12 +932,17 @@ function projectRecordItemsColumns(params) {
|
|
|
901
932
|
return {
|
|
902
933
|
items: params.items,
|
|
903
934
|
reason: null,
|
|
904
|
-
selectedColumns:
|
|
935
|
+
selectedColumns: [],
|
|
936
|
+
matchedAnswersCount: 0
|
|
905
937
|
};
|
|
906
938
|
}
|
|
907
939
|
const normalizedSelectors = normalizeColumnSelectors(params.selectColumns);
|
|
940
|
+
if (normalizedSelectors.length === 0) {
|
|
941
|
+
throw new Error("select_columns must contain at least one non-empty column identifier");
|
|
942
|
+
}
|
|
908
943
|
const selectorSet = new Set(normalizedSelectors.map((item) => normalizeColumnSelector(item)));
|
|
909
944
|
let columnCapped = false;
|
|
945
|
+
let matchedAnswersCount = 0;
|
|
910
946
|
const projectedItems = params.items.map((item) => {
|
|
911
947
|
const answers = asArray(item.answers);
|
|
912
948
|
let projected = answers;
|
|
@@ -917,6 +953,7 @@ function projectRecordItemsColumns(params) {
|
|
|
917
953
|
projected = projected.slice(0, params.maxColumns);
|
|
918
954
|
columnCapped = true;
|
|
919
955
|
}
|
|
956
|
+
matchedAnswersCount += projected.length;
|
|
920
957
|
return {
|
|
921
958
|
...item,
|
|
922
959
|
answers: projected
|
|
@@ -928,6 +965,22 @@ function projectRecordItemsColumns(params) {
|
|
|
928
965
|
return {
|
|
929
966
|
items: projectedItems,
|
|
930
967
|
reason,
|
|
968
|
+
selectedColumns: normalizedSelectors,
|
|
969
|
+
matchedAnswersCount
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
function projectAnswersForOutput(params) {
|
|
973
|
+
const normalizedSelectors = normalizeColumnSelectors(params.selectColumns);
|
|
974
|
+
const selectorSet = new Set(normalizedSelectors.map((item) => normalizeColumnSelector(item)));
|
|
975
|
+
let projected = params.answers;
|
|
976
|
+
if (selectorSet.size > 0) {
|
|
977
|
+
projected = projected.filter((answer) => answerMatchesAnySelector(answer, selectorSet));
|
|
978
|
+
}
|
|
979
|
+
if (params.maxColumns !== undefined && projected.length > params.maxColumns) {
|
|
980
|
+
projected = projected.slice(0, params.maxColumns);
|
|
981
|
+
}
|
|
982
|
+
return {
|
|
983
|
+
answers: projected,
|
|
931
984
|
selectedColumns: normalizedSelectors.length > 0 ? normalizedSelectors : null
|
|
932
985
|
};
|
|
933
986
|
}
|