qingflow-mcp 0.2.4 → 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 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.
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,7 +217,7 @@ 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
  }),
@@ -392,7 +393,7 @@ server.registerTool("qf_records_list", {
392
393
  const pageNum = args.page_num ?? 1;
393
394
  const pageSize = args.page_size ?? DEFAULT_PAGE_SIZE;
394
395
  const normalizedSort = await normalizeListSort(args.sort, args.app_key, args.user_id);
395
- const includeAnswers = Boolean(args.include_answers || (args.select_columns && args.select_columns.length > 0));
396
+ const includeAnswers = true;
396
397
  const payload = buildListPayload({
397
398
  pageNum,
398
399
  pageSize,
@@ -422,6 +423,11 @@ server.registerTool("qf_records_list", {
422
423
  maxColumns: args.max_columns,
423
424
  selectColumns: args.select_columns
424
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
+ }
425
431
  const fitted = fitListItemsWithinSize({
426
432
  items: columnProjection.items,
427
433
  limitBytes: MAX_LIST_ITEMS_BYTES
@@ -926,12 +932,17 @@ function projectRecordItemsColumns(params) {
926
932
  return {
927
933
  items: params.items,
928
934
  reason: null,
929
- selectedColumns: null
935
+ selectedColumns: [],
936
+ matchedAnswersCount: 0
930
937
  };
931
938
  }
932
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
+ }
933
943
  const selectorSet = new Set(normalizedSelectors.map((item) => normalizeColumnSelector(item)));
934
944
  let columnCapped = false;
945
+ let matchedAnswersCount = 0;
935
946
  const projectedItems = params.items.map((item) => {
936
947
  const answers = asArray(item.answers);
937
948
  let projected = answers;
@@ -942,6 +953,7 @@ function projectRecordItemsColumns(params) {
942
953
  projected = projected.slice(0, params.maxColumns);
943
954
  columnCapped = true;
944
955
  }
956
+ matchedAnswersCount += projected.length;
945
957
  return {
946
958
  ...item,
947
959
  answers: projected
@@ -953,7 +965,8 @@ function projectRecordItemsColumns(params) {
953
965
  return {
954
966
  items: projectedItems,
955
967
  reason,
956
- selectedColumns: normalizedSelectors.length > 0 ? normalizedSelectors : null
968
+ selectedColumns: normalizedSelectors,
969
+ matchedAnswersCount
957
970
  };
958
971
  }
959
972
  function projectAnswersForOutput(params) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qingflow-mcp",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "type": "module",