qingflow-mcp 0.5.0 → 0.7.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.
Files changed (3) hide show
  1. package/README.md +111 -156
  2. package/dist/server.js +893 -623
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,22 +1,19 @@
1
1
  # Qingflow MCP (CRUD)
2
2
 
3
- This MCP server wraps Qingflow OpenAPI for:
3
+ This MCP server exposes a canonical, agent-native public surface:
4
4
 
5
- - `qf_apps_list`
5
+ - `qf_tool_spec_get`
6
6
  - `qf_form_get`
7
7
  - `qf_field_resolve`
8
8
  - `qf_value_probe`
9
- - `qf_query_plan`
10
- - `qf_records_list`
11
- - `qf_record_get`
12
- - `qf_records_batch_get`
13
- - `qf_export_csv`
14
- - `qf_export_json`
15
- - `qf_query` (unified read entry: list / record / summary)
16
- - `qf_records_aggregate` (deterministic grouped metrics)
17
- - `qf_record_create`
18
- - `qf_record_update`
19
- - `qf_operation_get`
9
+ - `qf.query.plan`
10
+ - `qf.query.rows`
11
+ - `qf.query.record`
12
+ - `qf.query.aggregate`
13
+ - `qf.query.export`
14
+ - `qf.records.mutate`
15
+
16
+ Legacy tools still exist internally for compatibility logic, but they are no longer advertised via `listTools()`.
20
17
 
21
18
  It intentionally excludes delete for now.
22
19
 
@@ -90,12 +87,19 @@ qingflow-mcp cli tools
90
87
  # machine-readable tool list
91
88
  qingflow-mcp cli tools --json
92
89
 
93
- # call one tool with JSON args
94
- qingflow-mcp cli call qf_apps_list --args '{"limit":5}'
90
+ # canonical plan -> execute
91
+ qingflow-mcp cli call qf.query.plan --args '{
92
+ "kind":"rows",
93
+ "query":{
94
+ "app_key":"your_app_key",
95
+ "select":[1001,1002],
96
+ "where":[{"field":1003,"op":"between","from":"2026-01-01","to":"2026-01-31"}],
97
+ "limit":20
98
+ }
99
+ }'
95
100
 
96
- # call from stdin
97
- echo '{"app_key":"your_app_key","mode":"all","select_columns":[1001]}' \
98
- | qingflow-mcp cli call qf_query
101
+ # then execute with returned plan_id
102
+ qingflow-mcp cli call qf.query.rows --args '{"plan_id":"plan_xxx"}'
99
103
  ```
100
104
 
101
105
  ## CLI Install
@@ -109,7 +113,7 @@ npm i -g git+https://github.com/853046310/qingflow-mcp.git
109
113
  Install from npm (pinned version):
110
114
 
111
115
  ```bash
