@qingflow-tech/qingflow-app-user-mcp 1.0.2 → 1.0.3
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 +2 -2
- package/docs/local-agent-install.md +9 -3
- package/npm/lib/runtime.mjs +10 -3
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/skills/qingflow-app-user/SKILL.md +21 -12
- package/skills/qingflow-app-user/references/data-gotchas.md +1 -1
- package/skills/qingflow-app-user/references/public-surface-sync.md +70 -0
- package/skills/qingflow-app-user/references/record-patterns.md +1 -1
- package/skills/qingflow-record-analysis/SKILL.md +44 -2
- package/skills/qingflow-record-insert/SKILL.md +3 -0
- package/skills/qingflow-record-update/SKILL.md +3 -0
- package/skills/qingflow-task-ops/SKILL.md +31 -10
- package/src/qingflow_mcp/__init__.py +33 -1
- package/src/qingflow_mcp/builder_facade/models.py +14 -4
- package/src/qingflow_mcp/builder_facade/service.py +1582 -124
- package/src/qingflow_mcp/cli/commands/auth.py +63 -0
- package/src/qingflow_mcp/cli/commands/builder.py +4 -3
- package/src/qingflow_mcp/cli/commands/record.py +5 -5
- package/src/qingflow_mcp/cli/commands/task.py +74 -22
- package/src/qingflow_mcp/cli/commands/workspace.py +22 -0
- package/src/qingflow_mcp/cli/formatters.py +287 -48
- package/src/qingflow_mcp/cli/main.py +6 -1
- package/src/qingflow_mcp/cli/qingflow_login.py +116 -0
- package/src/qingflow_mcp/config.py +1 -1
- package/src/qingflow_mcp/errors.py +2 -2
- package/src/qingflow_mcp/id_utils.py +49 -0
- package/src/qingflow_mcp/public_surface.py +11 -1
- package/src/qingflow_mcp/response_trim.py +380 -9
- package/src/qingflow_mcp/server.py +4 -0
- package/src/qingflow_mcp/server_app_builder.py +11 -1
- package/src/qingflow_mcp/server_app_user.py +24 -0
- package/src/qingflow_mcp/session_store.py +69 -15
- package/src/qingflow_mcp/solution/compiler/form_compiler.py +2 -2
- package/src/qingflow_mcp/solution/executor.py +2 -2
- package/src/qingflow_mcp/tools/ai_builder_tools.py +48 -18
- package/src/qingflow_mcp/tools/app_tools.py +1 -0
- package/src/qingflow_mcp/tools/auth_tools.py +217 -9
- package/src/qingflow_mcp/tools/base.py +6 -2
- package/src/qingflow_mcp/tools/code_block_tools.py +2 -2
- package/src/qingflow_mcp/tools/import_tools.py +36 -2
- package/src/qingflow_mcp/tools/record_tools.py +410 -156
- package/src/qingflow_mcp/tools/resource_read_tools.py +114 -32
- package/src/qingflow_mcp/tools/task_context_tools.py +899 -141
- package/src/qingflow_mcp/tools/workspace_tools.py +141 -0
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @qingflow-tech/qingflow-app-user-mcp@1.0.
|
|
6
|
+
npm install @qingflow-tech/qingflow-app-user-mcp@1.0.3
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @qingflow-tech/qingflow-app-user-mcp@1.0.
|
|
12
|
+
npx -y -p @qingflow-tech/qingflow-app-user-mcp@1.0.3 qingflow-app-user-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
|
@@ -20,6 +20,12 @@
|
|
|
20
20
|
|
|
21
21
|
`auth_use_credential` 是本地唯一鉴权主路径。
|
|
22
22
|
|
|
23
|
+
补充说明:
|
|
24
|
+
|
|
25
|
+
- 对 stdio MCP 来说,主路径仍然只有 `auth_use_credential`。
|
|
26
|
+
- 如果你是在终端里直接使用 `qingflow` CLI,可以额外使用 `qingflow auth login` 作为“人类登录”入口;默认会提示轻流邮箱和隐藏密码,拿到 `token` 后建立本地 CLI 会话。
|
|
27
|
+
- 也就是说,这次新增的是 CLI 的登录入口,不是给 MCP 增加第二套会话模型。
|
|
28
|
+
|
|
23
29
|
## npm 安装器适用场景
|
|
24
30
|
|
|
25
31
|
适合这类本地 agent / gateway:
|
|
@@ -211,7 +217,7 @@ qingflow-app-builder-mcp
|
|
|
211
217
|
"command": "npx",
|
|
212
218
|
"args": [
|
|
213
219
|
"-y",
|
|
214
|
-
"@
|
|
220
|
+
"@josephyan/qingflow-app-user-mcp"
|
|
215
221
|
],
|
|
216
222
|
"env": {
|
|
217
223
|
"QINGFLOW_MCP_DEFAULT_BASE_URL": "https://qingflow.com/api",
|
|
@@ -224,7 +230,7 @@ qingflow-app-builder-mcp
|
|
|
224
230
|
"command": "npx",
|
|
225
231
|
"args": [
|
|
226
232
|
"-y",
|
|
227
|
-
"@
|
|
233
|
+
"@josephyan/qingflow-app-builder-mcp"
|
|
228
234
|
],
|
|
229
235
|
"env": {
|
|
230
236
|
"QINGFLOW_MCP_DEFAULT_BASE_URL": "https://qingflow.com/api",
|
|
@@ -266,7 +272,7 @@ npm install
|
|
|
266
272
|
|
|
267
273
|
如果 MCP 客户端一调用工具就报 `Transport closed`,优先检查这几件事:
|
|
268
274
|
|
|
269
|
-
1. 不要混用不同版本的 `@
|
|
275
|
+
1. 不要混用不同版本的 `@josephyan/qingflow-cli`、`@josephyan/qingflow-app-user-mcp`、`@josephyan/qingflow-app-builder-mcp`
|
|
270
276
|
2. 删除安装目录下的 `.npm-python`
|
|
271
277
|
3. 重新执行 `npm install` 或重新安装对应 tgz/npm 包
|
|
272
278
|
4. 再启动 MCP 客户端
|
package/npm/lib/runtime.mjs
CHANGED
|
@@ -287,7 +287,12 @@ function forwardSignal(child, signal) {
|
|
|
287
287
|
});
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
-
export function spawnServer(
|
|
290
|
+
export function spawnServer(
|
|
291
|
+
packageRoot,
|
|
292
|
+
args,
|
|
293
|
+
commandName = "qingflow-mcp",
|
|
294
|
+
{ allowRuntimeBootstrap = false, stdio = "proxy" } = {},
|
|
295
|
+
) {
|
|
291
296
|
let runtime = inspectPythonEnv(packageRoot, commandName);
|
|
292
297
|
let serverCommand = runtime.serverCommand;
|
|
293
298
|
|
|
@@ -315,12 +320,14 @@ export function spawnServer(packageRoot, args, commandName = "qingflow-mcp", { a
|
|
|
315
320
|
}
|
|
316
321
|
|
|
317
322
|
const child = spawn(serverCommand, args, {
|
|
318
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
323
|
+
stdio: stdio === "inherit" ? "inherit" : ["pipe", "pipe", "pipe"],
|
|
319
324
|
env: process.env,
|
|
320
325
|
windowsHide: true,
|
|
321
326
|
});
|
|
322
327
|
|
|
323
|
-
|
|
328
|
+
if (stdio !== "inherit") {
|
|
329
|
+
proxyStreams(child);
|
|
330
|
+
}
|
|
324
331
|
forwardSignal(child, "SIGINT");
|
|
325
332
|
forwardSignal(child, "SIGTERM");
|
|
326
333
|
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -11,31 +11,35 @@ metadata:
|
|
|
11
11
|
|
|
12
12
|
This skill is a lightweight router for operational Qingflow work.
|
|
13
13
|
Assumes MCP is connected, authenticated, and on the correct workspace.
|
|
14
|
+
Before routing, skim the shared maintenance baseline: [public-surface-sync.md](references/public-surface-sync.md).
|
|
14
15
|
|
|
15
16
|
## Default Paths
|
|
16
17
|
|
|
17
18
|
Route to exactly one of these specialized paths:
|
|
18
19
|
|
|
19
20
|
1. Record insert
|
|
20
|
-
Switch to [$qingflow-record-insert](
|
|
21
|
+
Switch to [$qingflow-record-insert](../qingflow-record-insert/SKILL.md)
|
|
21
22
|
|
|
22
23
|
2. Record update
|
|
23
|
-
Switch to [$qingflow-record-update](
|
|
24
|
+
Switch to [$qingflow-record-update](../qingflow-record-update/SKILL.md)
|
|
24
25
|
|
|
25
26
|
3. Record delete
|
|
26
|
-
Switch to [$qingflow-record-delete](
|
|
27
|
+
Switch to [$qingflow-record-delete](../qingflow-record-delete/SKILL.md)
|
|
27
28
|
|
|
28
29
|
4. Record import
|
|
29
|
-
Switch to [$qingflow-record-import](
|
|
30
|
+
Switch to [$qingflow-record-import](../qingflow-record-import/SKILL.md)
|
|
30
31
|
|
|
31
32
|
5. Task workflow operations
|
|
32
|
-
Switch to [$qingflow-task-ops](
|
|
33
|
+
Switch to [$qingflow-task-ops](../qingflow-task-ops/SKILL.md)
|
|
33
34
|
|
|
34
35
|
6. Analysis
|
|
35
|
-
Switch to [$qingflow-record-analysis](
|
|
36
|
+
Switch to [$qingflow-record-analysis](../qingflow-record-analysis/SKILL.md)
|
|
36
37
|
|
|
37
38
|
7. MCP connection / auth / workspace selection
|
|
38
|
-
Switch to [$qingflow-mcp-setup](
|
|
39
|
+
Switch to [$qingflow-mcp-setup](../qingflow-mcp-setup/SKILL.md)
|
|
40
|
+
|
|
41
|
+
8. App / view / workflow / chart / portal / package configuration
|
|
42
|
+
Switch to [$qingflow-app-builder](../qingflow-app-builder/SKILL.md)
|
|
39
43
|
|
|
40
44
|
## Routing Rules
|
|
41
45
|
|
|
@@ -46,11 +50,13 @@ Route to exactly one of these specialized paths:
|
|
|
46
50
|
- If the task is about deleting records directly, switch to `$qingflow-record-delete`
|
|
47
51
|
- If the task is about import templates, import capability discovery, import-file verification, authorized local file repair, import execution, or import status, switch to `$qingflow-record-import`
|
|
48
52
|
- If the task is about todo discovery, task context, approval actions, rollback or transfer, associated report review, or workflow log review, switch to `$qingflow-task-ops`
|
|
53
|
+
- If the task is about package, app, field, layout, workflow, view, chart, portal, visibility, icon, or app base configuration, switch to `$qingflow-app-builder`
|
|
49
54
|
- If the task involves member, department, or relation fields and the user only has natural names/titles, keep the same route; direct write now supports backend-native auto resolution and may return `needs_confirmation` with candidates instead of failing blind
|
|
50
55
|
- If the task involves linked visibility, upstream/downstream field dependencies, reference-driven auto fill, or formula-driven defaulting, keep the same insert/update route and read field-level `linkage` from the schema before composing payloads
|
|
51
56
|
- If the task is about subtable writes, still route to the matching insert/update skill, but shape the payload as parent subtable field -> row array; do not route users toward top-level leaf selectors
|
|
52
57
|
- If the task is insert-focused and readback consistency matters, keep the same route and prefer `record_get / record_list` with `output_profile="normalized"` after the write
|
|
53
58
|
- If the user sounds like an ordinary workflow assignee rather than a system operator, prefer `$qingflow-task-ops` over direct record mutation whenever both paths could fit
|
|
59
|
+
- If the task is about task discovery by natural language query, still route to `$qingflow-task-ops`; `task_list --query` now uses backend search first and only falls back to local matching when backend returns zero rows
|
|
54
60
|
- If the task is about grouped distributions, ratios, rankings, trends, insights, or any final statistical conclusion, switch to `$qingflow-record-analysis`
|
|
55
61
|
- If the MCP is not connected, authenticated, or bound to the right workspace, switch to `$qingflow-mcp-setup`
|
|
56
62
|
|
|
@@ -72,8 +78,11 @@ Route to exactly one of these specialized paths:
|
|
|
72
78
|
|
|
73
79
|
## Resources
|
|
74
80
|
|
|
75
|
-
-
|
|
76
|
-
- Record
|
|
77
|
-
- Record
|
|
78
|
-
- Record
|
|
79
|
-
-
|
|
81
|
+
- Shared public-surface baseline: [public-surface-sync.md](references/public-surface-sync.md)
|
|
82
|
+
- Record insert: [$qingflow-record-insert](../qingflow-record-insert/SKILL.md)
|
|
83
|
+
- Record update: [$qingflow-record-update](../qingflow-record-update/SKILL.md)
|
|
84
|
+
- Record delete: [$qingflow-record-delete](../qingflow-record-delete/SKILL.md)
|
|
85
|
+
- Record import: [$qingflow-record-import](../qingflow-record-import/SKILL.md)
|
|
86
|
+
- Task workflow operations: [$qingflow-task-ops](../qingflow-task-ops/SKILL.md)
|
|
87
|
+
- Dedicated analysis workflow: [$qingflow-record-analysis](../qingflow-record-analysis/SKILL.md)
|
|
88
|
+
- Builder workflow: [$qingflow-app-builder](../qingflow-app-builder/SKILL.md)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Data Gotchas
|
|
2
2
|
|
|
3
|
-
For final statistics, grouped distributions, rankings, trends, or insight-style conclusions, use [$qingflow-record-analysis](
|
|
3
|
+
For final statistics, grouped distributions, rankings, trends, or insight-style conclusions, use [$qingflow-record-analysis](../../qingflow-record-analysis/SKILL.md) instead of keeping that reasoning inside `$qingflow-app-user`.
|
|
4
4
|
|
|
5
5
|
## Record Reads
|
|
6
6
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Qingflow Core Public Surface Sync
|
|
2
|
+
|
|
3
|
+
Use this file as the maintenance baseline for the core Qingflow skills.
|
|
4
|
+
It is not a user-facing product spec. It exists to prevent skill drift.
|
|
5
|
+
|
|
6
|
+
## Current Public Defaults
|
|
7
|
+
|
|
8
|
+
### User data
|
|
9
|
+
|
|
10
|
+
- Read range first with `app_get`, then `record_browse_schema_get(view_id=...)`
|
|
11
|
+
- Standard flows:
|
|
12
|
+
- browse / export detail: `app_get -> record_browse_schema_get -> record_list / record_get`
|
|
13
|
+
- insert: `record_insert_schema_get -> record_insert`
|
|
14
|
+
- update: `record_update_schema_get -> record_update`
|
|
15
|
+
- analyze: `app_get -> record_browse_schema_get -> record_analyze`
|
|
16
|
+
|
|
17
|
+
### Tasks
|
|
18
|
+
|
|
19
|
+
- Discovery stays on `task_list`
|
|
20
|
+
- `task_list --query` uses backend search first and only applies local fallback when backend returns zero rows
|
|
21
|
+
- Public actions are:
|
|
22
|
+
- `approve`
|
|
23
|
+
- `reject`
|
|
24
|
+
- `rollback`
|
|
25
|
+
- `transfer`
|
|
26
|
+
- `urge`
|
|
27
|
+
- `save_only`
|
|
28
|
+
- `reject` requires `payload.audit_feedback`
|
|
29
|
+
- `save_only` requires non-empty `fields`
|
|
30
|
+
- `TASK_RUNTIME_CONSUMED_AFTER_ACTION` is a normal post-success warning when the current node runtime is consumed and `46001` appears on re-read
|
|
31
|
+
|
|
32
|
+
### Builder
|
|
33
|
+
|
|
34
|
+
- Official package entry: `package_get`, `package_apply`
|
|
35
|
+
- Official builder writes:
|
|
36
|
+
- `app_schema_apply`
|
|
37
|
+
- `app_layout_apply`
|
|
38
|
+
- `app_flow_apply`
|
|
39
|
+
- `app_views_apply`
|
|
40
|
+
- `app_charts_apply`
|
|
41
|
+
- `portal_apply`
|
|
42
|
+
- `app_publish_verify`
|
|
43
|
+
- `portal_apply` edit mode may omit `sections` for base-info-only updates
|
|
44
|
+
- `app_charts_apply.visibility` is a public capability and should be treated as a base-only visibility update
|
|
45
|
+
- `app_get.editability` uses:
|
|
46
|
+
- `can_edit_app_base`
|
|
47
|
+
- `can_edit_form`
|
|
48
|
+
- `can_edit_flow`
|
|
49
|
+
- `can_edit_views`
|
|
50
|
+
- `can_edit_charts`
|
|
51
|
+
|
|
52
|
+
## Known High-Drift Areas
|
|
53
|
+
|
|
54
|
+
- Task actions, especially `save_only`, reject payload requirements, and `46001` post-action interpretation
|
|
55
|
+
- Package public tools: do not regress to `package_create` / `package_attach_app` as the public default story
|
|
56
|
+
- App editability: do not let `can_edit_form` imply app base-info writes
|
|
57
|
+
- Portal and chart visibility: keep the public story on `portal_apply` / `app_charts_apply`, not low-level internal writes
|
|
58
|
+
- Analysis fallback: standard path stays `record_analyze`, but complex tables may require explicit fallback modes
|
|
59
|
+
|
|
60
|
+
## Release Checklist For Skill Maintenance
|
|
61
|
+
|
|
62
|
+
After each beta that changes public behavior, re-check:
|
|
63
|
+
|
|
64
|
+
1. `public_surface.py`
|
|
65
|
+
2. `README.md`
|
|
66
|
+
3. `server_app_builder.py` and `server.py` top-level guidance
|
|
67
|
+
4. CLI help for `task`, `builder package`, `builder portal`, `builder charts`
|
|
68
|
+
5. Whether new warnings or verification fields need to be explained in skills
|
|
69
|
+
|
|
70
|
+
If a tool behavior changed but the public surface did not, prefer updating the relevant skill section instead of expanding this file.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Record Patterns
|
|
2
2
|
|
|
3
|
-
If the task shifts into grouped analysis, ratio, ranking, trend, or any final statistical conclusion, switch to [$qingflow-record-analysis](
|
|
3
|
+
If the task shifts into grouped analysis, ratio, ranking, trend, or any final statistical conclusion, switch to [$qingflow-record-analysis](../../qingflow-record-analysis/SKILL.md).
|
|
4
4
|
|
|
5
5
|
## Browse Pattern
|
|
6
6
|
|
|
@@ -17,7 +17,42 @@ If `app_get.accessible_views` marks a view with `analysis_supported=false`, do n
|
|
|
17
17
|
|
|
18
18
|
This is the ONLY execution order. Never skip `app_get` when the browse range is unclear. Never call `record_analyze` without a browse schema.
|
|
19
19
|
|
|
20
|
-
Core tools: `app_get`, `record_browse_schema_get`, `record_analyze`. Use `record_list`/`record_get` only for post-analysis samples
|
|
20
|
+
Core tools: `app_get`, `record_browse_schema_get`, `record_analyze`. Use `record_list`/`record_get` only for post-analysis samples or an explicit fallback. Task/comment work stays in [$qingflow-task-ops](../qingflow-task-ops/SKILL.md).
|
|
21
|
+
|
|
22
|
+
## Execution Modes
|
|
23
|
+
|
|
24
|
+
Choose the lightest mode that can still support a trustworthy conclusion:
|
|
25
|
+
|
|
26
|
+
1. `server_aggregate`
|
|
27
|
+
- Default and preferred path
|
|
28
|
+
- Use `record_analyze` directly after `app_get -> record_browse_schema_get`
|
|
29
|
+
|
|
30
|
+
2. `client_aggregate_from_record_list`
|
|
31
|
+
- Allowed fallback when `record_analyze` is unstable, unsupported on the target view, or the table is complex enough that service-side aggregation is not trustworthy
|
|
32
|
+
- Still requires `app_get -> record_browse_schema_get` first
|
|
33
|
+
- Use only after disclosing that the result is a fallback built from detail rows
|
|
34
|
+
|
|
35
|
+
3. `cross_app_manual_reconcile`
|
|
36
|
+
- Use when the question depends on joining multiple apps, organization-history alias mapping, or other business logic that current public tools do not express directly
|
|
37
|
+
- Be explicit that the conclusion is based on manual reconciliation rules, not one single DSL execution
|
|
38
|
+
|
|
39
|
+
## Fallback Ladder
|
|
40
|
+
|
|
41
|
+
Trigger a fallback when any of these are true:
|
|
42
|
+
|
|
43
|
+
- `record_analyze` is unstable or cannot complete the scan reliably
|
|
44
|
+
- the target view is unsupported for analysis
|
|
45
|
+
- field semantics are ambiguous enough that a server aggregate would be misleading
|
|
46
|
+
- the question requires cross-app reconciliation
|
|
47
|
+
- the question depends on organization-tree scope, historical department aliases, or other business rules that are not first-class MCP filters
|
|
48
|
+
|
|
49
|
+
When you fall back:
|
|
50
|
+
|
|
51
|
+
1. Keep the standard read order: `app_get -> record_browse_schema_get`
|
|
52
|
+
2. State which fallback mode you chose
|
|
53
|
+
3. State whether the result is still full-scope or only a verified subset
|
|
54
|
+
4. State the time field, organization scope, and any alias mapping used
|
|
55
|
+
5. Prefer concrete numbers plus a conservative conclusion over broad wording
|
|
21
56
|
|
|
22
57
|
---
|
|
23
58
|
|
|
@@ -129,7 +164,7 @@ Top-level arguments:
|
|
|
129
164
|
- Do not pass field titles, aliases, or guessed ids.
|
|
130
165
|
- If `completeness.statement_scope=returned_groups_only` or `completeness.rows_truncated=true`, downgrade wording to returned groups only.
|
|
131
166
|
- One DSL per question. Multiple small DSLs > one overloaded request.
|
|
132
|
-
- `record_list` is
|
|
167
|
+
- `record_list` is not the default basis for final statistics. Use it only in an explicit fallback mode and disclose that fallback in the final answer.
|
|
133
168
|
- Set `alias` for any metric you will sort by, compare, or quote.
|
|
134
169
|
|
|
135
170
|
---
|
|
@@ -137,12 +172,18 @@ Top-level arguments:
|
|
|
137
172
|
## OUTPUT
|
|
138
173
|
|
|
139
174
|
- Final answer must show concrete numbers.
|
|
175
|
+
- Final answer must state which execution mode was used whenever the answer is not a straightforward `server_aggregate`
|
|
140
176
|
- If `result.rows` exists, list each returned row; if there are more than 20 rows, show Top 20 and say so.
|
|
141
177
|
- 占比 = 行指标值 / `result.totals.metric_totals` 总值;如 `metric_totals` 缺失,用各行之和作分母。
|
|
142
178
|
- Prefer the structured `ranking` block when it exists.
|
|
143
179
|
- `safe_for_final_conclusion=true` → `全量可信结论`
|
|
144
180
|
- Otherwise → `初步观察`
|
|
145
181
|
- `rows_truncated=true` → 用 `前 N 个分组`, 不用 `全部`/`所有`
|
|
182
|
+
- If you used a fallback mode, explicitly disclose:
|
|
183
|
+
- whether this is full-scope or a manually curated subset
|
|
184
|
+
- which time field was used
|
|
185
|
+
- which organization scope was used
|
|
186
|
+
- whether any historical department aliases or cross-app joins were applied
|
|
146
187
|
|
|
147
188
|
## Feedback Escalation
|
|
148
189
|
|
|
@@ -152,6 +193,7 @@ Top-level arguments:
|
|
|
152
193
|
|
|
153
194
|
## Resources
|
|
154
195
|
|
|
196
|
+
- Shared public-surface baseline: [public-surface-sync.md](../qingflow-app-user/references/public-surface-sync.md)
|
|
155
197
|
- DSL templates: [references/dsl-templates.md](references/dsl-templates.md)
|
|
156
198
|
- Analysis patterns: [references/analysis-patterns.md](references/analysis-patterns.md)
|
|
157
199
|
- Confidence reporting: [references/confidence-reporting.md](references/confidence-reporting.md)
|
|
@@ -36,6 +36,8 @@ metadata:
|
|
|
36
36
|
13. If the write returns `status="needs_confirmation"`, stop and surface the candidates
|
|
37
37
|
14. Retry with explicit ids / objects only after the user confirms
|
|
38
38
|
15. Keep `verify_write=true` for production inserts
|
|
39
|
+
16. If post-write readback consistency matters, prefer `record_get(..., output_profile="normalized")` and surface `normalized_ambiguous_fields` instead of pretending same-title columns are unambiguous
|
|
40
|
+
17. Treat nested schema shape as guidance, not a brittle contract; do not hard-code transient implementation details like optional nested `field_id` shape when composing inserts
|
|
39
41
|
|
|
40
42
|
## Field Notes
|
|
41
43
|
|
|
@@ -56,3 +58,4 @@ metadata:
|
|
|
56
58
|
- Do not invent missing required fields
|
|
57
59
|
- Do not flatten subtable leaf fields to the top level
|
|
58
60
|
- Do not silently guess member / department / relation ids
|
|
61
|
+
- Do not bind logic to a transient nested schema serialization detail when the field title and parent table already identify the legal payload shape
|
|
@@ -33,6 +33,8 @@ metadata:
|
|
|
33
33
|
11. If the write returns `status="needs_confirmation"`, stop and surface the candidates
|
|
34
34
|
12. Do not assume any arbitrary combination of writable fields will succeed; one single matched accessible view still has to cover the payload
|
|
35
35
|
13. Do not look for any extra context bucket in update schema; lookup behavior stays inline on the field definitions themselves
|
|
36
|
+
14. When update context feels unstable, trust `record_update_schema_get`'s route-aware matched-view result over guessed `view_id` or remembered UI scope
|
|
37
|
+
15. If readback consistency matters, prefer `record_get(..., output_profile="normalized")` after the write and surface `normalized_ambiguous_fields` instead of hiding same-title conflicts
|
|
36
38
|
|
|
37
39
|
## Do Not
|
|
38
40
|
|
|
@@ -40,3 +42,4 @@ metadata:
|
|
|
40
42
|
- Do not update fields missing from `writable_fields`
|
|
41
43
|
- Do not resolve lookup fields against a guessed record context
|
|
42
44
|
- Do not ignore `linkage.affects_fields` when changing source-like fields on complex forms
|
|
45
|
+
- Do not fall back to guessed browse scopes when `record_update_schema_get` already tells you which matched route can or cannot execute the payload
|
|
@@ -11,6 +11,7 @@ metadata:
|
|
|
11
11
|
|
|
12
12
|
This skill is for task workflow operations only.
|
|
13
13
|
Assumes MCP is connected, authenticated, and on the correct workspace.
|
|
14
|
+
Before executing, skim the shared maintenance baseline: [public-surface-sync.md](../qingflow-app-user/references/public-surface-sync.md).
|
|
14
15
|
|
|
15
16
|
## Default Paths
|
|
16
17
|
|
|
@@ -28,6 +29,9 @@ Use exactly one of these default paths:
|
|
|
28
29
|
4. Execute workflow action
|
|
29
30
|
`task_list -> exact target -> task_get -> task_action_execute`
|
|
30
31
|
|
|
32
|
+
5. Execute a user-specified action on an already-clear target
|
|
33
|
+
`task_list -> exact target -> (optional task_get) -> task_action_execute`
|
|
34
|
+
|
|
31
35
|
## Core Tools
|
|
32
36
|
|
|
33
37
|
- `task_list`
|
|
@@ -43,16 +47,25 @@ Use exactly one of these default paths:
|
|
|
43
47
|
|
|
44
48
|
## Standard Operating Order
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
Use one of these two modes:
|
|
51
|
+
|
|
52
|
+
1. Recommendation mode
|
|
53
|
+
1. Ensure auth exists
|
|
54
|
+
2. Ensure workspace is selected
|
|
55
|
+
3. Discover the exact target with `task_list`
|
|
56
|
+
4. Read node context with `task_get`
|
|
57
|
+
5. Before giving any approval recommendation, read `task_workflow_log_get`
|
|
58
|
+
6. If `task_get` returns any `associated_reports`, read every visible report through `task_associated_report_detail_get`
|
|
59
|
+
7. Give a recommendation only after reviewing node context, workflow log, and associated reports
|
|
60
|
+
8. Wait for explicit user confirmation before `task_action_execute`
|
|
61
|
+
|
|
62
|
+
2. User-directed execution mode
|
|
63
|
+
1. Ensure auth exists
|
|
64
|
+
2. Ensure workspace is selected
|
|
65
|
+
3. Discover the exact target with `task_list`
|
|
66
|
+
4. If the target or action requirements are ambiguous, read `task_get`; otherwise go straight to `task_action_execute`
|
|
67
|
+
5. Execute through `task_action_execute`
|
|
68
|
+
6. After actions, report the exact `app_key`, `record_id`, `workflow_node_id`, executed action, and any warnings
|
|
56
69
|
|
|
57
70
|
## Task-Center Rules
|
|
58
71
|
|
|
@@ -74,6 +87,7 @@ Use exactly one of these default paths:
|
|
|
74
87
|
- `unread`
|
|
75
88
|
- `ended`
|
|
76
89
|
- `task_list` is the only public task discovery path in this MCP surface
|
|
90
|
+
- `task_list --query` uses backend `searchKey` first; only when backend returns zero rows does MCP apply a local fallback match on normalized `app_name / workflow_node_name / app_key / record_id`
|
|
77
91
|
- Treat `task_id` as a locator only; the action primary key is `app_key + record_id + workflow_node_id`
|
|
78
92
|
- Default box usage:
|
|
79
93
|
- `todo`: `task_list -> task_get -> task_workflow_log_get / task_associated_report_detail_get -> recommendation -> explicit user confirmation -> task_action_execute`
|
|
@@ -91,15 +105,20 @@ Use exactly one of these default paths:
|
|
|
91
105
|
- `rollback`
|
|
92
106
|
- `transfer`
|
|
93
107
|
- `urge`
|
|
108
|
+
- `save_only`
|
|
94
109
|
- Before any approve/reject/rollback/transfer recommendation, always review `task_workflow_log_get` when `task_get.visibility.audit_record_visible=true`
|
|
95
110
|
- If `task_get` returns visible `associated_reports`, review each one with `task_associated_report_detail_get`; do not rely on report summary alone
|
|
96
111
|
- Do not give an approval recommendation based only on `task_get`
|
|
97
112
|
- Do not execute `task_action_execute` until the user explicitly confirms the chosen action
|
|
113
|
+
- Exception: if the user has already explicitly authorized a concrete action on exact targets, you may execute directly after exact target resolution
|
|
98
114
|
- Avoid actions on ambiguous tasks or records
|
|
99
115
|
- Summarize the final action and the exact `app_key / record_id / workflow_node_id`
|
|
116
|
+
- `reject` requires `payload.audit_feedback`
|
|
117
|
+
- `save_only` requires non-empty `fields` and is only available when the backend exposes editable fields for the current node
|
|
100
118
|
- `task_action_execute` now distinguishes action execution from workflow continuation. Read `verification.runtime_continuation_verified` before claiming the workflow actually moved on.
|
|
101
119
|
- If `task_action_execute` returns `partial_success` with `WORKFLOW_CONTINUATION_UNVERIFIED`, report the action as sent but the downstream continuation as unverified.
|
|
102
120
|
- If `task_action_execute` returns `TASK_CONTEXT_VISIBILITY_UNVERIFIED` after a `46001`-style context loss, do not claim the task was already processed unless the workflow log or record state proves it.
|
|
121
|
+
- If `task_action_execute` returns `TASK_RUNTIME_CONSUMED_AFTER_ACTION`, treat that as a normal post-success state: the current node runtime was consumed, the workflow likely continued, and `46001` does not by itself mean the action failed
|
|
103
122
|
|
|
104
123
|
## Feedback Escalation
|
|
105
124
|
|
|
@@ -110,11 +129,13 @@ Use exactly one of these default paths:
|
|
|
110
129
|
## Response Interpretation
|
|
111
130
|
|
|
112
131
|
- `task_list` returns normalized todo rows and is the only default discovery path
|
|
132
|
+
- `task_list` may return `TASK_LIST_QUERY_FALLBACK_APPLIED`; this means backend search missed the query and MCP recovered the result through local exact-field fallback
|
|
113
133
|
- `task_get` returns node context summary, not full historical report data
|
|
114
134
|
- `task_associated_report_detail_get` may return either:
|
|
115
135
|
- `result_type=view_list`
|
|
116
136
|
- `result_type=chart_data`
|
|
117
137
|
- `task_workflow_log_get` returns workflow log detail only when the node grants log visibility
|
|
138
|
+
- A successful approve/reject/rollback/transfer may still lose the current-node runtime immediately; treat `record_state_readable=false + backend 46001` as a post-action runtime loss unless continuation verification says otherwise
|
|
118
139
|
- Treat `request_route` as the source of truth for live route debugging
|
|
119
140
|
- If only part of the requested work is completed, explicitly disclose which parts are done and which are not
|
|
120
141
|
|
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, packages_distributions, version as _dist_version
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
3
6
|
__all__ = ["__version__"]
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
_FALLBACK_VERSION = "0.2.0b998"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _resolve_local_pyproject_version() -> str | None:
|
|
12
|
+
module_path = Path(__file__).resolve()
|
|
13
|
+
for parent in module_path.parents:
|
|
14
|
+
candidate = parent / "pyproject.toml"
|
|
15
|
+
if not candidate.is_file():
|
|
16
|
+
continue
|
|
17
|
+
for line in candidate.read_text(encoding="utf-8").splitlines():
|
|
18
|
+
stripped = line.strip()
|
|
19
|
+
if stripped.startswith("version = "):
|
|
20
|
+
return stripped.split("=", 1)[1].strip().strip('"')
|
|
21
|
+
break
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _resolve_runtime_version() -> str:
|
|
26
|
+
local_version = _resolve_local_pyproject_version()
|
|
27
|
+
if local_version:
|
|
28
|
+
return local_version
|
|
29
|
+
for dist_name in packages_distributions().get("qingflow_mcp", []):
|
|
30
|
+
try:
|
|
31
|
+
return _dist_version(dist_name)
|
|
32
|
+
except PackageNotFoundError:
|
|
33
|
+
continue
|
|
34
|
+
return _FALLBACK_VERSION
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__version__ = _resolve_runtime_version()
|
|
@@ -819,6 +819,10 @@ class FieldMutation(StrictModel):
|
|
|
819
819
|
validation_alias=AliasChoices("custom_button_text", "customButtonText", "custom_btn_text", "customBtnText"),
|
|
820
820
|
)
|
|
821
821
|
subfields: list[FieldPatch] | None = None
|
|
822
|
+
subfield_updates: list["FieldUpdatePatch"] | None = Field(
|
|
823
|
+
default=None,
|
|
824
|
+
validation_alias=AliasChoices("subfield_updates", "subfieldUpdates"),
|
|
825
|
+
)
|
|
822
826
|
|
|
823
827
|
@model_validator(mode="after")
|
|
824
828
|
def validate_shape(self) -> "FieldMutation":
|
|
@@ -848,8 +852,12 @@ class FieldMutation(StrictModel):
|
|
|
848
852
|
or self.custom_button_text is not None
|
|
849
853
|
):
|
|
850
854
|
raise ValueError("code_block_config, code_block_binding, auto_trigger, custom_button_text_enabled, and custom_button_text are only allowed for code_block fields")
|
|
851
|
-
if self.type == PublicFieldType.subtable and not self.subfields:
|
|
852
|
-
raise ValueError("subtable field requires subfields")
|
|
855
|
+
if self.type == PublicFieldType.subtable and not self.subfields and not self.subfield_updates:
|
|
856
|
+
raise ValueError("subtable field requires subfields or subfield_updates")
|
|
857
|
+
if self.type is not None and self.type != PublicFieldType.subtable and self.subfield_updates:
|
|
858
|
+
raise ValueError("subfield_updates are only allowed for subtable fields")
|
|
859
|
+
if self.subfields and self.subfield_updates:
|
|
860
|
+
raise ValueError("subfields and subfield_updates cannot be used together")
|
|
853
861
|
return self
|
|
854
862
|
|
|
855
863
|
@model_validator(mode="before")
|
|
@@ -1528,14 +1536,16 @@ class PortalApplyRequest(StrictModel):
|
|
|
1528
1536
|
raise ValueError("package_tag_id is required when dash_key is empty")
|
|
1529
1537
|
if not self.dash_key and not self.dash_name:
|
|
1530
1538
|
raise ValueError("dash_name is required when creating a portal")
|
|
1531
|
-
if not self.sections:
|
|
1532
|
-
raise ValueError("portal apply requires a non-empty sections list")
|
|
1539
|
+
if not self.dash_key and not self.sections:
|
|
1540
|
+
raise ValueError("portal apply requires a non-empty sections list when creating a portal")
|
|
1533
1541
|
if self.visibility is not None and self.auth is not None:
|
|
1534
1542
|
raise ValueError("visibility and auth cannot be provided together")
|
|
1535
1543
|
return self
|
|
1536
1544
|
|
|
1537
1545
|
|
|
1538
1546
|
FieldPatch.model_rebuild()
|
|
1547
|
+
FieldMutation.model_rebuild()
|
|
1548
|
+
FieldUpdatePatch.model_rebuild()
|
|
1539
1549
|
|
|
1540
1550
|
|
|
1541
1551
|
class AppGetResponse(StrictModel):
|