@josephyan/qingflow-app-builder-mcp 1.0.8 → 1.1.1
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 +4 -2
- package/npm/bin/qingflow-app-builder-mcp.mjs +31 -2
- package/npm/lib/runtime.mjs +43 -2
- package/package.json +1 -1
- package/pyproject.toml +2 -1
- package/skills/qingflow-app-builder/SKILL.md +135 -14
- package/skills/qingflow-app-builder/references/build-complete-system.md +428 -0
- package/skills/qingflow-app-builder/references/build-single-app.md +530 -0
- package/skills/qingflow-app-builder/references/create-app.md +43 -3
- package/skills/qingflow-app-builder/references/gotchas.md +47 -3
- package/skills/qingflow-app-builder/references/match-rules.md +114 -0
- package/skills/qingflow-app-builder/references/public-surface-sync.md +75 -0
- package/skills/qingflow-app-builder/references/tool-selection.md +25 -10
- package/skills/qingflow-app-builder/references/update-flow.md +29 -1
- package/skills/qingflow-app-builder/references/update-schema.md +6 -2
- package/skills/qingflow-app-builder/references/update-views.md +135 -6
- package/skills/qingflow-mcp-setup/SKILL.md +111 -0
- package/skills/qingflow-mcp-setup/agents/openai.yaml +4 -0
- package/skills/qingflow-mcp-setup/references/claude-desktop.md +34 -0
- package/skills/qingflow-mcp-setup/references/environments.md +61 -0
- package/skills/qingflow-mcp-setup/references/generic-stdio.md +31 -0
- package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +38 -0
- package/skills/qingflow-workflow-builder/SKILL.md +96 -0
- package/skills/qingflow-workflow-builder/manifest.yaml +8 -0
- package/skills/qingflow-workflow-builder/references/01-overview.md +45 -0
- package/skills/qingflow-workflow-builder/references/02-update-mode.md +53 -0
- package/skills/qingflow-workflow-builder/references/03-flow-patterns.md +57 -0
- package/skills/qingflow-workflow-builder/references/04-stage1-business-modeling.md +131 -0
- package/skills/qingflow-workflow-builder/references/05-stage2-members-roles.md +29 -0
- package/skills/qingflow-workflow-builder/references/06-stage3-build-spec.md +165 -0
- package/skills/qingflow-workflow-builder/references/07-stage4-validate-spec.md +33 -0
- package/skills/qingflow-workflow-builder/references/08-stage5-apply-verify.md +51 -0
- package/skills/qingflow-workflow-builder/references/09-stage6-summary.md +88 -0
- package/skills/qingflow-workflow-builder/references/10-node-config-reference.md +93 -0
- package/skills/qingflow-workflow-builder/references/11-troubleshooting.md +15 -0
- package/skills/qingflow-workflow-builder/scripts/diff_flow_spec.py +275 -0
- package/skills/qingflow-workflow-builder/scripts/validate_flow_spec.py +605 -0
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/backend_client.py +55 -1
- package/src/qingflow_mcp/builder_facade/models.py +532 -48
- package/src/qingflow_mcp/builder_facade/service.py +9194 -2384
- package/src/qingflow_mcp/builder_facade/workflow_spec.py +111 -0
- package/src/qingflow_mcp/cli/commands/app.py +3 -16
- package/src/qingflow_mcp/cli/commands/builder.py +354 -56
- package/src/qingflow_mcp/cli/commands/record.py +112 -6
- package/src/qingflow_mcp/cli/formatters.py +133 -2
- package/src/qingflow_mcp/cli/main.py +204 -3
- package/src/qingflow_mcp/public_surface.py +12 -9
- package/src/qingflow_mcp/response_trim.py +288 -22
- package/src/qingflow_mcp/server.py +29 -20
- package/src/qingflow_mcp/server_app_builder.py +108 -30
- package/src/qingflow_mcp/server_app_user.py +29 -23
- package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +294 -0
- package/src/qingflow_mcp/solution/executor.py +3 -133
- package/src/qingflow_mcp/tools/ai_builder_tools.py +2617 -440
- package/src/qingflow_mcp/tools/app_tools.py +53 -8
- package/src/qingflow_mcp/tools/package_tools.py +16 -2
- package/src/qingflow_mcp/tools/record_tools.py +14451 -9110
- package/src/qingflow_mcp/tools/resource_read_tools.py +3 -0
- package/src/qingflow_mcp/tools/solution_tools.py +30 -2
- package/src/qingflow_mcp/tools/workflow_tools.py +3 -31
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Builder Match Rules
|
|
2
|
+
|
|
3
|
+
Use this reference for builder-side field matching rules in custom buttons and associated view/report filters.
|
|
4
|
+
|
|
5
|
+
This is not the same thing as `record_access` analysis filters or normal view `filters`.
|
|
6
|
+
|
|
7
|
+
## Where These Rules Apply
|
|
8
|
+
|
|
9
|
+
- `app_custom_buttons_apply.upsert_buttons[].trigger_add_data_config.field_mappings`
|
|
10
|
+
- `app_custom_buttons_apply.patch_buttons[].set.trigger_add_data_config.field_mappings`
|
|
11
|
+
- `app_associated_resources_apply.upsert_resources[].match_mappings`
|
|
12
|
+
- `app_associated_resources_apply.patch_resources[].set.match_mappings`
|
|
13
|
+
|
|
14
|
+
Prefer semantic mappings. Do not handwrite raw `que_relation` or `match_rules` unless you are preserving an existing backend config.
|
|
15
|
+
|
|
16
|
+
## System Fields
|
|
17
|
+
|
|
18
|
+
System fields can participate in mappings:
|
|
19
|
+
|
|
20
|
+
- `数据ID`, `row_record_id`, `apply_id`, `_id` -> `field_id=-17`
|
|
21
|
+
This is the current record's real apply/record ID.
|
|
22
|
+
- `编号`, `数据编号`, `record_number` -> `field_id=0`
|
|
23
|
+
This is the frontend-visible record number. It may be custom formatted and is not the same as `数据ID`.
|
|
24
|
+
|
|
25
|
+
Explicit selectors such as `{"field_id": -17}` and `{"field_id": 0}` are allowed and remove ambiguity.
|
|
26
|
+
|
|
27
|
+
## Custom Buttons
|
|
28
|
+
|
|
29
|
+
Use `field_mappings` for dynamic values copied from the current source record:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"target_app_key": "WORKLOG_APP",
|
|
34
|
+
"field_mappings": [
|
|
35
|
+
{"source_field": "数据ID", "target_field": "关联员工"},
|
|
36
|
+
{"source_field": "员工名称", "target_field": "员工姓名"}
|
|
37
|
+
],
|
|
38
|
+
"default_values": {
|
|
39
|
+
"状态": "待提交"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Use `default_values` only for static constants. Do not use `default_values` as the preferred way to pass the current record.
|
|
45
|
+
|
|
46
|
+
For an existing button, put the same semantic config under `patch_buttons[].set`:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"button_id": 3858041,
|
|
51
|
+
"set": {
|
|
52
|
+
"trigger_add_data_config": {
|
|
53
|
+
"target_app_key": "WORKLOG_APP",
|
|
54
|
+
"field_mappings": [
|
|
55
|
+
{"source_field": "数据ID", "target_field": "关联员工"}
|
|
56
|
+
],
|
|
57
|
+
"default_values": {
|
|
58
|
+
"状态": "待提交"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Associated Resources
|
|
66
|
+
|
|
67
|
+
Use `match_mappings` for associated view/report filters:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"graph_type": "view",
|
|
72
|
+
"target_app_key": "WORKLOG_APP",
|
|
73
|
+
"view_key": "WORKLOG_VIEW",
|
|
74
|
+
"match_mappings": [
|
|
75
|
+
{"target_field": "关联员工", "source_field": "数据ID"},
|
|
76
|
+
{"target_field": "状态", "value": "待提交"}
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Dynamic conditions use `source_field`; static conditions use `value`.
|
|
82
|
+
|
|
83
|
+
For an existing associated resource, put the same filter mapping under `patch_resources[].set`:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"associated_item_id": 3497587,
|
|
88
|
+
"set": {
|
|
89
|
+
"match_mappings": [
|
|
90
|
+
{"target_field": "关联员工", "source_field": "数据ID"}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Type Compatibility
|
|
97
|
+
|
|
98
|
+
- Relation fields: match another relation field with the same target app, or match `数据ID(-17)` when the target relation points to the current source app.
|
|
99
|
+
- Record ID `数据ID(-17)`: can match relation fields that point to the current source app, or ID-compatible text/number fields.
|
|
100
|
+
- Record number `编号(0)`: can match text, long text, number, amount, or another record-number field.
|
|
101
|
+
- Member fields: only match member fields.
|
|
102
|
+
- Department fields: only match department fields.
|
|
103
|
+
- Option fields: single select, multi select, and boolean can match option-family fields or static option values.
|
|
104
|
+
- Date fields: date and datetime can match each other.
|
|
105
|
+
- Text fields: text, long text, phone, and email can match each other.
|
|
106
|
+
- Number fields: number and amount can match each other.
|
|
107
|
+
- Attachment, subtable, code block, Q-Linker, and address fields are not default match fields.
|
|
108
|
+
|
|
109
|
+
## Error Interpretation
|
|
110
|
+
|
|
111
|
+
- Field not found: re-read `app_get` or `app_get_fields` and retry with an exact title or `field_id`.
|
|
112
|
+
- Type mismatch: choose a compatible source/target pair from the matrix above.
|
|
113
|
+
- Reference source mismatch: the target relation field points to a different app; choose a relation field that points back to the current source app, or match a non-relation field.
|
|
114
|
+
- Mixed raw and semantic modes: use either semantic `field_mappings/match_mappings` or raw `que_relation/match_rules`, never both in the same item.
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
- Treat `record_browse_schema_get.fields` as the selected Qingflow table view header schema; `record_access.fields` and `record_list` field selectors must stay aligned with it.
|
|
12
|
+
- Standard flows:
|
|
13
|
+
- analyze: `app_get -> record_browse_schema_get -> record_access -> Python`
|
|
14
|
+
- browse detail: `app_get -> record_browse_schema_get -> record_list / record_get`
|
|
15
|
+
- explicit export/download/Excel: `app_get -> choose view_id -> view_get -> record_export_*` or `record_export_direct`; export tools require explicit `view_id`
|
|
16
|
+
- insert: `record_insert_schema_get -> record_insert(items)`
|
|
17
|
+
- update: `record_get -> record_update`; use `record_update_schema_get` only for failure diagnosis or ambiguous writable-field routing
|
|
18
|
+
|
|
19
|
+
### Tasks
|
|
20
|
+
|
|
21
|
+
- Discovery stays on `task_list`
|
|
22
|
+
- `task_list --query` uses backend search first and only applies local fallback when backend returns zero rows
|
|
23
|
+
- Public actions are:
|
|
24
|
+
- `approve`
|
|
25
|
+
- `reject`
|
|
26
|
+
- `rollback`
|
|
27
|
+
- `transfer`
|
|
28
|
+
- `urge`
|
|
29
|
+
- `save_only`
|
|
30
|
+
- `reject` requires `payload.audit_feedback`
|
|
31
|
+
- `save_only` requires non-empty `fields`
|
|
32
|
+
- `TASK_RUNTIME_CONSUMED_AFTER_ACTION` is a normal post-success warning when the current node runtime is consumed and `46001` appears on re-read
|
|
33
|
+
|
|
34
|
+
### Builder
|
|
35
|
+
|
|
36
|
+
- Official package entry: `package_get`, `package_apply`
|
|
37
|
+
- Official builder writes:
|
|
38
|
+
- `app_schema_apply`
|
|
39
|
+
- `app_layout_apply`
|
|
40
|
+
- `app_flow_apply`
|
|
41
|
+
- `app_views_apply`
|
|
42
|
+
- `app_custom_buttons_apply`
|
|
43
|
+
- `app_associated_resources_apply`
|
|
44
|
+
- `app_charts_apply`
|
|
45
|
+
- `portal_apply`
|
|
46
|
+
- `app_publish_verify`
|
|
47
|
+
- `portal_apply` edit mode may omit `sections` for base-info-only updates
|
|
48
|
+
- `app_charts_apply.visibility` is a public capability and should be treated as a base-only visibility update
|
|
49
|
+
- Existing object parameter replacement should use `patch_views`, `patch_buttons`, `patch_resources`, and `patch_charts`; `upsert_*` is for creation or full target config
|
|
50
|
+
- `app_get.editability` uses:
|
|
51
|
+
- `can_edit_app_base`
|
|
52
|
+
- `can_edit_form`
|
|
53
|
+
- `can_edit_flow`
|
|
54
|
+
- `can_edit_views`
|
|
55
|
+
- `can_edit_charts`
|
|
56
|
+
|
|
57
|
+
## Known High-Drift Areas
|
|
58
|
+
|
|
59
|
+
- Task actions, especially `save_only`, reject payload requirements, and `46001` post-action interpretation
|
|
60
|
+
- Package public tools: do not regress to `package_create` / `package_attach_app` as the public default story
|
|
61
|
+
- App editability: do not let `can_edit_form` imply app base-info writes
|
|
62
|
+
- Portal and chart visibility: keep the public story on `portal_apply` / `app_charts_apply`, not low-level internal writes
|
|
63
|
+
- Analysis path: standard path stays `record_access -> Python`
|
|
64
|
+
|
|
65
|
+
## Release Checklist For Skill Maintenance
|
|
66
|
+
|
|
67
|
+
After each beta that changes public behavior, re-check:
|
|
68
|
+
|
|
69
|
+
1. `public_surface.py`
|
|
70
|
+
2. `README.md`
|
|
71
|
+
3. `server_app_builder.py` and `server.py` top-level guidance
|
|
72
|
+
4. CLI help for `task`, `builder package`, `builder portal`, `builder charts`
|
|
73
|
+
5. Whether new warnings or verification fields need to be explained in skills
|
|
74
|
+
|
|
75
|
+
If a tool behavior changed but the public surface did not, prefer updating the relevant skill section instead of expanding this file.
|
|
@@ -30,24 +30,33 @@ If the user asks for multiple forms/modules that relate to each other, this is a
|
|
|
30
30
|
|
|
31
31
|
## Summary reads
|
|
32
32
|
|
|
33
|
+
All `app_get_*` tools accept either `app_key` (single) or `app_keys[]` (batch). Batch returns `{status, apps[], errors[]}` — prefer batch when reading multiple apps in one step.
|
|
34
|
+
|
|
33
35
|
- `app_get`: overall app config health, publish state, counts, and builder editability
|
|
34
36
|
- `app_get_fields`: field names, types, required flags, section ids
|
|
35
37
|
- `app_get_layout`: sections, rows, unplaced fields
|
|
36
38
|
- `app_get_views`: current view names, types, columns, group-by
|
|
37
|
-
- `app_get_flow`: workflow enabled state, nodes
|
|
39
|
+
- `app_get_flow`: workflow enabled state, full spec (nodes + transitions)
|
|
38
40
|
- `app_get_charts`: current chart ids, names, types, order
|
|
41
|
+
- `app_get_buttons`: current custom button list (draft state)
|
|
42
|
+
- `app_get_associated_resources`: current associated resource pool (draft state)
|
|
43
|
+
- `app_publish_verify`: publish state and package tag verification — accepts `app_keys[]` for multi-app batch
|
|
39
44
|
- `portal_get`: current portal config detail and component inventory
|
|
40
45
|
|
|
41
46
|
## Apply tools
|
|
42
47
|
|
|
43
|
-
These execute normalized patches
|
|
48
|
+
These execute normalized patches. Some app apply tools publish by default and still accept `publish=false`; custom button and associated-resource apply publish after at least one write succeeds and do not expose that switch.
|
|
44
49
|
|
|
45
50
|
- `app_schema_apply`: create app shell or change fields
|
|
46
|
-
- `app_layout_apply`: merge or replace layout
|
|
47
|
-
- `app_flow_apply`: replace workflow
|
|
48
|
-
- `app_views_apply`:
|
|
49
|
-
- `
|
|
50
|
-
- `
|
|
51
|
+
- `app_layout_apply`: merge or replace layout; use `apps[]` (each item `{app_key, mode?, sections}`) for multi-app batch
|
|
52
|
+
- `app_flow_apply`: replace full workflow spec; use `patch_nodes[]` (`{id, set, unset}`) to update specific nodes without rewriting the full spec
|
|
53
|
+
- `app_views_apply`: use `patch_views` for existing-view parameter replacement; use `upsert_views` for creation or full target config; remove views by key; new views default associated report/view display to visible with `limit_type="all"`; use `apps[]` for multi-app batch
|
|
54
|
+
- `app_custom_buttons_apply`: use `patch_buttons` for existing-button parameter replacement; use `upsert_buttons` for creation or full target config; configure add-data field mappings/default values; bind buttons to header/detail/list view positions; `placement=list` maps to the backend `INSIDE` row/list button position; merge-mode view configs require `buttons`; use `view_configs[].mode="replace"` or `buttons=[]` to clear a view's custom button bindings. For child-record creation linked to the current source record, map `source_field: "数据ID"` to the target relation field. Use `apps[]` for multi-app batch.
|
|
55
|
+
- `app_associated_resources_apply`: attach existing BI reports/views to the Qingflow app associated-resource pool and per-view display area; it does not create or edit QingBI report bodies/configs. Use `patch_resources` for existing associated-resource parameter replacement; use `upsert_resources` for creation or full target config; `view_configs`, remove, and reorder may reference existing resources by internal `associated_item_id` or by `chart_id`/`chart_key`/`view_key`; use `match_mappings` for associated view/report filters; publishes after successful writes; omit raw `sourceType`, and use `report_source="dataset"` only to attach an existing BI dataset report. Before `upsert_resources`, read `app_get.associated_resources` and reuse an existing matching `target_app_key + view_key/chart_key`; repeated upsert can create duplicates because `client_key` is only valid inside one apply call.
|
|
56
|
+
- `app_charts_apply`: create/edit/remove/reorder app-source QingBI report bodies/configs with `dataSourceType=qingflow`; it does not create/edit dataset BI reports and does not attach reports to Qingflow app associated-resource display. Use `patch_charts` for existing-chart parameter replacement; use `upsert_charts` for creation or full target config; supports `target/table` aliases plus QingBI chart types such as `summary`, `columnar`, `area`, `funnel`, `radar`, `scatter`, `dualaxes`, and `map`; charts are immediate-live and do not publish; use `chart_id` when names are not unique; use `apps[]` for multi-app batch
|
|
57
|
+
- `portal_apply`: create or replace-update portal pages; use `dash_key` for update mode or `package_id + dash_name` for create mode; edit mode may omit `sections` for base-info-only updates; when sections are supplied they still use replace semantics; sections use a 24-column PC grid — all components in the same row (same `y`) must share the same `rows` value and their `cols` must sum to exactly 24; mismatched `rows` causes height misalignment, `cols` under 24 leaves trailing blank space; use `patch_sections[]` (`{chart_ref/view_ref/order, set, unset}`) to update individual sections without replacing all
|
|
58
|
+
|
|
59
|
+
For object-level updates, the safe partial syntax is `patch_*` with the object's real selector field plus `set` and optional `unset`. `selector` is only a concept, not a literal key. Examples: `patch_views: [{"view_key": "VIEW_KEY", "set": {...}}]`, `patch_buttons: [{"button_id": 1001, "set": {...}}]`, `patch_resources: [{"associated_item_id": 123, "set": {...}}]`, `patch_charts: [{"chart_id": 456, "set": {...}}]`, `patch_nodes: [{"id": "approve_1", "set": {...}}]`, `patch_sections: [{"chart_ref": {"chart_key": "ck_001"}, "set": {"title": "新标题"}}]`. The tool reads the current backend config, merges the patch, then submits the full backend payload internally. Do not send a partial `upsert_*` and expect missing required fields to be preserved.
|
|
51
60
|
|
|
52
61
|
## Explicit post-apply tools
|
|
53
62
|
|
|
@@ -60,19 +69,25 @@ These execute normalized patches and publish by default unless `publish=false`.
|
|
|
60
69
|
- Create a brand new package, then create one app in it:
|
|
61
70
|
`package_apply(create_if_missing=true) -> app_schema_apply`
|
|
62
71
|
- Create a brand new multi-app system/package:
|
|
63
|
-
`package_apply(create_if_missing=true) ->
|
|
72
|
+
`package_apply(create_if_missing=true) -> app_schema_apply(apps[])`
|
|
64
73
|
- Update fields on an existing app:
|
|
65
74
|
`app_resolve -> app_get_fields -> app_schema_apply`
|
|
66
75
|
- Tidy layout:
|
|
67
76
|
`app_get_fields -> app_get_layout -> builder_tool_contract (if shape is unclear) -> app_layout_apply`
|
|
68
77
|
- Add workflow:
|
|
69
78
|
`builder_tool_contract -> app_get_fields -> app_get_flow -> role_search/member_search -> app_flow_apply -> app_get_flow`
|
|
79
|
+
- Update specific flow nodes:
|
|
80
|
+
`app_get_flow -> app_flow_apply.patch_nodes[]`
|
|
70
81
|
- Add views:
|
|
71
|
-
`builder_tool_contract -> app_get_fields -> app_get_views -> app_views_apply -> app_get_views`
|
|
82
|
+
`builder_tool_contract -> app_get_fields -> app_get_views -> app_views_apply.patch_views/upsert_views -> app_get_views`
|
|
72
83
|
- Add QingBI charts:
|
|
73
|
-
`builder_tool_contract -> app_get_fields -> app_get_charts -> app_charts_apply -> app_get_charts`
|
|
84
|
+
`builder_tool_contract -> app_get_fields -> app_get_charts -> app_charts_apply.patch_charts/upsert_charts -> app_get_charts`
|
|
85
|
+
- Show an existing QingBI chart inside a Qingflow app/view:
|
|
86
|
+
`app_get -> app_associated_resources_apply.upsert_resources/patch_resources + view_configs -> app_get`
|
|
74
87
|
- Create or update a portal:
|
|
75
88
|
`builder_tool_contract -> portal_get -> portal_apply -> portal_get`
|
|
89
|
+
- Update portal section(s) without replacing all:
|
|
90
|
+
`portal_get -> portal_apply.patch_sections[]`
|
|
76
91
|
|
|
77
92
|
## Avoid
|
|
78
93
|
|
|
@@ -61,6 +61,34 @@ For flexible business requirements, do not jump straight to a full custom graph.
|
|
|
61
61
|
2. identify the business-specific changes
|
|
62
62
|
3. patch nodes/transitions explicitly
|
|
63
63
|
|
|
64
|
+
## Targeted node updates
|
|
65
|
+
|
|
66
|
+
To change one or a few nodes without rewriting the full spec, use `patch_nodes[]` instead of `spec`:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"tool_name": "app_flow_apply",
|
|
71
|
+
"arguments": {
|
|
72
|
+
"profile": "default",
|
|
73
|
+
"app_key": "APP_123",
|
|
74
|
+
"publish": true,
|
|
75
|
+
"patch_nodes": [
|
|
76
|
+
{
|
|
77
|
+
"id": "approve_1",
|
|
78
|
+
"set": {
|
|
79
|
+
"name": "总监审批",
|
|
80
|
+
"assignees": {"role_names": ["总监"]}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`patch_nodes[]` reads the current flow, merges `set` keys and removes `unset` keys on the matched node, then writes the full spec back. It returns `FLOW_NODE_NOT_FOUND` if the `id` does not match any existing node — call `app_get_flow` first to confirm ids.
|
|
89
|
+
|
|
90
|
+
Do NOT use `patch_nodes` and `spec` together in the same call.
|
|
91
|
+
|
|
64
92
|
Public flow building is intentionally limited to linear workflows. Use only:
|
|
65
93
|
|
|
66
94
|
- `start`
|
|
@@ -149,7 +177,7 @@ Do not copy internal keys from old plan outputs or logs, including:
|
|
|
149
177
|
|
|
150
178
|
## Notes
|
|
151
179
|
|
|
152
|
-
- `mode=replace` is the only supported flow apply mode
|
|
180
|
+
- `mode=replace` is the only supported flow apply mode when using `spec`; `patch_nodes[]` does not require a `mode`
|
|
153
181
|
- `app_flow_apply` publishes by default
|
|
154
182
|
- Prefer roles over explicit members unless the user explicitly asks for named members
|
|
155
183
|
- `basic_approval` and `basic_fill_then_approve` are skeletons, not complete business workflows
|
|
@@ -32,10 +32,12 @@ Apply the patch:
|
|
|
32
32
|
"app_key": "APP_123",
|
|
33
33
|
"publish": true,
|
|
34
34
|
"add_fields": [
|
|
35
|
-
{"name": "跟进日期", "type": "date"}
|
|
35
|
+
{"name": "跟进日期", "type": "date"},
|
|
36
|
+
{"name": "数据封面", "type": "attachment", "as_data_cover": true}
|
|
36
37
|
],
|
|
37
38
|
"update_fields": [
|
|
38
|
-
{"selector": {"name": "金额"}, "set": {"name": "订单金额", "required": true}}
|
|
39
|
+
{"selector": {"name": "金额"}, "set": {"name": "订单金额", "required": true}},
|
|
40
|
+
{"selector": {"name": "客户名称"}, "set": {"as_data_title": true}}
|
|
39
41
|
],
|
|
40
42
|
"remove_fields": [
|
|
41
43
|
{"name": "旧字段"}
|
|
@@ -66,3 +68,5 @@ Treat it as uncertain write state. Read back with `app_get_fields` before retryi
|
|
|
66
68
|
- `mobile -> phone`
|
|
67
69
|
- `select/radio -> single_select`
|
|
68
70
|
- `checkbox -> multi_select`
|
|
71
|
+
- `as_data_title: true` marks the app data title; the final form must have exactly one top-level data title field
|
|
72
|
+
- `as_data_cover: true` marks the app data cover; the cover is optional and must be a top-level `attachment` field
|
|
@@ -6,17 +6,20 @@ Use this when the task is only about table, card, board, or gantt views.
|
|
|
6
6
|
|
|
7
7
|
1. `builder_tool_contract(tool_name="app_views_apply")`
|
|
8
8
|
2. `app_get_fields`
|
|
9
|
-
3. `
|
|
9
|
+
3. `app_get` for current view/chart/button inventory and app-level associated resource ids
|
|
10
10
|
4. `app_views_apply`
|
|
11
|
-
5. `
|
|
11
|
+
5. Use `app_associated_resources_apply` if the task includes associated reports/views
|
|
12
|
+
6. `app_get` or `view_get` again whenever apply returns `failed` or `partial_success`
|
|
12
13
|
|
|
13
14
|
If you are unsure about keys or view types, call `builder_tool_contract(tool_name="app_views_apply")` before guessing.
|
|
14
15
|
|
|
15
16
|
Important verification rule:
|
|
16
17
|
|
|
17
18
|
- `app_views_apply` can create a view object before every filter is fully verified in readback
|
|
18
|
-
- Do not report “筛选已成功应用” unless the apply result also shows `verification.views_verified=true`
|
|
19
|
-
-
|
|
19
|
+
- Do not report “筛选已成功应用” unless the apply result also shows `verification.views_verified=true` and `verification.view_filters_verified=true`
|
|
20
|
+
- Do not report “查询条件已成功配置” unless the apply result also shows `verification.view_query_conditions_verified=true`
|
|
21
|
+
- Do not report “关联资源已成功配置” unless `app_associated_resources_apply` shows `verification.associated_resource_view_configs_verified=true`
|
|
22
|
+
- If apply returns `partial_success`, inspect `verification.by_view`, `details.filter_mismatches`, `details.query_condition_mismatches`, and `details.associated_resource_mismatches` before claiming filters/query conditions/associated resources are active
|
|
20
23
|
|
|
21
24
|
## Example
|
|
22
25
|
|
|
@@ -26,8 +29,14 @@ Canonical rules before any example:
|
|
|
26
29
|
- Do not emit `column_names`
|
|
27
30
|
- Treat `fields` only as a legacy alias the MCP may normalize, not as the preferred shape
|
|
28
31
|
- Use `filters` with canonical keys `field_name`, `operator`, `value`/`values`
|
|
32
|
+
- Use `query_conditions` for the frontend query panel. Do not put query-panel fields into `filters`.
|
|
33
|
+
- When creating any new table or card view, always include `query_conditions` with `enabled: true`. In `rows`, list the fields users would most commonly search by — typically the data-title field, any status/type select fields, date fields, and member fields. Do not omit `query_conditions` or leave `rows` empty on a new view.
|
|
34
|
+
- New views created by `app_views_apply` default the frontend associated report/view area to visible with `limit_type="all"`. Existing views preserve their current associated-resource display unless `associated_resources` is explicitly patched.
|
|
35
|
+
- Use `app_associated_resources_apply` for the associated report/view resource pool, selected resources, and match rules. `associated_item_id` is the internal associated-resource id from `app_get.associated_resources`; `view_configs`, remove, and reorder may also pass an existing resource's `chart_id`/`chart_key`/`view_key`, which the tool resolves to the internal id. Do not write backend raw `sourceType`; reports default to BI app reports, and dataset reports use `report_source="dataset"`. Use `match_mappings` for associated view/report filters; read `match-rules.md` if field type compatibility is unclear.
|
|
29
36
|
- For gantt, use `start_field`, `end_field`, and optionally `title_field`
|
|
30
|
-
- If `app_get_views` shows duplicate view names, include `view_key` in `upsert_views[]` and update that exact target
|
|
37
|
+
- If `app_get.views` or `app_get_views` shows duplicate view names, include `view_key` in `upsert_views[]` and update that exact target
|
|
38
|
+
- Builder view writes always use the raw `view_key` from `app_get.views[].view_key`, such as `emsrao25rs02`. Do not pass `custom:emsrao25rs02`; that prefixed form is only for record-data `view_id`.
|
|
39
|
+
- For an existing view, prefer `patch_views` for parameter replacement. Do not send a partial `upsert_views` object such as only `name/type/query_conditions`; backend view saves require other type-specific fields, and the patch path preserves them for you.
|
|
31
40
|
|
|
32
41
|
Apply a default table view:
|
|
33
42
|
|
|
@@ -43,7 +52,13 @@ Apply a default table view:
|
|
|
43
52
|
"name": "全部订单",
|
|
44
53
|
"view_key": "VIEW_KEY_IF_DUPLICATE_NAMES_EXIST",
|
|
45
54
|
"type": "table",
|
|
46
|
-
"columns": ["订单编号", "客户名称", "订单金额", "状态"]
|
|
55
|
+
"columns": ["订单编号", "客户名称", "订单金额", "状态", "负责人", "创建时间"],
|
|
56
|
+
"query_conditions": {
|
|
57
|
+
"enabled": true,
|
|
58
|
+
"exact": false,
|
|
59
|
+
"hide_before_query": false,
|
|
60
|
+
"rows": [["订单编号", "客户名称"], ["状态", "负责人"], ["创建时间"]]
|
|
61
|
+
}
|
|
47
62
|
}
|
|
48
63
|
],
|
|
49
64
|
"remove_views": []
|
|
@@ -52,6 +67,100 @@ Apply a default table view:
|
|
|
52
67
|
```
|
|
53
68
|
After `app_views_apply` returns canonical arguments or blocking issues, prefer reusing its `suggested_next_call.arguments` directly. Do not rewrite aliases back into non-canonical keys such as `column_names`.
|
|
54
69
|
|
|
70
|
+
Existing table view: replace only frontend query conditions:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"tool_name": "app_views_apply",
|
|
75
|
+
"arguments": {
|
|
76
|
+
"profile": "default",
|
|
77
|
+
"app_key": "APP_123",
|
|
78
|
+
"publish": true,
|
|
79
|
+
"patch_views": [
|
|
80
|
+
{
|
|
81
|
+
"view_key": "VIEW_KEY",
|
|
82
|
+
"set": {
|
|
83
|
+
"query_conditions": {
|
|
84
|
+
"enabled": true,
|
|
85
|
+
"exact": false,
|
|
86
|
+
"hide_before_query": false,
|
|
87
|
+
"rows": [["客户名称", "负责人"], ["创建时间"]]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
"remove_views": []
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Meaning:
|
|
98
|
+
|
|
99
|
+
- `filters` are saved view filters and apply immediately when the view opens.
|
|
100
|
+
- `query_conditions` only configures which fields appear in the frontend query panel. The query values come later from the user through the page.
|
|
101
|
+
- `query_conditions.rows` is a matrix of field names and is compiled to backend `queryCondition` field ids.
|
|
102
|
+
- The patch call preserves the view's existing columns, saved filters, type-specific date/card/board config, buttons, visibility, and other backend-required fields.
|
|
103
|
+
|
|
104
|
+
View associated resources example:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"tool_name": "app_associated_resources_apply",
|
|
109
|
+
"arguments": {
|
|
110
|
+
"profile": "default",
|
|
111
|
+
"app_key": "APP_123",
|
|
112
|
+
"upsert_resources": [],
|
|
113
|
+
"remove_associated_item_ids": [],
|
|
114
|
+
"reorder_associated_item_ids": [],
|
|
115
|
+
"view_configs": [
|
|
116
|
+
{
|
|
117
|
+
"view_key": "VIEW_KEY",
|
|
118
|
+
"visible": true,
|
|
119
|
+
"limit_type": "select",
|
|
120
|
+
"associated_item_ids": [123, 124]
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Use `{"visible": true, "limit_type": "all"}` to show all app-level associated resources, and `{"visible": false}` to hide the area. The ids above can be internal `associated_item_id` values or existing resource `chart_id`/`chart_key`/`view_key` values from `app_get.associated_resources`. Before creating a resource, check whether the same `target_app_key + view_key/chart_key` already exists; if it does, use `patch_resources` with that `associated_item_id`. Dataset BI reports must already exist in QingBI and should be attached with `report_source="dataset"`; do not try to create them through `app_charts_apply`. If you create a new associated item in the same call, give it a `client_key` and reference it from `view_configs[].associated_item_refs`; `client_key` is not persisted and cannot deduplicate a later call.
|
|
128
|
+
|
|
129
|
+
Create and show a BI indicator-card report in the same call:
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"tool_name": "app_associated_resources_apply",
|
|
134
|
+
"arguments": {
|
|
135
|
+
"profile": "default",
|
|
136
|
+
"app_key": "APP_123",
|
|
137
|
+
"upsert_resources": [
|
|
138
|
+
{
|
|
139
|
+
"client_key": "total_hours_card",
|
|
140
|
+
"graph_type": "chart",
|
|
141
|
+
"target_app_key": "TIMESHEET_APP",
|
|
142
|
+
"chart_key": "CHART_KEY",
|
|
143
|
+
"report_source": "app",
|
|
144
|
+
"match_mappings": [
|
|
145
|
+
{"target_field": "关联员工", "source_field": "数据ID"},
|
|
146
|
+
{"target_field": "状态", "value": "待提交"}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
"remove_associated_item_ids": [],
|
|
151
|
+
"reorder_associated_item_ids": [],
|
|
152
|
+
"view_configs": [
|
|
153
|
+
{
|
|
154
|
+
"view_key": "VIEW_KEY",
|
|
155
|
+
"visible": true,
|
|
156
|
+
"limit_type": "select",
|
|
157
|
+
"associated_item_refs": ["total_hours_card"]
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
55
164
|
Board example:
|
|
56
165
|
|
|
57
166
|
```json
|
|
@@ -151,6 +260,25 @@ Treat this as:
|
|
|
151
260
|
|
|
152
261
|
Do not tell the user the filter is active until the readback verification matches the intended filter.
|
|
153
262
|
|
|
263
|
+
### `VIEW_QUERY_CONDITION_READBACK_MISMATCH`
|
|
264
|
+
|
|
265
|
+
The view object was created or updated, but the readback config did not keep the intended frontend query-condition settings.
|
|
266
|
+
|
|
267
|
+
Treat this as:
|
|
268
|
+
|
|
269
|
+
- the view exists
|
|
270
|
+
- the query panel configuration is **not yet verified**
|
|
271
|
+
|
|
272
|
+
Do not tell the user the query condition is active until readback matches the intended `query_conditions`.
|
|
273
|
+
|
|
274
|
+
### `QUERY_CONDITION_FIELD_NOT_FOUND`
|
|
275
|
+
|
|
276
|
+
At least one `query_conditions.rows` field does not exist on the app.
|
|
277
|
+
|
|
278
|
+
### `INVALID_QUERY_CONDITION_FIELD`
|
|
279
|
+
|
|
280
|
+
The field exists but cannot be used in the frontend query-condition panel, such as attachment, relation, Q-Linker, code block, location/address, or subtable/subfield selections.
|
|
281
|
+
|
|
154
282
|
## Notes
|
|
155
283
|
|
|
156
284
|
- `fields` is accepted as an alias for `columns`, but skill examples should still use `columns`
|
|
@@ -158,5 +286,6 @@ Do not tell the user the filter is active until the readback verification matche
|
|
|
158
286
|
- `app_get_views` should be treated as canonical readback and now returns `columns`
|
|
159
287
|
- If `app_views_apply` returns `AMBIGUOUS_VIEW`, stop and re-run `app_get_views`; then retry with the exact `view_key`
|
|
160
288
|
- `filters` are ANDed together as one flat condition group
|
|
289
|
+
- `query_conditions.rows` does not mean OR; it controls frontend query-field layout
|
|
161
290
|
- `app_views_apply` publishes by default
|
|
162
291
|
- For select-style filters, success means the backend preserved the option value in readback, not just that the view name now exists
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qingflow-mcp-setup
|
|
3
|
+
description: Install, connect, authenticate, and troubleshoot the Qingflow MCP server in local AI clients such as Claude Desktop or any stdio-compatible MCP client. Use when the user wants to configure the MCP, verify local startup, log in with token/password, select a workspace, or fix connection/authentication issues.
|
|
4
|
+
metadata:
|
|
5
|
+
short-description: Install and connect the Qingflow MCP locally
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Qingflow MCP Setup
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
This skill sets up the local Qingflow MCP server, connects it to a local AI client, and verifies authentication and workspace selection. Use it for installation, client configuration, token login, and connection troubleshooting.
|
|
13
|
+
|
|
14
|
+
## Workflow
|
|
15
|
+
|
|
16
|
+
Follow these steps in order.
|
|
17
|
+
|
|
18
|
+
`feedback_submit` is also available as a cross-cutting helper when setup or MCP capability gaps still block the user after reasonable troubleshooting. It does not require Qingflow login or workspace selection, and should be called only after explicit user confirmation.
|
|
19
|
+
|
|
20
|
+
Before configuration or live calls, identify the target environment explicitly as `test` or `prod`, then read [references/environments.md](references/environments.md). If the user did not specify one, default to `prod`.
|
|
21
|
+
|
|
22
|
+
### Step 1: Verify the local project path
|
|
23
|
+
|
|
24
|
+
Resolve the repository root first. In this repo, the MCP server should live at:
|
|
25
|
+
|
|
26
|
+
- `<repo_root>/qingflow-support/mcp-server`
|
|
27
|
+
|
|
28
|
+
Key entrypoint:
|
|
29
|
+
|
|
30
|
+
- `<repo_root>/qingflow-support/mcp-server/qingflow-mcp`
|
|
31
|
+
|
|
32
|
+
If the path differs, stop and update all client config snippets before proceeding.
|
|
33
|
+
If the skill is installed under `$CODEX_HOME/skills` instead of the repo-local `.codex/skills`, set `QINGFLOW_MCP_ROOT=<repo_root>/qingflow-support/mcp-server` before running `scripts/check_local_server.sh`.
|
|
34
|
+
|
|
35
|
+
### Step 2: Install local dependencies
|
|
36
|
+
|
|
37
|
+
Run:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
cd <repo_root>/qingflow-support/mcp-server
|
|
41
|
+
python3 -m venv .venv
|
|
42
|
+
./.venv/bin/pip install -e '.[dev]'
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Use `scripts/check_local_server.sh` to verify the entrypoint and virtualenv. The script will first try `QINGFLOW_MCP_ROOT`, then the current git repo, then a repo-local `.codex/skills` layout.
|
|
46
|
+
|
|
47
|
+
### Step 3: Configure the local AI client
|
|
48
|
+
|
|
49
|
+
For any stdio-compatible MCP client, map these values:
|
|
50
|
+
|
|
51
|
+
- `command`: `<repo_root>/qingflow-support/mcp-server/qingflow-mcp`
|
|
52
|
+
- `args`: `[]`
|
|
53
|
+
- `env.QINGFLOW_MCP_DEFAULT_BASE_URL`: the target backend URL for the active environment
|
|
54
|
+
- `env.QINGFLOW_MCP_DEFAULT_QF_VERSION`: set this when the environment must route to a specific version such as `canary`
|
|
55
|
+
|
|
56
|
+
Client-specific snippets:
|
|
57
|
+
|
|
58
|
+
- Claude Desktop: read [references/claude-desktop.md](references/claude-desktop.md)
|
|
59
|
+
- Generic stdio MCP clients: read [references/generic-stdio.md](references/generic-stdio.md)
|
|
60
|
+
|
|
61
|
+
When both test and production are in play, keep separate config snippets or clearly labeled `env` blocks so the user can switch without ambiguity.
|
|
62
|
+
|
|
63
|
+
### Step 4: Validate startup
|
|
64
|
+
|
|
65
|
+
The server is a stdio MCP process. A direct terminal launch may print nothing and wait for a client. That is normal.
|
|
66
|
+
|
|
67
|
+
Manual startup command:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
cd <repo_root>/qingflow-support/mcp-server
|
|
71
|
+
./qingflow-mcp
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Prefer validating through the client after config is added.
|
|
75
|
+
|
|
76
|
+
### Step 5: Authenticate and select workspace
|
|
77
|
+
|
|
78
|
+
Recommended order inside the client:
|
|
79
|
+
|
|
80
|
+
1. `auth_use_token` or `auth_login`
|
|
81
|
+
2. `workspace_list`
|
|
82
|
+
3. `workspace_select`
|
|
83
|
+
4. Only then use business tools
|
|
84
|
+
|
|
85
|
+
If the user already knows the target workspace, prefer `auth_use_token(..., ws_id=<id>)`.
|
|
86
|
+
After auth, prefer one read-only tool call that returns `request_route` so you can confirm the live `base_url` and `qf_version` before proceeding.
|
|
87
|
+
`auth_use_token` is a tool call, not a custom HTTP header. The runtime injects `token`, `wsId`, and when needed `Cookie: qfVersion=...` after the session is established.
|
|
88
|
+
Do not skip `workspace_select` on production paths. If `/user` does not return a version lane, the session now falls back to the workspace `systemVersion`.
|
|
89
|
+
|
|
90
|
+
### Step 6: Troubleshoot in the right layer
|
|
91
|
+
|
|
92
|
+
- If the client cannot launch the server, check path, execute permission, and `.venv`
|
|
93
|
+
- If tools return `auth required`, re-run `auth_use_token` or `auth_login`
|
|
94
|
+
- If tools return `workspace not selected`, call `workspace_list` and `workspace_select`
|
|
95
|
+
- If calls fail after a working session, assume token expiry and re-authenticate
|
|
96
|
+
- If the browser shows data but MCP does not, compare `request_route` against the browser environment and check whether `qf_version` should be `canary`
|
|
97
|
+
|
|
98
|
+
## Guardrails
|
|
99
|
+
|
|
100
|
+
- Never write tokens into the skill files
|
|
101
|
+
- Never assume a workspace is selected until `auth_whoami` or `workspace_select` confirms it
|
|
102
|
+
- When debugging, distinguish server startup issues from Qingflow auth issues
|
|
103
|
+
- When the user mentions production, restate the exact `base_url`, `qf_version`, and workspace before any live validation
|
|
104
|
+
- If setup is complete but the current MCP capability is still unsupported or the user's need still cannot be satisfied, summarize the gap, ask whether to submit feedback, and call `feedback_submit` only after explicit user confirmation
|
|
105
|
+
|
|
106
|
+
## Resources
|
|
107
|
+
|
|
108
|
+
- Environment switching: [references/environments.md](references/environments.md)
|
|
109
|
+
- Claude Desktop config: [references/claude-desktop.md](references/claude-desktop.md)
|
|
110
|
+
- Generic stdio config: [references/generic-stdio.md](references/generic-stdio.md)
|
|
111
|
+
- Local checks: [scripts/check_local_server.sh](scripts/check_local_server.sh)
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Qingflow MCP Setup"
|
|
3
|
+
short_description: "Install and connect the Qingflow MCP locally"
|
|
4
|
+
default_prompt: "Use $qingflow-mcp-setup to install and connect the Qingflow MCP in a local AI client, and to verify the active base_url, qf_version, auth state, and workspace selection."
|