@josephyan/qingflow-app-builder-mcp 1.1.24 → 1.1.26
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 +5 -6
- package/docs/local-agent-install.md +6 -57
- package/entry_point.py +1 -1
- package/npm/bin/qingflow-app-builder-mcp.mjs +33 -2
- package/npm/lib/runtime.mjs +101 -21
- package/npm/scripts/postinstall.mjs +10 -1
- package/package.json +2 -3
- package/pyproject.toml +1 -1
- package/skills/qingflow-app-builder/SKILL.md +42 -104
- 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 +11 -30
- package/skills/qingflow-app-builder/references/environments.md +1 -1
- package/skills/qingflow-app-builder/references/gotchas.md +9 -31
- package/skills/qingflow-app-builder/references/match-rules.md +0 -15
- package/skills/qingflow-app-builder/references/solution-playbooks.md +0 -10
- package/skills/qingflow-app-builder/references/tool-selection.md +23 -38
- package/skills/qingflow-app-builder/references/update-flow.md +38 -22
- package/skills/qingflow-app-builder/references/update-schema.md +0 -14
- package/skills/qingflow-app-builder/references/update-views.md +14 -83
- package/skills/qingflow-app-builder-code-integrations/SKILL.md +3 -5
- package/skills/qingflow-app-builder-code-integrations/references/code-block.md +1 -1
- package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +1 -1
- 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/__main__.py +2 -6
- package/src/qingflow_mcp/builder_facade/models.py +108 -485
- package/src/qingflow_mcp/builder_facade/service.py +1686 -6674
- package/src/qingflow_mcp/cli/commands/builder.py +364 -322
- package/src/qingflow_mcp/cli/commands/chart.py +1 -1
- package/src/qingflow_mcp/cli/commands/common.py +3 -12
- package/src/qingflow_mcp/cli/commands/exports.py +2 -2
- package/src/qingflow_mcp/cli/commands/imports.py +3 -3
- package/src/qingflow_mcp/cli/commands/portal.py +2 -2
- package/src/qingflow_mcp/cli/commands/record.py +27 -101
- package/src/qingflow_mcp/cli/commands/task.py +47 -28
- package/src/qingflow_mcp/cli/commands/view.py +1 -1
- package/src/qingflow_mcp/cli/context.py +3 -0
- package/src/qingflow_mcp/cli/formatters.py +16 -784
- package/src/qingflow_mcp/cli/main.py +33 -117
- package/src/qingflow_mcp/errors.py +2 -43
- package/src/qingflow_mcp/public_surface.py +17 -26
- package/src/qingflow_mcp/response_trim.py +17 -81
- package/src/qingflow_mcp/server.py +12 -14
- package/src/qingflow_mcp/server_app_builder.py +36 -79
- package/src/qingflow_mcp/server_app_user.py +16 -22
- package/src/qingflow_mcp/session_store.py +7 -11
- package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
- package/src/qingflow_mcp/solution/executor.py +18 -245
- package/src/qingflow_mcp/tools/ai_builder_tools.py +1396 -3100
- package/src/qingflow_mcp/tools/app_tools.py +43 -184
- package/src/qingflow_mcp/tools/approval_tools.py +35 -197
- package/src/qingflow_mcp/tools/auth_tools.py +16 -92
- package/src/qingflow_mcp/tools/code_block_tools.py +40 -298
- package/src/qingflow_mcp/tools/custom_button_tools.py +10 -64
- package/src/qingflow_mcp/tools/directory_tools.py +72 -236
- package/src/qingflow_mcp/tools/export_tools.py +34 -244
- package/src/qingflow_mcp/tools/feedback_tools.py +0 -9
- package/src/qingflow_mcp/tools/file_tools.py +3 -9
- package/src/qingflow_mcp/tools/import_tools.py +49 -336
- package/src/qingflow_mcp/tools/navigation_tools.py +12 -91
- package/src/qingflow_mcp/tools/package_tools.py +6 -118
- package/src/qingflow_mcp/tools/portal_tools.py +3 -39
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +7 -116
- package/src/qingflow_mcp/tools/record_tools.py +360 -1145
- package/src/qingflow_mcp/tools/resource_read_tools.py +39 -188
- package/src/qingflow_mcp/tools/role_tools.py +9 -80
- package/src/qingflow_mcp/tools/solution_tools.py +45 -59
- package/src/qingflow_mcp/tools/task_context_tools.py +158 -662
- package/src/qingflow_mcp/tools/task_tools.py +29 -113
- package/src/qingflow_mcp/tools/view_tools.py +3 -106
- package/src/qingflow_mcp/tools/workflow_tools.py +4 -48
- package/src/qingflow_mcp/tools/workspace_tools.py +3 -71
- package/npm/bin/qingflow-skills.mjs +0 -5
- package/skills/qingflow-app-builder/references/complete-system-development-guide.md +0 -172
- package/skills/qingflow-app-builder/references/single-app-development-guide.md +0 -87
- package/skills/qingflow-app-builder/scripts/validate_system_build_summary.py +0 -124
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
|
@@ -50,7 +50,6 @@ All resource tools operate with the logged-in user's Qingflow permissions.
|
|
|
50
50
|
|
|
51
51
|
If `app_key` is unknown, use `app_list` first. Pass `query` to filter visible apps by keyword.
|
|
52
52
|
If the app is known but the data range is not, use `app_get` first and choose from `accessible_views`.
|
|
53
|
-
Treat an explicit `view_id` as the exact frontend view context. If `system:all` fails, do not silently switch to `system:initiated`, `system:todo`, or another system view unless the user, frontend URL, or `app_get.accessible_views` explicitly selects that view.
|
|
54
53
|
If an accessible view has `analysis_supported=false`, do not use it for `record_access` or `record_list`. `boardView` and `ganttView` are special UI views, not data-access targets.
|
|
55
54
|
`view_get(view_id=...)` also returns `export_capability`; it only means there is a supported export route, not that export permission has been verified.
|
|
56
55
|
|
|
@@ -58,7 +57,7 @@ If an accessible view has `analysis_supported=false`, do not use it for `record_
|
|
|
58
57
|
|
|
59
58
|
Call `record_insert_schema_get` before `record_insert`.
|
|
60
59
|
For simple field changes after the target record is clear, call `record_update` directly. Use `record_update_schema_get` for diagnostics, ambiguous fields, or complex writable-scope inspection.
|
|
61
|
-
|
|
60
|
+
Call `record_code_block_schema_get` before `record_code_block_run`.
|
|
62
61
|
Call `app_get` first when the data range is unclear, then use `record_browse_schema_get(view_id=...)` before `record_access`, `record_list`, `record_get`, or `record_logs_get`.
|
|
63
62
|
Call `record_import_schema_get` when the import field mapping is unclear before template download or verify.
|
|
64
63
|
|
|
@@ -68,7 +67,7 @@ Call `record_import_schema_get` when the import field mapping is unclear before
|
|
|
68
67
|
## Schema Scope
|
|
69
68
|
|
|
70
69
|
`record_insert_schema_get` returns the current user's insert-ready applicant schema; read `required_fields`, `optional_fields`, `runtime_linked_required_fields`, and `payload_template`.
|
|
71
|
-
`record_update_schema_get` returns the current record's update-ready writable field set and route diagnostics
|
|
70
|
+
`record_update_schema_get` returns the current record's overall update-ready writable field set and route diagnostics across matched accessible views; read `writable_fields`, `payload_template`, `available_update_routes`, and `recommended_update_route`.
|
|
72
71
|
`record_browse_schema_get(view_id=...)` returns the same readable fields shown in the selected Qingflow table view header.
|
|
73
72
|
`record_access.fields` / CSV columns and `record_list.columns / where / order_by / query_fields` use that exact same view schema.
|
|
74
73
|
`record_code_block_schema_get` returns code-block-ready schema for exact code block field selection.
|
|
@@ -107,8 +106,8 @@ Analysis answers must include concrete numbers. When applicable, include percent
|
|
|
107
106
|
`app_get -> record_browse_schema_get(view_id=...) -> record_list / record_get / record_logs_get`
|
|
108
107
|
`record_insert_schema_get -> record_insert(items)`
|
|
109
108
|
`record_update` for simple updates; `record_update_schema_get -> record_update` when the writable field scope is unclear.
|
|
110
|
-
`record_list / record_get -> record_delete
|
|
111
|
-
`
|
|
109
|
+
`record_list / record_get -> record_delete`
|
|
110
|
+
`record_code_block_schema_get -> record_code_block_run`
|
|
112
111
|
|
|
113
112
|
- Use `columns` as `[{{field_id}}]`
|
|
114
113
|
- Use `record_list(query=..., query_fields=[{{field_id}}])` for fuzzy single-record lookup, then follow `lookup.next_action`; `query_fields` is search scope and `columns` is display shape.
|
|
@@ -117,14 +116,14 @@ Analysis answers must include concrete numbers. When applicable, include percent
|
|
|
117
116
|
- Legacy forms such as bare integer `field_id`, `fieldId`, `operator`, `values`, or `order` may still parse, but they are compatibility-only and not the canonical DSL
|
|
118
117
|
|
|
119
118
|
- `record_insert` defaults to an applicant-node `items` array; each item contains a field-title keyed `fields` map. A single insert is one item.
|
|
120
|
-
- `record_update` uses a field-title keyed `fields` map. It first tries the data-manager direct update route, then falls back to the frontend custom-view detail edit route when the selected view can cover the payload; if a unique current-user todo task for the same record exposes editable fields, it can finally use the workflow save-only route.
|
|
119
|
+
- `record_update` uses a field-title keyed `fields` map. It first tries the data-manager direct update route, then falls back to the frontend custom-view detail edit route when the selected view can cover the payload; if a unique current-user todo task for the same record exposes editable fields, it can finally use the workflow save-only route. Read `update_route` and `tried_routes` after execution.
|
|
121
120
|
- For insert, `runtime_linked_required_fields` means required-but-not-directly-writable fields that are usually supplied by runtime linkage or upstream context.
|
|
122
121
|
- For insert, fields marked `may_become_required=true` stay in `optional_fields`; they are still directly writable, but linked visibility or option-driven rules can make them required at runtime.
|
|
123
122
|
- Read field-level `linkage` whenever present on `record_insert_schema_get` or `record_update_schema_get`; it is the static hint for linked visibility, reference-driven auto fill, and formula/default auto-fill behavior.
|
|
124
123
|
- `linkage.sources` lists upstream field titles that influence the current field; `linkage.affects_fields` lists downstream fields that may change when the current field changes.
|
|
125
124
|
- `linkage.kind=logic_visibility` means linked visibility or option-driven rules are involved; `linkage.kind=reference_fill` means reference/default matching logic is involved; `linkage.kind=formula_fill` means formula/default auto-fill logic is involved.
|
|
126
|
-
- `record_update_schema_get` exposes the writable field set and route candidates for the record
|
|
127
|
-
- `record_delete` deletes by `record_id` or `record_ids
|
|
125
|
+
- `record_update_schema_get` exposes the overall writable field set and route candidates for the record, but not every field combination is guaranteed; `record_update` still needs data-manager permission, one single matched custom view that can cover the payload, or one unique editable current-user todo task.
|
|
126
|
+
- `record_delete` deletes by `record_id` or `record_ids`.
|
|
128
127
|
- `record_get` is the single-record frontend detail context tool. It returns detail-page visible fields, one-level relation targets, first-page data/workflow logs, associated views/reports, local readable image assets, local downloadable file assets, unavailable context, and `semantic_context`.
|
|
129
128
|
- Use `record_logs_get` only when the user needs the full visible data/workflow log history for a specific record. It writes JSONL files locally and returns file paths plus completeness metadata; do not expect full log arrays in the response.
|
|
130
129
|
- Read record images from `record_get.media_assets.items[].local_path` when `readable_by_agent=true`; read attachments/documents/tables from `record_get.file_assets.items[].local_path` and `extraction.text_path` when present. `record_get` follows the frontend storage cookie redirect path for Qingflow attachments, and remote file URLs should not be treated as directly readable.
|
|
@@ -141,8 +140,7 @@ Analysis answers must include concrete numbers. When applicable, include percent
|
|
|
141
140
|
|
|
142
141
|
Use `record_code_block_run` when the user wants to execute a form code-block field against an existing record.
|
|
143
142
|
|
|
144
|
-
-
|
|
145
|
-
- `record_code_block_run` uses the record/task detail answers for the execution context; when applicant schema is unavailable it can still execute with a numeric code-block field id, but schema-bound relation writeback may be skipped.
|
|
143
|
+
- Always resolve the exact code-block field from `record_code_block_schema_get` first.
|
|
146
144
|
- Treat code-block execution as write-capable, not read-only.
|
|
147
145
|
- If the code block is bound to relation outputs, Qingflow may calculate target answers and write them back automatically.
|
|
148
146
|
- For safe debugging, pass `apply_writeback=false` and inspect the parsed alias results plus `relation.calculated_answers_preview` before allowing any writeback.
|
|
@@ -184,12 +182,12 @@ Use `record_code_block_run` when the user wants to execute a form code-block fie
|
|
|
184
182
|
`task_list -> task_get -> task_action_execute`
|
|
185
183
|
|
|
186
184
|
- `task_list` returns task-card summaries keyed by `task_id`.
|
|
187
|
-
-
|
|
188
|
-
- `task_id` is
|
|
189
|
-
- `task_action_execute` only uses `task_id`; do not reconstruct or pass `app_key + record_id + workflow_node_id` for actions.
|
|
185
|
+
- Prefer `task_get(task_id=...)` for detail reads; MCP resolves the current todo locator internally.
|
|
186
|
+
- `task_action_execute(task_id=..., action=...)` is also supported; MCP resolves the current todo locator internally before calling the real action route.
|
|
190
187
|
- `task_workflow_log_get(task_id=...)` and `task_associated_report_detail_get(task_id=...)` are also supported for the current todo context.
|
|
191
188
|
- Use `task_associated_report_detail_get` for associated view or report details.
|
|
192
|
-
- Use `task_workflow_log_get` for the current task context workflow log page. For full record-level data/workflow logs,
|
|
189
|
+
- Use `task_workflow_log_get` for the current task context workflow log page. For full record-level data/workflow logs, use `record_logs_get(app_key, record_id, view_id?)`.
|
|
190
|
+
- Task actions operate on `app_key + record_id + workflow_node_id`, not `task_id`.
|
|
193
191
|
|
|
194
192
|
## Time Handling
|
|
195
193
|
|
|
@@ -37,22 +37,21 @@ def build_builder_server() -> FastMCP:
|
|
|
37
37
|
"Use solution_install when the user explicitly wants to install a packaged solution/template by solution_key, optionally copying bundled demo data. "
|
|
38
38
|
"Use package_list to find visible app packages by keyword and package_get to read package detail before editing; if creating or updating an app package may be appropriate, use package_apply with explicit user intent; otherwise use app_resolve to locate app resources, "
|
|
39
39
|
"Use workspace_icon_catalog_get before creating app packages, apps, or portals when supported icon/color candidates are needed; new workspace resources require explicit non-template icon + color, and the CLI validates choices without inferring business defaults. "
|
|
40
|
-
"app_get as the default app map read, then app_get_fields/app_repair_code_blocks/app_get_layout/app_get_views/app_get_flow/app_get_charts/portal_list/portal_get/view_get/chart_get for focused configuration reads, "
|
|
40
|
+
"app_get as the default app map read, then app_get_fields/app_repair_code_blocks/app_get_layout/app_get_views/app_get_flow/app_flow_get/app_flow_get_schema/app_get_charts/portal_list/portal_get/view_get/chart_get for focused configuration reads, "
|
|
41
41
|
"member_search/role_search/role_create when workflow assignees must come from the directory or role catalog, preferring roles over explicit members unless the user explicitly names members, "
|
|
42
|
-
"then app_schema_apply/app_layout_apply/app_flow_apply/app_views_apply/app_custom_buttons_apply/app_associated_resources_apply/app_charts_apply/portal_apply
|
|
42
|
+
"then app_schema_apply/app_layout_apply/app_flow_apply/app_views_apply/app_custom_buttons_apply/app_associated_resources_apply/app_charts_apply/portal_apply to execute normalized patches; these apply tools perform planning, normalization, and dependency checks internally where applicable. Schema/layout/views noop requests skip publish, app_custom_buttons_apply and app_associated_resources_apply publish after at least one write succeeds and expose no draft-only parameter, charts are immediate-live without publish and resolve targets by chart_id first then exact unique chart name, portal sections should use chart for QingBI blocks so portal_apply can inline create/update app-source charts before saving the portal, portal updates use replace semantics only when sections are supplied and edit-mode base-info-only updates may omit sections, portal pc layout is a 24-column grid and mobile is a 6-column grid so omit position or use layout_preset when unsure, publish=false only guarantees draft/base-info updates for tools that still expose that parameter, and flow should use publish=false whenever you only want draft/precheck behavior. "
|
|
43
43
|
"Builder apply/write outputs include schema_version, operation, summary, and resources[]; use resources[].id/key/name/ids/parent as the stable UI and agent display entry, and keep legacy fields such as field_diff/views_diff/chart_results only for compatibility or troubleshooting. "
|
|
44
44
|
"For existing object parameter replacement, prefer patch_views, patch_buttons, patch_resources, and patch_charts with set/unset; the tool reads current config and full-saves internally, while upsert_* is for creation or full target configuration and should not be used as an incomplete partial update. "
|
|
45
45
|
"For builder delete/remove apply results, separate delete execution from readback verification: after DELETE is sent, resources expose delete_executed, readback_status, and safe_to_retry_delete=false. If readback_status is unavailable or still_exists, do not blindly repeat the delete; confirm later with app_get/view_get/chart_get or the relevant apply readback. Views/buttons use single-item readback; associated resources use one app-level resource-pool readback because there is no confirmed single-item GET. "
|
|
46
|
-
"For builder write/apply results, separate write execution from final readback verification: status=partial_success with write_executed=true and safe_to_retry=false means the write was sent or succeeded, while final readback, publish verification, or metadata confirmation is pending or permission-restricted. Report it as written but unverified; do not repeat the same write blindly. "
|
|
47
46
|
"For app_schema_apply, configure data title and data cover directly in field JSON with as_data_title=true and as_data_cover=true; data title is required and exactly one field may be marked, while data cover is optional and must be a top-level attachment field. For multi-app creation, pass apps[]/--apps-file on app_schema_apply; each item may have client_key, and relation fields may use target_app_ref to point at another same-call client_key. "
|
|
48
|
-
"For app_views_apply, keep fixed saved filters in filters and configure the frontend query panel separately with query_conditions; query_conditions.rows is a matrix of field names compiled to backend queryCondition queIds. New views default associated report/view display to visible with limit_type=all; existing views preserve their current associated display unless associated_resources is explicitly patched.
|
|
49
|
-
"For custom button body create/update/delete and view placement, use app_custom_buttons_apply.
|
|
50
|
-
"For BI
|
|
47
|
+
"For app_views_apply, keep fixed saved filters in filters and configure the frontend query panel separately with query_conditions; query_conditions.rows is a matrix of field names compiled to backend queryCondition queIds. New views default associated report/view display to visible with limit_type=all; existing views preserve their current associated display unless associated_resources is explicitly patched. "
|
|
48
|
+
"For custom button body create/update/delete and view placement, use app_custom_buttons_apply. For addData buttons, prefer trigger_add_data_config.target_app_key + field_mappings/default_values; do not ask agents to write raw que_relation unless maintaining a legacy config. field_mappings.source_field accepts source schema fields and supported system fields: 数据ID/row_record_id/apply_id/_id means current record id (-17), 编号/record_number means visible record number (0). To fill a target relation with the current record, map {'source_field': '数据ID', 'target_field': '目标引用字段'}; default_values is only for static constants. View button bindings merge by default and merge-mode view_configs must include buttons; use view_configs[].mode=replace or explicit buttons=[] only when clearing/replacing existing bindings is intended. Builder view_key arguments are raw keys from app_get.views[].view_key and must not be prefixed with custom:. "
|
|
49
|
+
"For portal BI blocks, prefer portal_apply sections[].chart so the portal write can inline create/update app-source QingBI charts and attach the resulting chart_id. Use app_charts_apply for advanced standalone report maintenance such as batch edits, deletes, or reordering; chart dimension/metric/filter/query fields must come from app_get_fields.chart_fields, not record schema or form-only fields; dataset BI reports are not created or edited by app_charts_apply yet and should be created in QingBI first, then attached with app_associated_resources_apply using report_source=dataset. "
|
|
51
50
|
"For associated views/reports, use app_associated_resources_apply. Use match_mappings for filtering associated resources: dynamic current-record conditions use source_field, static conditions use value. match_mappings also supports 数据ID(-17) and 编号(0). Do not ask agents to write raw match_rules unless preserving a legacy backend config. "
|
|
52
51
|
"For associated reports/views, use app_associated_resources_apply for both the app-level associated_resources pool and per-view display config; associated_item_id is the app-level form_asos_chart.id, and view_configs/remove/reorder may also pass an existing resource's chart_id/chart_key/view_key because the tool resolves those to the internal id. Before creating an associated resource, read app_get.associated_resources and reuse an existing matching target_app_key + view_key/chart_key through patch_resources; client_key only works inside one apply call and is not persisted. Do not ask agents to pass backend raw sourceType: views infer the internal Qingflow view source, reports default to BI app reports, and dataset reports use report_source=dataset. "
|
|
53
52
|
"For code_block fields with output bindings, always use qf_output assignment rather than const/let qf_output, and use app_repair_code_blocks when an existing form hangs because output-bound fields stay loading. "
|
|
54
53
|
"Use package_apply to manage package metadata, visibility, grouping, and ordering, and app_publish_verify for explicit final publish verification. "
|
|
55
|
-
"For workflow edits,
|
|
54
|
+
"For workflow edits, use app_flow_get_schema and app_flow_get (GET-first), author a complete WorkflowSpecDTO in spec, then app_flow_apply; CLI equivalent is qingflow builder flow schema|get|apply --spec-file. Do not use general-server legacy workflow config reads (auditNodes list/detail, global settings, qsource config reads); they are removed. "
|
|
56
55
|
"If builder writes are blocked by the current user's own edit lock, use app_release_edit_lock_if_mine with the lock owner details from the failed result. "
|
|
57
56
|
"Do not handcraft internal solution payloads or rely on build_id/stage/repair. "
|
|
58
57
|
"If the current MCP capability is unsupported, the workflow is awkward, or the user's need still cannot be satisfied after reasonable use, first summarize the gap, ask whether to submit feedback, and call feedback_submit only after explicit user confirmation."
|
|
@@ -197,6 +196,7 @@ def build_builder_server() -> FastMCP:
|
|
|
197
196
|
profile: str = DEFAULT_PROFILE,
|
|
198
197
|
package_id: int | None = None,
|
|
199
198
|
package_name: str | None = None,
|
|
199
|
+
create_if_missing: bool = False,
|
|
200
200
|
icon: str | None = None,
|
|
201
201
|
color: str | None = None,
|
|
202
202
|
visibility: dict | None = None,
|
|
@@ -207,6 +207,7 @@ def build_builder_server() -> FastMCP:
|
|
|
207
207
|
profile=profile,
|
|
208
208
|
package_id=package_id,
|
|
209
209
|
package_name=package_name,
|
|
210
|
+
create_if_missing=create_if_missing,
|
|
210
211
|
icon=icon,
|
|
211
212
|
color=color,
|
|
212
213
|
visibility=visibility,
|
|
@@ -319,7 +320,6 @@ def build_builder_server() -> FastMCP:
|
|
|
319
320
|
patch_buttons: list[dict] | None = None,
|
|
320
321
|
remove_buttons: list[dict] | None = None,
|
|
321
322
|
view_configs: list[dict] | None = None,
|
|
322
|
-
apps: list[dict] | None = None,
|
|
323
323
|
) -> dict:
|
|
324
324
|
return ai_builder.app_custom_buttons_apply(
|
|
325
325
|
profile=profile,
|
|
@@ -328,7 +328,6 @@ def build_builder_server() -> FastMCP:
|
|
|
328
328
|
patch_buttons=patch_buttons or [],
|
|
329
329
|
remove_buttons=remove_buttons or [],
|
|
330
330
|
view_configs=view_configs or [],
|
|
331
|
-
apps=apps,
|
|
332
331
|
)
|
|
333
332
|
|
|
334
333
|
@server.tool()
|
|
@@ -340,7 +339,6 @@ def build_builder_server() -> FastMCP:
|
|
|
340
339
|
remove_associated_item_ids: list[int] | None = None,
|
|
341
340
|
reorder_associated_item_ids: list[int] | None = None,
|
|
342
341
|
view_configs: list[dict] | None = None,
|
|
343
|
-
apps: list[dict] | None = None,
|
|
344
342
|
) -> dict:
|
|
345
343
|
return ai_builder.app_associated_resources_apply(
|
|
346
344
|
profile=profile,
|
|
@@ -350,16 +348,15 @@ def build_builder_server() -> FastMCP:
|
|
|
350
348
|
remove_associated_item_ids=remove_associated_item_ids or [],
|
|
351
349
|
reorder_associated_item_ids=reorder_associated_item_ids or [],
|
|
352
350
|
view_configs=view_configs or [],
|
|
353
|
-
apps=apps,
|
|
354
351
|
)
|
|
355
352
|
|
|
356
353
|
@server.tool()
|
|
357
|
-
def app_get(profile: str = DEFAULT_PROFILE, app_key: str = ""
|
|
358
|
-
return ai_builder.app_get(profile=profile, app_key=app_key
|
|
354
|
+
def app_get(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
355
|
+
return ai_builder.app_get(profile=profile, app_key=app_key)
|
|
359
356
|
|
|
360
357
|
@server.tool()
|
|
361
|
-
def app_get_fields(profile: str = DEFAULT_PROFILE, app_key: str = ""
|
|
362
|
-
return ai_builder.app_get_fields(profile=profile, app_key=app_key
|
|
358
|
+
def app_get_fields(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
359
|
+
return ai_builder.app_get_fields(profile=profile, app_key=app_key)
|
|
363
360
|
|
|
364
361
|
@server.tool()
|
|
365
362
|
def app_repair_code_blocks(
|
|
@@ -371,28 +368,20 @@ def build_builder_server() -> FastMCP:
|
|
|
371
368
|
return ai_builder.app_repair_code_blocks(profile=profile, app_key=app_key, field=field, apply=apply)
|
|
372
369
|
|
|
373
370
|
@server.tool()
|
|
374
|
-
def app_get_layout(profile: str = DEFAULT_PROFILE, app_key: str = ""
|
|
375
|
-
return ai_builder.app_get_layout(profile=profile, app_key=app_key
|
|
376
|
-
|
|
377
|
-
@server.tool()
|
|
378
|
-
def app_get_views(profile: str = DEFAULT_PROFILE, app_key: str = "", app_keys: list[str] | None = None) -> dict:
|
|
379
|
-
return ai_builder.app_get_views(profile=profile, app_key=app_key, app_keys=app_keys)
|
|
380
|
-
|
|
381
|
-
@server.tool()
|
|
382
|
-
def app_get_flow(profile: str = DEFAULT_PROFILE, app_key: str = "", app_keys: list[str] | None = None) -> dict:
|
|
383
|
-
return ai_builder.app_get_flow(profile=profile, app_key=app_key, app_keys=app_keys)
|
|
371
|
+
def app_get_layout(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
372
|
+
return ai_builder.app_get_layout(profile=profile, app_key=app_key)
|
|
384
373
|
|
|
385
374
|
@server.tool()
|
|
386
|
-
def
|
|
387
|
-
return ai_builder.
|
|
375
|
+
def app_get_views(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
376
|
+
return ai_builder.app_get_views(profile=profile, app_key=app_key)
|
|
388
377
|
|
|
389
378
|
@server.tool()
|
|
390
|
-
def
|
|
391
|
-
return ai_builder.
|
|
379
|
+
def app_get_flow(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
380
|
+
return ai_builder.app_get_flow(profile=profile, app_key=app_key)
|
|
392
381
|
|
|
393
382
|
@server.tool()
|
|
394
|
-
def
|
|
395
|
-
return ai_builder.
|
|
383
|
+
def app_get_charts(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
384
|
+
return ai_builder.app_get_charts(profile=profile, app_key=app_key)
|
|
396
385
|
|
|
397
386
|
@server.tool()
|
|
398
387
|
def portal_list(profile: str = DEFAULT_PROFILE) -> dict:
|
|
@@ -424,18 +413,18 @@ def build_builder_server() -> FastMCP:
|
|
|
424
413
|
icon: str = "",
|
|
425
414
|
color: str = "",
|
|
426
415
|
visibility: dict | None = None,
|
|
416
|
+
create_if_missing: bool = False,
|
|
427
417
|
publish: bool = True,
|
|
428
|
-
form: list[dict] | None = None,
|
|
429
418
|
add_fields: list[dict] | None = None,
|
|
430
419
|
update_fields: list[dict] | None = None,
|
|
431
420
|
remove_fields: list[dict] | None = None,
|
|
432
421
|
apps: list[dict] | None = None,
|
|
433
422
|
) -> dict:
|
|
434
423
|
if apps:
|
|
435
|
-
if app_key or app_name or app_title or
|
|
424
|
+
if app_key or app_name or app_title or add_fields or update_fields or remove_fields:
|
|
436
425
|
return _config_failure(
|
|
437
|
-
"app_schema_apply multi-app mode accepts package_id plus apps only.",
|
|
438
|
-
fix_hint="Use apps
|
|
426
|
+
"app_schema_apply multi-app mode accepts package_id/create_if_missing plus apps only.",
|
|
427
|
+
fix_hint="Use `apps` for batch mode, or use the single-app arguments without `apps`.",
|
|
439
428
|
)
|
|
440
429
|
if package_id is None:
|
|
441
430
|
return _config_failure(
|
|
@@ -446,8 +435,8 @@ def build_builder_server() -> FastMCP:
|
|
|
446
435
|
profile=profile,
|
|
447
436
|
package_id=package_id,
|
|
448
437
|
visibility=visibility,
|
|
438
|
+
create_if_missing=create_if_missing,
|
|
449
439
|
publish=publish,
|
|
450
|
-
form=None,
|
|
451
440
|
add_fields=[],
|
|
452
441
|
update_fields=[],
|
|
453
442
|
remove_fields=[],
|
|
@@ -457,17 +446,16 @@ def build_builder_server() -> FastMCP:
|
|
|
457
446
|
has_app_name = bool((app_name or "").strip())
|
|
458
447
|
has_app_title = bool((app_title or "").strip())
|
|
459
448
|
has_package_id = package_id is not None
|
|
460
|
-
create_by_shape = has_package_id and (has_app_name or has_app_title)
|
|
461
449
|
if has_app_key:
|
|
462
|
-
if has_package_id:
|
|
450
|
+
if create_if_missing or has_package_id:
|
|
463
451
|
return _config_failure(
|
|
464
452
|
"app_schema_apply edit mode accepts app_key and optional app_name rename only.",
|
|
465
|
-
fix_hint="For existing apps, use `app_key` and optionally `app_name`. For create mode, use `package_id + app_name`.",
|
|
453
|
+
fix_hint="For existing apps, use `app_key` and optionally `app_name`. For create mode, use `package_id + app_name + create_if_missing=true`.",
|
|
466
454
|
)
|
|
467
|
-
elif not
|
|
455
|
+
elif not (create_if_missing and has_package_id and (has_app_name or has_app_title)):
|
|
468
456
|
return _config_failure(
|
|
469
|
-
"app_schema_apply create mode requires package_id and
|
|
470
|
-
fix_hint="Use `app_key` for existing apps, or pass `package_id + app_name` to create a new app.",
|
|
457
|
+
"app_schema_apply create mode requires package_id, app_name, and create_if_missing=true.",
|
|
458
|
+
fix_hint="Use `app_key` for existing apps, or pass `package_id + app_name + create_if_missing=true` to create a new app.",
|
|
471
459
|
)
|
|
472
460
|
return ai_builder.app_schema_apply(
|
|
473
461
|
profile=profile,
|
|
@@ -478,12 +466,12 @@ def build_builder_server() -> FastMCP:
|
|
|
478
466
|
icon=icon,
|
|
479
467
|
color=color,
|
|
480
468
|
visibility=visibility,
|
|
469
|
+
create_if_missing=create_if_missing,
|
|
481
470
|
publish=publish,
|
|
482
|
-
form=form or [],
|
|
483
471
|
add_fields=add_fields or [],
|
|
484
472
|
update_fields=update_fields or [],
|
|
485
473
|
remove_fields=remove_fields or [],
|
|
486
|
-
apps=
|
|
474
|
+
apps=[],
|
|
487
475
|
)
|
|
488
476
|
|
|
489
477
|
@server.tool()
|
|
@@ -493,17 +481,12 @@ def build_builder_server() -> FastMCP:
|
|
|
493
481
|
mode: str = "merge",
|
|
494
482
|
publish: bool = True,
|
|
495
483
|
sections: list[dict] | None = None,
|
|
496
|
-
apps: list[dict] | None = None,
|
|
497
484
|
) -> dict:
|
|
498
|
-
return ai_builder.app_layout_apply(profile=profile, app_key=app_key, mode=mode, publish=publish, sections=sections or []
|
|
485
|
+
return ai_builder.app_layout_apply(profile=profile, app_key=app_key, mode=mode, publish=publish, sections=sections or [])
|
|
499
486
|
|
|
500
487
|
@server.tool()
|
|
501
|
-
def app_flow_get(
|
|
502
|
-
profile
|
|
503
|
-
app_key: str = "",
|
|
504
|
-
version_id: str = "",
|
|
505
|
-
) -> dict:
|
|
506
|
-
return ai_builder.app_flow_get(profile=profile, app_key=app_key, version_id=version_id or None)
|
|
488
|
+
def app_flow_get(profile: str = DEFAULT_PROFILE, app_key: str = "", version_id: str = "") -> dict:
|
|
489
|
+
return ai_builder.app_get_flow(profile=profile, app_key=app_key, version_id=version_id or None)
|
|
507
490
|
|
|
508
491
|
@server.tool()
|
|
509
492
|
def app_flow_get_schema(profile: str = DEFAULT_PROFILE, schema_version: str = "") -> dict:
|
|
@@ -513,26 +496,18 @@ def build_builder_server() -> FastMCP:
|
|
|
513
496
|
def app_flow_apply(
|
|
514
497
|
profile: str = DEFAULT_PROFILE,
|
|
515
498
|
app_key: str = "",
|
|
516
|
-
mode: str = "replace",
|
|
517
499
|
publish: bool = True,
|
|
518
|
-
nodes: list[dict] | None = None,
|
|
519
|
-
transitions: list[dict] | None = None,
|
|
520
500
|
spec: dict | None = None,
|
|
521
501
|
idempotency_key: str = "",
|
|
522
502
|
schema_version: str = "",
|
|
523
|
-
patch_nodes: list[dict] | None = None,
|
|
524
503
|
) -> dict:
|
|
525
504
|
return ai_builder.app_flow_apply(
|
|
526
505
|
profile=profile,
|
|
527
506
|
app_key=app_key,
|
|
528
|
-
mode=mode,
|
|
529
507
|
publish=publish,
|
|
530
|
-
|
|
531
|
-
transitions=transitions or [],
|
|
532
|
-
spec=spec,
|
|
508
|
+
spec=spec or {},
|
|
533
509
|
idempotency_key=idempotency_key or None,
|
|
534
510
|
schema_version=schema_version or None,
|
|
535
|
-
patch_nodes=patch_nodes,
|
|
536
511
|
)
|
|
537
512
|
|
|
538
513
|
@server.tool()
|
|
@@ -543,7 +518,6 @@ def build_builder_server() -> FastMCP:
|
|
|
543
518
|
upsert_views: list[dict] | None = None,
|
|
544
519
|
patch_views: list[dict] | None = None,
|
|
545
520
|
remove_views: list[str] | None = None,
|
|
546
|
-
apps: list[dict] | None = None,
|
|
547
521
|
) -> dict:
|
|
548
522
|
return ai_builder.app_views_apply(
|
|
549
523
|
profile=profile,
|
|
@@ -552,7 +526,6 @@ def build_builder_server() -> FastMCP:
|
|
|
552
526
|
upsert_views=upsert_views or [],
|
|
553
527
|
patch_views=patch_views or [],
|
|
554
528
|
remove_views=remove_views or [],
|
|
555
|
-
apps=apps,
|
|
556
529
|
)
|
|
557
530
|
|
|
558
531
|
@server.tool()
|
|
@@ -563,7 +536,6 @@ def build_builder_server() -> FastMCP:
|
|
|
563
536
|
patch_charts: list[dict] | None = None,
|
|
564
537
|
remove_chart_ids: list[str] | None = None,
|
|
565
538
|
reorder_chart_ids: list[str] | None = None,
|
|
566
|
-
apps: list[dict] | None = None,
|
|
567
539
|
) -> dict:
|
|
568
540
|
return ai_builder.app_charts_apply(
|
|
569
541
|
profile=profile,
|
|
@@ -572,7 +544,6 @@ def build_builder_server() -> FastMCP:
|
|
|
572
544
|
patch_charts=patch_charts or [],
|
|
573
545
|
remove_chart_ids=remove_chart_ids or [],
|
|
574
546
|
reorder_chart_ids=reorder_chart_ids or [],
|
|
575
|
-
apps=apps,
|
|
576
547
|
)
|
|
577
548
|
|
|
578
549
|
@server.tool()
|
|
@@ -594,7 +565,6 @@ def build_builder_server() -> FastMCP:
|
|
|
594
565
|
dash_global_config: dict | None = None,
|
|
595
566
|
config: dict | None = None,
|
|
596
567
|
payload: dict | None = None,
|
|
597
|
-
patch_sections: list[dict] | None = None,
|
|
598
568
|
) -> dict:
|
|
599
569
|
payload = payload if isinstance(payload, dict) else {}
|
|
600
570
|
has_dash_key = bool((dash_key or "").strip())
|
|
@@ -630,30 +600,17 @@ def build_builder_server() -> FastMCP:
|
|
|
630
600
|
dash_global_config=dash_global_config,
|
|
631
601
|
config=config or {},
|
|
632
602
|
payload=payload,
|
|
633
|
-
patch_sections=patch_sections,
|
|
634
|
-
)
|
|
635
|
-
|
|
636
|
-
@server.tool()
|
|
637
|
-
def portal_delete(
|
|
638
|
-
profile: str = DEFAULT_PROFILE,
|
|
639
|
-
dash_key: str = "",
|
|
640
|
-
) -> dict:
|
|
641
|
-
return ai_builder.portal_delete(
|
|
642
|
-
profile=profile,
|
|
643
|
-
dash_key=dash_key,
|
|
644
603
|
)
|
|
645
604
|
|
|
646
605
|
@server.tool()
|
|
647
606
|
def app_publish_verify(
|
|
648
607
|
profile: str = DEFAULT_PROFILE,
|
|
649
608
|
app_key: str = "",
|
|
650
|
-
app_keys: list[str] | None = None,
|
|
651
609
|
expected_package_id: int | None = None,
|
|
652
610
|
) -> dict:
|
|
653
611
|
return ai_builder.app_publish_verify(
|
|
654
612
|
profile=profile,
|
|
655
613
|
app_key=app_key,
|
|
656
|
-
app_keys=app_keys,
|
|
657
614
|
expected_package_id=expected_package_id,
|
|
658
615
|
)
|
|
659
616
|
|
|
@@ -33,7 +33,6 @@ def build_user_server() -> FastMCP:
|
|
|
33
33
|
|
|
34
34
|
If `app_key` is unknown, use `app_list` first. Pass `query` to filter visible apps by keyword.
|
|
35
35
|
If the app is known but the data range is not, use `app_get` first and choose from `accessible_views`.
|
|
36
|
-
Treat an explicit `view_id` as the exact frontend view context. If `system:all` fails, do not silently switch to `system:initiated`, `system:todo`, or another system view unless the user, frontend URL, or `app_get.accessible_views` explicitly selects that view.
|
|
37
36
|
If an accessible view has `analysis_supported=false`, do not use it for `record_access` or `record_list`. `boardView` and `ganttView` are special UI views, not data-access targets.
|
|
38
37
|
`view_get(view_id=...)` also returns `export_capability`; it only means there is a supported export route, not that export permission has been verified.
|
|
39
38
|
|
|
@@ -49,7 +48,7 @@ If an accessible view has `analysis_supported=false`, do not use it for `record_
|
|
|
49
48
|
|
|
50
49
|
Call `record_insert_schema_get` before `record_insert`.
|
|
51
50
|
For simple field changes after the target record is clear, call `record_update` directly. Use `record_update_schema_get` for diagnostics, ambiguous fields, or complex writable-scope inspection.
|
|
52
|
-
|
|
51
|
+
Call `record_code_block_schema_get` before `record_code_block_run`.
|
|
53
52
|
Call `app_get` first when the data range is unclear, then use `record_browse_schema_get(view_id=...)` before `record_access`, `record_list`, `record_get`, or `record_logs_get`.
|
|
54
53
|
Call `record_import_schema_get` when the import field mapping is unclear before template download or verify.
|
|
55
54
|
|
|
@@ -60,11 +59,11 @@ Call `record_import_schema_get` when the import field mapping is unclear before
|
|
|
60
59
|
|
|
61
60
|
`record_insert_schema_get` returns the current user's insert-ready applicant schema; read `required_fields`, `optional_fields`, `runtime_linked_required_fields`, and `payload_template`.
|
|
62
61
|
Inside `optional_fields`, any field with `may_become_required=true` is still writable, but may become required when linked visibility or option-driven runtime rules activate.
|
|
63
|
-
`record_update_schema_get` returns the current record's update-ready writable field set and route diagnostics
|
|
62
|
+
`record_update_schema_get` returns the current record's overall update-ready writable field set and route diagnostics across matched accessible views; read `writable_fields`, `payload_template`, `available_update_routes`, and `recommended_update_route`.
|
|
64
63
|
`record_browse_schema_get(view_id=...)` returns the same readable fields shown in the selected Qingflow table view header.
|
|
65
64
|
`record_access.fields` / CSV columns and `record_list.columns / where / order_by / query_fields` use that exact same view schema; a missing field means it is not readable in that view.
|
|
66
65
|
`searchQueIds` is a backend full-text search scope, not an output-column/projection mechanism.
|
|
67
|
-
`record_code_block_schema_get` returns code-block-ready schema for exact code block field selection
|
|
66
|
+
`record_code_block_schema_get` returns code-block-ready schema for exact code block field selection.
|
|
68
67
|
`record_import_schema_get` returns import-ready column metadata.
|
|
69
68
|
|
|
70
69
|
- Hidden fields are omitted.
|
|
@@ -106,8 +105,8 @@ Analysis answers must include concrete numbers. When applicable, include percent
|
|
|
106
105
|
`app_get -> record_browse_schema_get(view_id=...) -> record_list / record_get / record_logs_get`
|
|
107
106
|
`record_insert_schema_get -> record_insert(items)`
|
|
108
107
|
`record_update` for simple updates; `record_update_schema_get -> record_update` when the writable field scope is unclear.
|
|
109
|
-
`record_list / record_get -> record_delete
|
|
110
|
-
`
|
|
108
|
+
`record_list / record_get -> record_delete`
|
|
109
|
+
`record_code_block_schema_get -> record_code_block_run`
|
|
111
110
|
`portal_list -> portal_get -> chart_get / view_get`
|
|
112
111
|
`portal_get -> view_get -> record_list`
|
|
113
112
|
|
|
@@ -118,14 +117,14 @@ Analysis answers must include concrete numbers. When applicable, include percent
|
|
|
118
117
|
- Legacy forms such as bare integer `field_id`, `fieldId`, `operator`, `values`, or `order` may still parse, but they are compatibility-only and not the canonical DSL
|
|
119
118
|
|
|
120
119
|
- `record_insert` defaults to an applicant-node `items` array; each item contains a field-title keyed `fields` map. A single insert is one item.
|
|
121
|
-
- `record_update` uses a field-title keyed `fields` map. It first tries the data-manager direct update route, then falls back to the frontend custom-view detail edit route when the selected view can cover the payload; if a unique current-user todo task for the same record exposes editable fields, it can finally use the workflow save-only route.
|
|
120
|
+
- `record_update` uses a field-title keyed `fields` map. It first tries the data-manager direct update route, then falls back to the frontend custom-view detail edit route when the selected view can cover the payload; if a unique current-user todo task for the same record exposes editable fields, it can finally use the workflow save-only route. Read `update_route` and `tried_routes` after execution.
|
|
122
121
|
- For insert, `runtime_linked_required_fields` means required-but-not-directly-writable fields that are usually supplied by runtime linkage or upstream context.
|
|
123
122
|
- For insert, fields marked `may_become_required=true` stay in `optional_fields`; they are still directly writable, but linked visibility or option-driven rules can make them required at runtime.
|
|
124
123
|
- Read field-level `linkage` whenever present on `record_insert_schema_get` or `record_update_schema_get`; it is the static hint for linked visibility, reference-driven auto fill, and formula/default auto-fill behavior.
|
|
125
124
|
- `linkage.sources` lists upstream field titles that influence the current field; `linkage.affects_fields` lists downstream fields that may change when the current field changes.
|
|
126
125
|
- `linkage.kind=logic_visibility` means linked visibility or option-driven rules are involved; `linkage.kind=reference_fill` means reference/default matching logic is involved; `linkage.kind=formula_fill` means formula/default auto-fill logic is involved.
|
|
127
|
-
- `record_update_schema_get` exposes the writable field set and update route candidates for the record
|
|
128
|
-
- `record_delete` deletes by `record_id` or `record_ids
|
|
126
|
+
- `record_update_schema_get` exposes the overall writable field set and update route candidates for the record, but not every field combination is guaranteed; `record_update` still needs data-manager permission, one single matched custom view that can cover the payload, or one unique editable current-user todo task.
|
|
127
|
+
- `record_delete` deletes by `record_id` or `record_ids`.
|
|
129
128
|
- `record_get` is the single-record frontend detail context tool. It returns detail-page visible fields, one-level relation targets, first-page data/workflow logs, associated views/reports, local readable image assets, local downloadable file assets, unavailable context, and `semantic_context`.
|
|
130
129
|
- Use `record_logs_get` only when the user needs the full visible data/workflow log history for a specific record. It writes JSONL files locally and returns file paths plus completeness metadata; do not expect full log arrays in the response.
|
|
131
130
|
- Read record images from `record_get.media_assets.items[].local_path` when `readable_by_agent=true`; read attachments/documents/tables from `record_get.file_assets.items[].local_path` and `extraction.text_path` when present. `record_get` follows the frontend storage cookie redirect path for Qingflow attachments, and remote file URLs should not be treated as directly readable.
|
|
@@ -143,8 +142,7 @@ Analysis answers must include concrete numbers. When applicable, include percent
|
|
|
143
142
|
|
|
144
143
|
Use `record_code_block_run` when the user wants to execute a form code-block field against an existing record.
|
|
145
144
|
|
|
146
|
-
-
|
|
147
|
-
- `record_code_block_run` uses the record/task detail answers for the execution context; when applicant schema is unavailable it can still execute with a numeric code-block field id, but schema-bound relation writeback may be skipped.
|
|
145
|
+
- Always resolve the exact code-block field from `record_code_block_schema_get` first.
|
|
148
146
|
- Treat code-block execution as write-capable, not read-only.
|
|
149
147
|
- If the code block is bound to relation outputs, Qingflow may calculate target answers and write them back automatically.
|
|
150
148
|
- For safe debugging, pass `apply_writeback=false` and inspect the parsed alias results plus `relation.calculated_answers_preview` before allowing any writeback.
|
|
@@ -189,12 +187,12 @@ Use export only when the user explicitly asks to export/download/generate an Exc
|
|
|
189
187
|
`task_list -> task_get -> task_action_execute`
|
|
190
188
|
|
|
191
189
|
- `task_list` returns task-card summaries keyed by `task_id`.
|
|
192
|
-
-
|
|
193
|
-
- `task_id` is
|
|
194
|
-
- `task_action_execute` only uses `task_id`; do not reconstruct or pass `app_key + record_id + workflow_node_id` for actions.
|
|
190
|
+
- Prefer `task_get(task_id=...)` for detail reads; MCP resolves the current todo locator internally.
|
|
191
|
+
- `task_action_execute(task_id=..., action=...)` is also supported; MCP resolves the current todo locator internally before calling the real action route.
|
|
195
192
|
- `task_workflow_log_get(task_id=...)` and `task_associated_report_detail_get(task_id=...)` are also supported for the current todo context.
|
|
196
193
|
- Use `task_associated_report_detail_get` for associated view or report details.
|
|
197
|
-
- Use `task_workflow_log_get` for the current task context workflow log page. For full record-level data/workflow logs,
|
|
194
|
+
- Use `task_workflow_log_get` for the current task context workflow log page. For full record-level data/workflow logs, use `record_logs_get(app_key, record_id, view_id?)`.
|
|
195
|
+
- Task actions operate on `app_key + record_id + workflow_node_id`, not `task_id`.
|
|
198
196
|
- Treat `task_action_execute` as the tool-level action enum surface; the current task's real actions are only the ones listed in `task_get.capabilities.available_actions`.
|
|
199
197
|
- Use `task_action_execute(action="save_only", fields=...)` when the user wants to save editable field changes on the current node without advancing the workflow.
|
|
200
198
|
- `save_only` is exposed only when the backend current-node `editableQueIds` signal returns a non-empty result; MCP no longer infers `save_only` from local schema reconstruction.
|
|
@@ -265,11 +263,10 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
|
|
|
265
263
|
def record_export_start(
|
|
266
264
|
profile: str = DEFAULT_PROFILE,
|
|
267
265
|
app_key: str = "",
|
|
268
|
-
view_id: str = "",
|
|
266
|
+
view_id: str = "system:all",
|
|
269
267
|
columns: list[dict | int] | None = None,
|
|
270
268
|
where: list[dict] | None = None,
|
|
271
269
|
order_by: list[dict] | None = None,
|
|
272
|
-
record_id: str | int | None = None,
|
|
273
270
|
record_ids: list[str | int] | None = None,
|
|
274
271
|
include_workflow_log: bool = False,
|
|
275
272
|
) -> dict:
|
|
@@ -280,7 +277,6 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
|
|
|
280
277
|
columns=columns or [],
|
|
281
278
|
where=where or [],
|
|
282
279
|
order_by=order_by or [],
|
|
283
|
-
record_id=record_id,
|
|
284
280
|
record_ids=record_ids or [],
|
|
285
281
|
include_workflow_log=include_workflow_log,
|
|
286
282
|
)
|
|
@@ -308,11 +304,10 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
|
|
|
308
304
|
def record_export_direct(
|
|
309
305
|
profile: str = DEFAULT_PROFILE,
|
|
310
306
|
app_key: str = "",
|
|
311
|
-
view_id: str = "",
|
|
307
|
+
view_id: str = "system:all",
|
|
312
308
|
columns: list[dict | int] | None = None,
|
|
313
309
|
where: list[dict] | None = None,
|
|
314
310
|
order_by: list[dict] | None = None,
|
|
315
|
-
record_id: str | int | None = None,
|
|
316
311
|
record_ids: list[str | int] | None = None,
|
|
317
312
|
include_workflow_log: bool = False,
|
|
318
313
|
download_to_path: str | None = None,
|
|
@@ -325,7 +320,6 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
|
|
|
325
320
|
columns=columns or [],
|
|
326
321
|
where=where or [],
|
|
327
322
|
order_by=order_by or [],
|
|
328
|
-
record_id=record_id,
|
|
329
323
|
record_ids=record_ids or [],
|
|
330
324
|
include_workflow_log=include_workflow_log,
|
|
331
325
|
download_to_path=download_to_path,
|
|
@@ -480,7 +474,7 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
|
|
|
480
474
|
|
|
481
475
|
code_block_tools.register(server)
|
|
482
476
|
task_context_tools.register(server)
|
|
483
|
-
directory_tools.
|
|
477
|
+
directory_tools.register(server)
|
|
484
478
|
|
|
485
479
|
return server
|
|
486
480
|
|
|
@@ -176,21 +176,17 @@ class SessionStore:
|
|
|
176
176
|
return memory_session
|
|
177
177
|
if not session_profile:
|
|
178
178
|
return None
|
|
179
|
-
token = session_profile.
|
|
180
|
-
if not token
|
|
181
|
-
token =
|
|
179
|
+
token = self._get_secret(self._token_key(profile)) if session_profile.persisted else None
|
|
180
|
+
if not token:
|
|
181
|
+
token = session_profile.token
|
|
182
182
|
if not token:
|
|
183
183
|
return None
|
|
184
|
-
login_token = session_profile.
|
|
185
|
-
|
|
186
|
-
login_token = self._get_secret(self._login_token_key(profile))
|
|
187
|
-
credential = session_profile.credential
|
|
188
|
-
if not credential and session_profile.persisted:
|
|
189
|
-
credential = self._get_secret(self._credential_key(profile))
|
|
184
|
+
login_token = self._get_secret(self._login_token_key(profile)) if session_profile.persisted else None
|
|
185
|
+
credential = self._get_secret(self._credential_key(profile)) if session_profile.persisted else None
|
|
190
186
|
backend_session = BackendSession(
|
|
191
187
|
token=token,
|
|
192
|
-
login_token=login_token,
|
|
193
|
-
credential=credential,
|
|
188
|
+
login_token=login_token or session_profile.login_token,
|
|
189
|
+
credential=credential or session_profile.credential,
|
|
194
190
|
profile=profile,
|
|
195
191
|
base_url=session_profile.base_url,
|
|
196
192
|
qf_version=session_profile.qf_version,
|
|
@@ -10,8 +10,6 @@ from .navigation_compiler import compile_navigation
|
|
|
10
10
|
from .package_compiler import compile_package
|
|
11
11
|
from .portal_compiler import compile_portal
|
|
12
12
|
from .view_compiler import compile_views
|
|
13
|
-
from .workflow_compiler import compile_workflow
|
|
14
|
-
|
|
15
13
|
|
|
16
14
|
@dataclass(slots=True)
|
|
17
15
|
class ExecutionStep:
|
|
@@ -101,7 +99,7 @@ def compile_solution(spec: SolutionSpec) -> CompiledSolution:
|
|
|
101
99
|
|
|
102
100
|
def compile_entity(entity: EntitySpec, *, include_package: bool) -> CompiledEntity:
|
|
103
101
|
app_create_payload, form_base_payload, form_relation_payload, field_specs, field_labels = compile_entity_form(entity, include_package=include_package)
|
|
104
|
-
workflow_plan =
|
|
102
|
+
workflow_plan = None
|
|
105
103
|
view_plans = compile_views(entity)
|
|
106
104
|
chart_plans = compile_charts(entity)
|
|
107
105
|
return CompiledEntity(
|