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.
Files changed (3) hide show
  1. package/README.md +16 -0
  2. package/dist/server.js +66 -13
  3. 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.object({
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
- select_columns: z
197
- .array(z.union([z.string().min(1), z.number().int()]))
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()).nullable()
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 = Boolean(args.include_answers || (args.select_columns && args.select_columns.length > 0));
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 answerCount = asArray(record.answers).length;
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: response.result
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: null
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qingflow-mcp",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "type": "module",