112
- npm i -g qingflow-mcp@0.5.0
116
+ npm i -g qingflow-mcp@0.7.0
113
117
  ```
114
118
 
115
119
  Or one-click installer:
@@ -156,171 +160,122 @@ Full calling contract (Chinese):
156
160
  - [MCP 调用规范](./docs/MCP_CALLING_SPEC.md)
157
161
  - [vNext Agent-Native 设计稿](./docs/VNEXT_AGENT_NATIVE_DESIGN.md)
158
162
 
159
- ## Unified Query (`qf_query`)
160
-
161
- `qf_query` is the recommended read entry for agents.
162
-
163
- 1. `query_mode=auto`:
164
- - if `apply_id` is set, route to single-record query.
165
- - if summary params are set (`amount_column` / `time_range` / `stat_policy` / `scan_max_pages`), route to aggregate-backed summary query.
166
- - otherwise route to list query.
167
- 2. `query_mode=list|record|summary` forces explicit behavior.
168
- 3. In `list` mode, `time_range` is translated to list filters when `from` or `to` is provided.
169
- 4. In `list` mode, `select_columns` is required.
170
- 5. In `list` mode, row cap defaults to 200 when `max_rows` and `max_items` are omitted.
171
- 6. In `record` mode, `select_columns` is required.
172
- 7. In `summary` mode, `select_columns` is required (`max_rows` defaults to 200 when omitted).
173
-
174
- Summary mode output:
175
-
176
- 1. `qf_query(summary)` is a compatibility wrapper over the aggregate core.
177
- 2. `summary`: aggregated stats (`counts.effective_record_count`, `counts.final_record_count`, `total_amount`, `by_day`, `missing_count`).
178
- 3. `rows`: strict sample rows (only requested `select_columns`).
179
- 4. `meta`: field mapping, filter scope, stat policy, execution limits (`output_profile=verbose` only).
180
-
181
- Return shape:
182
-
183
- 1. success: structured payload `{ "ok": true, "data": ... }` (`meta` only in `output_profile=verbose`)
184
- 2. failure: MCP `isError=true`, and text content is JSON payload like `{ "ok": false, "message": ..., ... }`
185
- 3. incomplete strict queries fail with `{ "code": "NEED_MORE_DATA", "status": "need_more_data", ... }`
186
-
187
- Deterministic read protocol (list/summary/aggregate):
188
-
189
- 1. output profile:
190
- - default `output_profile=compact`: return core data only (`rows/row/groups/summary` + `next_page_token`)
191
- - `output_profile=verbose`: include full contract (`completeness` + `evidence` + `meta`)
192
- - exception: aggregate-style outputs (`qf_query(summary)` compatibility wrapper and `qf_records_aggregate`) always return `completeness`, even in `compact`, so agents can block on incomplete statistics
193
- 2. when `output_profile=verbose`, `completeness` fields are:
194
- - `result_amount`
195
- - `returned_items`
196
- - `fetched_pages`
197
- - `requested_pages`
198
- - `actual_scanned_pages`
199
- - `has_more`
200
- - `next_page_token`
201
- - `is_complete`
202
- - `partial`
203
- - `omitted_items`
204
- - `omitted_chars`
205
- 3. when `output_profile=verbose`, `evidence` fields are:
206
- - `query_id`
207
- - `app_key`
208
- - `filters`
209
- - `selected_columns`
210
- - `time_range`
211
- - `source_pages`
212
- 4. `strict_full=true` makes incomplete results fail fast with `NEED_MORE_DATA`.
213
- - for `qf_query(summary)`, `strict_full` enforces raw source scan completeness; sample-row truncation does not change aggregate completeness
214
- 5. Error payloads expose `error_code` and `fix_hint` for actionable retries.
215
- 6. Public MCP `inputSchema` is strict:
216
- - numbers must be native JSON numbers
217
- - arrays must be native JSON arrays
218
- - objects must be native JSON objects
219
- - booleans must be native JSON booleans
220
- - unknown fields are rejected by the MCP boundary
221
- 7. Use `qf_query_plan` as the only preflight tool when the agent is unsure about arguments. It can normalize loose/model-shaped inputs before a real query is issued, but runtime aliases like `from`/`to`/`dateFrom`/`dateTo`/`searchKey`/`searchKeys` are rejected.
222
-
223
- For `qf_query(summary)` and `qf_records_aggregate`, read `data.summary.completeness` / `data.completeness` before concluding:
224
-
225
- 1. `raw_scan_complete=false`: source data is not fully scanned, do not produce a final conclusion.
226
- 2. `scan_limit_hit=true`: query stopped because scan budget was hit.
227
- 3. `output_page_complete=false`: source may be complete, but aggregate output itself was truncated by output limits such as `max_groups`.
228
- 4. `raw_next_page_token`: use this token to continue raw scan pagination (`next_page_token` remains as a backward-compatible alias). For `qf_query(summary)` / `qf_records_aggregate`, the token carries cumulative state, so keep query arguments unchanged when resuming.
229
-
230
- ## List Query Tips
231
-
232
- Strict mode (`qf_records_list`):
233
-
234
- 1. `select_columns` is required.
235
- 2. `include_answers=false` is not allowed.
236
- 3. Output is flat `rows[]` (no raw `answers` payload).
237
-
238
- 1. For `qf_records_list.sort[].que_id`, use a real field `que_id` (numeric) or exact field title from `qf_form_get`.
239
- 2. Avoid aliases like `create_time`; Qingflow often rejects them.
240
- 3. Use `max_rows` (or `max_items`) to cap returned rows. Default row cap is 200.
241
- 4. Use `max_columns` to cap returned columns per row.
242
- 5. Use `select_columns` to return only specific columns (supports `que_id` or exact field title).
243
- 6. The server may still trim by response-size guardrail (`QINGFLOW_LIST_MAX_ITEMS_BYTES`) when payload is too large.
244
- 7. Use `requested_pages` and `scan_max_pages` for deterministic page scan.
245
- 8. Continue with `page_token` from previous `next_page_token`.
246
- 9. Column limits: `select_columns <= 2`, `max_columns <= 2`.
247
-
248
- Example:
163
+ ## Canonical Usage
164
+
165
+ Public agent flow is now:
166
+
167
+ 1. `qf_form_get` / `qf_field_resolve` when field mapping is unclear
168
+ 2. `qf_value_probe` when field value candidates are unclear
169
+ 3. `qf.query.plan`
170
+ 4. Execute the returned `plan_id` with:
171
+ - `qf.query.rows`
172
+ - `qf.query.record`
173
+ - `qf.query.aggregate`
174
+ - `qf.query.export`
175
+ - `qf.records.mutate`
176
+
177
+ ### Planner Example
249
178
 
250
179
  ```json
