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.
- package/README.md +111 -156
- package/dist/server.js +893 -623
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
# Qingflow MCP (CRUD)
|
|
2
2
|
|
|
3
|
-
This MCP server
|
|
3
|
+
This MCP server exposes a canonical, agent-native public surface:
|
|
4
4
|
|
|
5
|
-
- `
|
|
5
|
+
- `qf_tool_spec_get`
|
|
6
6
|
- `qf_form_get`
|
|
7
7
|
- `qf_field_resolve`
|
|
8
8
|
- `qf_value_probe`
|
|
9
|
-
- `
|
|
10
|
-
- `
|
|
11
|
-
- `
|
|
12
|
-
- `
|
|
13
|
-
- `
|
|
14
|
-
- `
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
#
|
|
94
|
-
qingflow-mcp cli call
|
|
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
|
-
#
|
|
97
|
-
|
|
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.
|
|
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
|
-
##
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
1. `
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
"
|
|
253
|
-
"
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
197
|
+
### Execute Example
|
|
267
198
|
|
|
268
199
|
```json
|
|
269
200
|
{
|
|
270
|
-
"
|
|
271
|
-
"max_columns": 2,
|
|
272
|
-
"select_columns": [1, "客户名称"],
|
|
273
|
-
"output_profile": "compact"
|
|
201
|
+
"plan_id": "plan_xxx"
|
|
274
202
|
}
|
|
275
203
|
```
|
|
276
204
|
|
|
277
|
-
|
|
205
|
+
Rules:
|
|
278
206
|
|
|
279
|
-
|
|
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
|
-
|
|
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
|
-
|
|
214
|
+
Aggregate business summaries use one canonical count contract:
|
|
295
215
|
|
|
296
216
|
```json
|
|
297
217
|
{
|
|
298
|
-
"
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
310
|
-
"
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
269
|
+
Common codes:
|
|
320
270
|
|
|
321
|
-
|
|
322
|
-
|
|
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
|
|