251
180
  {
252
- "app_key": "your_app_key",
253
- "mode": "all",
254
- "page_size": 50,
255
- "requested_pages": 1,
256
- "scan_max_pages": 1,
257
- "include_answers": true,
258
- "max_rows": 10,
259
- "max_columns": 2,
260
- "select_columns": [1, "客户名称"],
261
- "output_profile": "compact",
262
- "strict_full": false
181
+ "kind": "aggregate",
182
+ "query": {
183
+ "app_key": "your_app_key",
184
+ "where": [
185
+ { "field": 1003, "op": "between", "from": "2026-01-01", "to": "2026-01-31" }
186
+ ],
187
+ "group_by": [1003],
188
+ "metrics": [
189
+ { "op": "count" },
190
+ { "column": 1002, "op": "sum" }
191
+ ],
192
+ "strict_full": true
193
+ }
263
194
  }
264
195
  ```
265
196
 
266
- For single record details (`qf_record_get`), the same column controls are supported:
197
+ ### Execute Example
267
198
 
268
199
  ```json
269
200
  {
270
- "apply_id": "497600278750478338",
271
- "max_columns": 2,
272
- "select_columns": [1, "客户名称"],
273
- "output_profile": "compact"
201
+ "plan_id": "plan_xxx"
274
202
  }
275
203
  ```
276
204
 
277
- `qf_record_get` requires `select_columns`.
205
+ Rules:
278
206
 
279
- Aggregate example (`qf_records_aggregate`):
207
+ 1. Public execute tools require `plan_id`.
208
+ 2. Optional `query` / `action` echo is only used for drift checking.
209
+ 3. If execute input drifts from the planned canonical query, the server returns `PLAN_DRIFT`.
210
+ 4. Public filtering uses canonical `where[]`; legacy `filters` are no longer part of the public contract.
280
211
 
281
- ```json
282
- {
283
- "app_key": "your_app_key",
284
- "group_by": ["归属部门", "归属销售"],
285
- "amount_columns": ["报价总金额"],
286
- "metrics": ["count", "sum", "avg", "min", "max"],
287
- "time_bucket": "day",
288
- "requested_pages": 10,
289
- "scan_max_pages": 10,
290
- "strict_full": true
291
- }
292
- ```
212
+ ### Aggregate Business Counts
293
213
 
294
- Batch detail example (`qf_records_batch_get`):
214
+ Aggregate business summaries use one canonical count contract:
295
215
 
296
216
  ```json
297
217
  {
298
- "app_key": "your_app_key",
299
- "apply_ids": ["497600278750478338", "497600278750478339"],
300
- "select_columns": [1, "客户名称"],
301
- "max_columns": 2
218
+ "summary": {
219
+ "counts": {
220
+ "source_record_count": 370,
221
+ "group_assignment_count": 405,
222
+ "metric_nonnull_record_count": 395
223
+ },
224
+ "primary_metric_total": 12272931.75,
225
+ "primary_metric_missing_count": 10
226
+ }
302
227
  }
303
228
  ```
304
229
 
305
- Export example (`qf_export_json`):
230
+ Default answer for “多少单/多少条” must read `summary.counts.source_record_count`.
231
+
232
+ ### Completeness
233
+
234
+ Canonical `completeness` is technical-only:
235
+
236
+ - `is_complete`
237
+ - `raw_scan_complete`
238
+ - `scan_limit_hit`
239
+ - `fetched_pages`
240
+ - `requested_pages`
241
+ - `actual_scanned_pages`
242
+ - `scanned_pages`
243
+ - `scan_limit`
244
+ - `has_more`
245
+ - `next_page_token`
246
+ - `stop_reason`
247
+ - `output_truncated`
248
+ - `omitted_items`
249
+ - `omitted_chars`
250
+
251
+ When `strict_full=true`, any incomplete result fails with `INCOMPLETE_RESULT`.
252
+
253
+ ### Error Protocol
254
+
255
+ Failures return structured JSON with a machine-readable `error.code`, for example:
306
256
 
307
257
  ```json
308
258
  {
309
- "app_key": "your_app_key",
310
- "mode": "all",
311
- "page_size": 50,
312
- "requested_pages": 5,
313
- "max_rows": 500,
314
- "select_columns": [1, "客户名称"],
315
- "file_name": "报价单导出.json"
259
+ "ok": false,
260
+ "error": {
261
+ "code": "PLAN_REQUIRED",
262
+ "message": "...",
263
+ "fix_hint": "...",
264
+ "retryable": true
265
+ }
316
266
  }
317
267
  ```
318
268
 
319
- Optional env vars:
269
+ Common codes:
320
270
 
321
- ```bash
322
- export QINGFLOW_LIST_MAX_ITEMS_BYTES=400000
323
- ```
271
+ - `PLAN_REQUIRED`
272
+ - `PLAN_NOT_READY`
273
+ - `PLAN_DRIFT`
274
+ - `FORBIDDEN_RUNTIME_ALIAS`
275
+ - `VALIDATION_ERROR`
276
+ - `INCOMPLETE_RESULT`
277
+ - `UPSTREAM_TIMEOUT`
278
+ - `UPSTREAM_API_ERROR`
324
279
 
325
280
  ## Troubleshooting
326
281