@qingflow-tech/qingflow-app-builder-mcp 1.0.6 → 1.0.8

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 CHANGED
@@ -3,13 +3,13 @@
3
3
  Install:
4
4
 
5
5
  ```bash
6
- npm install @qingflow-tech/qingflow-app-builder-mcp@1.0.6
6
+ npm install @qingflow-tech/qingflow-app-builder-mcp@1.0.8
7
7
  ```
8
8
 
9
9
  Run:
10
10
 
11
11
  ```bash
12
- npx -y -p @qingflow-tech/qingflow-app-builder-mcp@1.0.6 qingflow-app-builder-mcp
12
+ npx -y -p @qingflow-tech/qingflow-app-builder-mcp@1.0.8 qingflow-app-builder-mcp
13
13
  ```
14
14
 
15
15
  Environment:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qingflow-tech/qingflow-app-builder-mcp",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Builder MCP for Qingflow app/package/system design and staged solution workflows.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qingflow-mcp"
7
- version = "1.0.6"
7
+ version = "1.0.8"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -38,7 +38,7 @@ Default modeling rules:
38
38
  - App reads: `app_resolve`, `app_get`, `app_get_fields`, `app_get_layout`, `app_get_views`, `app_get_flow`, `app_get_charts`
39
39
  - Builder reads: `portal_list`, `portal_get`, `view_get`, `chart_get`, `builder_tool_contract`
40
40
  - Directory: `member_search`, `role_search`, `role_create`
41
- - Writes: `app_schema_apply`, `app_layout_apply`, `app_flow_apply`, `app_views_apply`, `app_charts_apply`, `portal_apply`, `app_release_edit_lock_if_mine`
41
+ - Writes: `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`, `app_release_edit_lock_if_mine`
42
42
  - Verification: `app_publish_verify`
43
43
  - Cross-cutting escalation: `feedback_submit` after explicit user confirmation when the public builder surface still cannot satisfy the user's need
44
44
 
@@ -52,17 +52,48 @@ Treat these as the official surface. Do not default to `package_create`, `packag
52
52
  - App base permissions:
53
53
  - trust `app_get.editability.can_edit_app_base` for app base-info writes like app name, icon, and visibility
54
54
  - `can_edit_form` only means schema/form-route capability; it no longer implies app base-info write capability
55
+ - Partial update rule:
56
+ - for existing views, custom buttons, associated resources, and charts, prefer `patch_views` / `patch_buttons` / `patch_resources` / `patch_charts`
57
+ - each patch item uses the object's real selector field (`view_key` / `button_id` / `associated_item_id` / `chart_id`, or unique name where supported) plus `set` and optional `unset`; do not send a literal `selector` key for these tools
58
+ - use `upsert_*` for creation or full target configuration; do not send a partial `upsert_*` and expect the backend to merge missing required fields
55
59
  - Portal work:
56
60
  - `portal_apply` is the public write path
57
61
  - in edit mode, omitting `sections` means “preserve existing layout and update base info only”
58
62
  - supplying `sections` means full replace semantics for sections
59
63
  - Chart work:
60
64
  - `app_charts_apply` is the public write path
65
+ - use `patch_charts` for changing a chart name, visibility, filters, or one config fragment on an existing chart
61
66
  - `visibility` is a public capability and should be treated as a base-only permission update
62
67
  - do not model chart visibility changes as raw config rewrites
68
+ - Button work:
69
+ - `app_custom_buttons_apply` is the public write path for custom button bodies and view placement
70
+ - use `patch_buttons` for changing a single existing button parameter such as URL, text, icon, style, or add-data mapping
71
+ - for add-data buttons, use `trigger_add_data_config.target_app_key + field_mappings/default_values`; do not handwrite raw `que_relation`
72
+ - use `field_mappings` for dynamic current-record values, including system fields such as `数据ID` (`field_id=-17`) and `编号` (`field_id=0`)
73
+ - to prefill a target relation field with the current source record, map `{"source_field": "数据ID", "target_field": "目标引用字段"}`; use `default_values` only for static constants
74
+ - if field type compatibility is unclear, read [references/match-rules.md](references/match-rules.md)
75
+ - bind buttons to views through `view_configs[].buttons[]`; default to `placement`: `header` or `detail`
76
+ - builder `view_configs[].view_key` uses the raw key from `app_get.views[].view_key`, without a `custom:` prefix
77
+ - `buttons` is required in merge mode; do not send a view config with only `view_key`
78
+ - `view_configs[].mode` defaults to `merge`; use `mode: "replace"` or an explicit empty `buttons: []` to clear a view's custom button bindings
79
+ - `placement=list` configures row/list buttons and maps to the backend `INSIDE` button position
80
+ - advanced bindings may use `button_limit`, `button_formula`, `button_formula_type`, and `print_tpls` only when visibility or print-template behavior is required
81
+ - Associated resources:
82
+ - `app_associated_resources_apply` is the public write path for the app-level associated report/view pool and per-view display config
83
+ - use `patch_resources` for changing match rules or other existing associated-resource parameters; the tool preserves backend-required raw fields internally
84
+ - `associated_item_id` must come from `app_get.associated_resources[].associated_item_id`; it is not `chart_id`, `chart_key`, or `view_key`
85
+ - before creating a resource, check `app_get.associated_resources` for the same `target_app_key + view_key/chart_key`; if it already exists, use `patch_resources` with that `associated_item_id`
86
+ - `client_key` is only a same-call reference for `view_configs[].associated_item_refs`; it is not saved and cannot deduplicate later calls
87
+ - do not pass backend raw `sourceType`; view resources infer the internal Qingflow view source, report/chart resources default to BI app reports, and dataset reports use `report_source="dataset"`
88
+ - use `match_mappings` for associated view/report filters; dynamic conditions use `source_field`, static conditions use `value`
89
+ - if field type compatibility is unclear, read [references/match-rules.md](references/match-rules.md)
63
90
  - Views and flows:
64
91
  - stay on `app_views_apply` / `app_flow_apply`
92
+ - use `patch_views` for existing-view parameter changes such as `query_conditions`, `filters`, `columns`, or visibility
93
+ - builder view writes use raw `view_key` values from `app_get.views`; `custom:<viewKey>` is a record-data `view_id` form, not a builder `view_key`
65
94
  - use `builder_tool_contract` whenever the minimal legal shape is unclear
95
+ - keep view `filters` and `query_conditions` separate: `filters` are fixed saved filters; `query_conditions` configure the frontend query panel fields and only take effect after users enter query values
96
+ - configure associated reports/views through `app_associated_resources_apply`, not through `app_views_apply`
66
97
 
67
98
  ## Standard Operating Order
68
99
 
@@ -74,20 +105,22 @@ Treat these as the official surface. Do not default to `package_create`, `packag
74
105
  - package-scoped work with known id -> `package_get`
75
106
  - portal inventory -> `portal_list`
76
107
  5. Read only the smallest config slice needed:
77
- - app summary -> `app_get`
108
+ - app map -> `app_get` (default entry; includes compact views, charts, custom buttons, and associated resource pool)
78
109
  - fields -> `app_get_fields`
79
110
  - layout -> `app_get_layout`
80
- - views -> `app_get_views`
111
+ - views -> `app_get_views` only when the app_get compact list is not enough
81
112
  - flow -> `app_get_flow`
82
- - charts -> `app_get_charts`
113
+ - charts -> `app_get_charts` only when the app_get compact list is not enough
83
114
  - portal -> `portal_get`
84
115
  6. If the public shape is unclear, call `builder_tool_contract`
85
116
  7. Apply the smallest patch tool that fits:
86
117
  - fields -> `app_schema_apply`
87
118
  - layout -> `app_layout_apply`
88
119
  - flow -> `app_flow_apply`
89
- - views -> `app_views_apply`
90
- - charts -> `app_charts_apply`
120
+ - existing views -> `app_views_apply.patch_views`; new/full views -> `app_views_apply.upsert_views`
121
+ - existing buttons -> `app_custom_buttons_apply.patch_buttons`; new/full buttons -> `app_custom_buttons_apply.upsert_buttons`
122
+ - existing associated resources -> `app_associated_resources_apply.patch_resources`; new/full resources -> `app_associated_resources_apply.upsert_resources`
123
+ - existing charts -> `app_charts_apply.patch_charts`; new/full charts -> `app_charts_apply.upsert_charts`
91
124
  - portal -> `portal_apply`
92
125
  - package metadata/layout -> `package_apply`
93
126
  8. Use `app_publish_verify` only when the user explicitly wants final publish/live verification or you need a dedicated verification pass
@@ -98,7 +131,8 @@ Treat these as the official surface. Do not default to `package_create`, `packag
98
131
  - If `package_id` is unknown, derive it from related app/portal readback when possible; otherwise ask the user instead of falling back to hidden package lookup tools.
99
132
  - Do not use `package_create` or `package_attach_app` as a public default path. If they still appear in low-level code, treat them as internal/legacy implementation details.
100
133
  - Do not use raw `portal_*` writes or raw `qingbi_report_*` writes as the default builder strategy.
101
- - `app_schema_apply`, `app_layout_apply`, `app_flow_apply`, and `app_views_apply` publish by default; `app_charts_apply` is immediate-live without publish.
134
+ - `app_schema_apply`, `app_layout_apply`, `app_flow_apply`, and `app_views_apply` publish by default; `app_custom_buttons_apply` and `app_associated_resources_apply` publish after at least one write succeeds and do not accept a draft-only `publish` parameter; `app_charts_apply` is immediate-live without publish.
135
+ - When creating or updating fields, configure the app data title/cover directly in field JSON: `as_data_title: true` is required on exactly one readable top-level title field; `as_data_cover: true` is optional and only valid on one top-level `attachment` field.
102
136
  - If the same validation error repeats twice, stop guessing and re-read `builder_tool_contract`.
103
137
  - For workflow assignees, prefer `role_search` over explicit members unless the user explicitly wants named members.
104
138
  - Public flow building is still intentionally limited to stable linear workflows. If a requirement sounds like branches/conditions, explain the limitation instead of freehanding unsupported graph shapes.
@@ -112,6 +146,9 @@ Treat these as the official surface. Do not default to `package_create`, `packag
112
146
  - For portals, distinguish clearly between:
113
147
  - base-info-only update with layout preserved
114
148
  - full sections replace
149
+ - For object apply tools, distinguish clearly between:
150
+ - `patch_*`: public partial parameter replacement; the tool hydrates current config and full-saves internally
151
+ - `upsert_*`: create or full target configuration; omitted fields may mean default/clear depending on that object
115
152
  - For charts, distinguish clearly between:
116
153
  - base / visibility change
117
154
  - real chart-config change
@@ -122,7 +159,7 @@ Treat these as the official surface. Do not default to `package_create`, `packag
122
159
  - Read one package: `package_get`
123
160
  - Create or update one package: `package_apply`
124
161
  - Resolve one app: `app_resolve`
125
- - Read one app summary: `app_get`
162
+ - Read one app map: `app_get`
126
163
  - Read fields only: `app_get_fields`
127
164
  - Read layout summary: `app_get_layout`
128
165
  - Read views summary: `app_get_views`
@@ -134,17 +171,68 @@ Treat these as the official surface. Do not default to `package_create`, `packag
134
171
  - Add/update/remove fields: `app_schema_apply`
135
172
  - Merge or replace layout: `app_layout_apply`
136
173
  - Replace workflow: `app_flow_apply`
137
- - Upsert/remove views: `app_views_apply`
138
- - Upsert/remove/reorder charts: `app_charts_apply`
174
+ - Upsert/patch/remove views: `app_views_apply`
175
+ - Upsert/patch/remove custom buttons and bind them to header/detail view positions: `app_custom_buttons_apply`
176
+ - Upsert/patch/remove app associated reports/views and per-view display: `app_associated_resources_apply`
177
+ - Upsert/patch/remove/reorder charts: `app_charts_apply`
139
178
  - Create or update portal pages: `portal_apply`
140
179
  - Release your own stale edit lock: `app_release_edit_lock_if_mine`
141
180
  - Final publish verification: `app_publish_verify`
142
181
 
182
+ ## Button Pattern
183
+
184
+ Use `app_custom_buttons_apply` for the whole custom-button path: button body, add-data mapping, default values, and placement.
185
+
186
+ ```json
187
+ {
188
+ "tool_name": "app_custom_buttons_apply",
189
+ "arguments": {
190
+ "app_key": "EMPLOYEE_APP",
191
+ "upsert_buttons": [
192
+ {
193
+ "client_key": "add_worklog",
194
+ "button_text": "快捷添加工时",
195
+ "style_preset": "primary_blue",
196
+ "button_icon": "ex-plus-circle",
197
+ "trigger_action": "addData",
198
+ "trigger_add_data_config": {
199
+ "target_app_key": "WORKLOG_APP",
200
+ "field_mappings": [
201
+ {"source_field": "数据ID", "target_field": "关联员工"},
202
+ {"source_field": "员工名称", "target_field": "员工姓名"},
203
+ {"source_field": "所属部门", "target_field": "部门名称"}
204
+ ],
205
+ "default_values": {
206
+ "工时类型": "日常工作",
207
+ "状态": "待提交"
208
+ }
209
+ }
210
+ }
211
+ ],
212
+ "remove_buttons": [],
213
+ "view_configs": [
214
+ {
215
+ "view_key": "EMPLOYEE_VIEW",
216
+ "mode": "merge",
217
+ "buttons": [
218
+ {"button_ref": "add_worklog", "placement": "detail", "primary": true}
219
+ ]
220
+ }
221
+ ]
222
+ }
223
+ }
224
+ ```
225
+
226
+ `button_ref` may be a same-call `client_key` or an existing `button_id`. Supported placements are `header`, `detail`, and `list`; `list` maps to the backend `INSIDE` row/list button position.
227
+
228
+ For add-data buttons that create a child record linked back to the current record, map `source_field: "数据ID"` to the target relation field. Do not use raw `que_relation` unless maintaining an existing backend config.
229
+
143
230
  ## Resources
144
231
 
145
232
  - Shared public-surface baseline: [public-surface-sync.md](../qingflow-app-user/references/public-surface-sync.md)
146
233
  - Environment switching: [references/environments.md](references/environments.md)
147
234
  - Tool choice and sequencing: [references/tool-selection.md](references/tool-selection.md)
235
+ - Field matching rules: [references/match-rules.md](references/match-rules.md)
148
236
  - Result semantics and gotchas: [references/gotchas.md](references/gotchas.md)
149
237
  - Create one app in an existing package: [references/create-app.md](references/create-app.md)
150
238
  - Update fields only: [references/update-schema.md](references/update-schema.md)
@@ -79,8 +79,9 @@ Apply schema for a new app:
79
79
  "create_if_missing": true,
80
80
  "publish": true,
81
81
  "add_fields": [
82
- {"name": "订单编号", "type": "text", "required": true},
82
+ {"name": "订单编号", "type": "text", "required": true, "as_data_title": true},
83
83
  {"name": "客户名称", "type": "text", "required": true},
84
+ {"name": "订单封面", "type": "attachment", "as_data_cover": true},
84
85
  {"name": "订单金额", "type": "amount"},
85
86
  {"name": "状态", "type": "single_select", "options": ["草稿", "进行中", "已完成"], "required": true}
86
87
  ],
@@ -90,6 +91,8 @@ Apply schema for a new app:
90
91
  }
91
92
  ```
92
93
 
94
+ Data title is required: mark exactly one top-level field with `as_data_title: true`. Data cover is optional and only valid on a top-level `attachment` field.
95
+
93
96
  ## Common failures
94
97
 
95
98
  ### `APP_NOT_FOUND`
@@ -21,9 +21,33 @@
21
21
  ## Auto publish
22
22
 
23
23
  - `app_schema_apply`, `app_layout_apply`, `app_flow_apply`, and `app_views_apply` publish by default
24
- - Pass `publish=false` only when the user explicitly wants to leave changes in draft
24
+ - For those four tools, pass `publish=false` only when the user explicitly wants to leave changes in draft
25
+ - `app_custom_buttons_apply` and `app_associated_resources_apply` publish after at least one write succeeds and do not accept `publish=false`
25
26
  - `app_publish_verify` is for explicit final verification, not the default next step after every write
26
27
 
28
+ ## Custom buttons
29
+
30
+ - Use `app_custom_buttons_apply` for button creation/update/removal and view placement; do not split placement into `app_views_apply.buttons` unless you are maintaining a legacy patch.
31
+ - When changing one existing button parameter, use `patch_buttons` with `set/unset`. Do not send a partial `upsert_buttons` object and expect the backend to preserve hidden required fields.
32
+ - For add-data buttons, prefer semantic `trigger_add_data_config.target_app_key`, `field_mappings`, and `default_values`.
33
+ - Do not handwrite raw `que_relation` unless you are preserving an existing backend config. If `field_mappings` and `que_relation` are mixed, the tool blocks the write.
34
+ - `field_mappings.source_field` can use source app fields and supported system fields such as `数据ID` (`field_id=-17`) and `编号` (`field_id=0`).
35
+ - To fill a target relation field with the current source record, map `{"source_field": "数据ID", "target_field": "目标引用字段"}`. Use `default_values` only for static constants.
36
+ - For field type compatibility, read `references/match-rules.md`.
37
+ - Default button placements are `header` and `detail`; these map to the frontend's header and detail button areas.
38
+ - `view_configs[].buttons` is required in merge mode. Do not send a view config with only `view_key`; it is blocked to avoid no-op writes and accidental publish.
39
+ - Builder view configs use raw `view_key` from `app_get.views[].view_key`; do not prefix it with `custom:`. The `custom:<viewKey>` form is for record-data `view_id`.
40
+ - View button bindings merge by default. Use `view_configs[].mode="replace"` to replace the full set, or pass an explicit empty `buttons: []` when you intend to clear all custom button bindings for that view.
41
+ - Advanced view button bindings can include `button_limit`, `button_formula`, `button_formula_type`, and `print_tpls`, but keep ordinary buttons simple unless the user asks for conditional visibility or print templates.
42
+ - `placement=list` configures row/list buttons. The tool maps it to the backend `INSIDE` button position, not a raw `LIST` config type.
43
+
44
+ ## Associated resources
45
+
46
+ - Before creating an associated view/report, check `app_get.associated_resources` for the same `target_app_key + view_key/chart_key`.
47
+ - If the resource already exists, use `patch_resources` with its `associated_item_id`; do not call `upsert_resources` again.
48
+ - `client_key` is only a same-call alias for `view_configs[].associated_item_refs`. It is not stored by Qingflow and cannot prevent duplicates in later calls.
49
+ - Repeated `upsert_resources` without `associated_item_id` can create multiple app-level associated items pointing to the same view/report.
50
+
27
51
  ## Readback scope
28
52
 
29
53
  - Prefer summary reads over large raw payloads
@@ -32,6 +56,15 @@
32
56
  - Use `app_get_flow` before workflow work
33
57
  - Use `app_get_views` before view work
34
58
 
59
+ ## Partial update discipline
60
+
61
+ - Existing views, custom buttons, associated resources, and charts support the same public partial pattern: `patch_*[].set` plus optional `patch_*[].unset`.
62
+ - The backend may still save a full payload. The MCP/CLI patch path is responsible for reading current config and preserving fields the user did not mention.
63
+ - Do not use `upsert_views`, `upsert_buttons`, `upsert_resources`, or `upsert_charts` for a tiny parameter replacement unless you are deliberately providing the full desired target config.
64
+ - `app_layout_apply(mode=merge)` is already a safe layout merge path; `mode=replace` is full layout replacement.
65
+ - `portal_apply` without `sections` is base-info-only. Supplying `sections` replaces the portal sections list.
66
+ - `app_flow_apply` is intentionally replace-only for the public linear workflow graph.
67
+
35
68
  ## Workflow dependencies
36
69
 
37
70
  - Approval-style flows usually require an explicit status field
@@ -54,6 +87,7 @@
54
87
  - `assignees.role_ids` / `assignees.member_uids` / `assignees.member_emails`
55
88
  - `permissions.editable_fields`
56
89
  - For view work, treat `columns` as the only canonical public key. `app_get_views` and `app_views_apply` should all be read and written in that shape.
90
+ - For existing view parameter changes, prefer `patch_views`. Example: to change only query-panel fields, set only `query_conditions`; the tool will preserve columns, filters, date config, card fields, buttons, and other backend-required fields.
57
91
  - For layout work, treat `title + rows` as the only canonical public section shape. `fields`, `field_ids`, and `columns` may appear in legacy/internal shapes, but they are not the preferred public write shape.
58
92
  - A created view is not enough to claim a filter succeeded. When `app_views_apply` returns `partial_success`, `views_verified=false`, or `details.filter_mismatches`, treat the view as present but the filter as unverified.
59
93
  - If duplicate view names exist, do not retry by name. Read the exact `view_key` and target that one.
@@ -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.
@@ -40,15 +40,19 @@ If the user asks for multiple forms/modules that relate to each other, this is a
40
40
 
41
41
  ## Apply tools
42
42
 
43
- These execute normalized patches and publish by default unless `publish=false`.
43
+ 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
44
 
45
45
  - `app_schema_apply`: create app shell or change fields
46
46
  - `app_layout_apply`: merge or replace layout
47
47
  - `app_flow_apply`: replace workflow
48
- - `app_views_apply`: upsert or remove views
49
- - `app_charts_apply`: upsert/remove/reorder QingBI charts; charts are immediate-live and do not publish; use `chart_id` when names are not unique
48
+ - `app_views_apply`: use `patch_views` for existing-view parameter replacement; use `upsert_views` for creation or full target config; remove views by key
49
+ - `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.
50
+ - `app_associated_resources_apply`: use `patch_resources` for existing associated-resource parameter replacement; use `upsert_resources` for creation or full target config; use `match_mappings` for associated view/report filters; manage per-view display config; publishes after successful writes; omit raw `sourceType`, and use `report_source="dataset"` only for BI dataset reports. 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.
51
+ - `app_charts_apply`: use `patch_charts` for existing-chart parameter replacement; use `upsert_charts` for creation or full target config; remove/reorder QingBI charts; charts are immediate-live and do not publish; use `chart_id` when names are not unique
50
52
  - `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
51
53
 
54
+ 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": {...}}]`. 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.
55
+
52
56
  ## Explicit post-apply tools
53
57
 
54
58
  - `app_publish_verify`: explicit final publish verification when the user asks for live confirmation
@@ -68,9 +72,9 @@ These execute normalized patches and publish by default unless `publish=false`.
68
72
  - Add workflow:
69
73
  `builder_tool_contract -> app_get_fields -> app_get_flow -> role_search/member_search -> app_flow_apply -> app_get_flow`
70
74
  - Add views:
71
- `builder_tool_contract -> app_get_fields -> app_get_views -> app_views_apply -> app_get_views`
75
+ `builder_tool_contract -> app_get_fields -> app_get_views -> app_views_apply.patch_views/upsert_views -> app_get_views`
72
76
  - Add QingBI charts:
73
- `builder_tool_contract -> app_get_fields -> app_get_charts -> app_charts_apply -> app_get_charts`
77
+ `builder_tool_contract -> app_get_fields -> app_get_charts -> app_charts_apply.patch_charts/upsert_charts -> app_get_charts`
74
78
  - Create or update a portal:
75
79
  `builder_tool_contract -> portal_get -> portal_apply -> portal_get`
76
80
 
@@ -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. `app_get_views`
9
+ 3. `app_get` for current view/chart/button inventory and app-level associated resource ids
10
10
  4. `app_views_apply`
11
- 5. `app_get_views` again whenever apply returns `failed` or `partial_success`
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
- - If apply returns `partial_success`, inspect `verification.by_view` and `details.filter_mismatches` before claiming the filters are active
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,12 @@ 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
+ - Use `app_associated_resources_apply` for the frontend associated report/view area. `associated_item_id` must be copied from `app_get.associated_resources[].associated_item_id`; it is not `chart_id` or `chart_key`. 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
34
  - 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
35
+ - If `app_get.views` or `app_get_views` shows duplicate view names, include `view_key` in `upsert_views[]` and update that exact target
36
+ - 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`.
37
+ - 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
38
 
32
39
  Apply a default table view:
33
40
 
@@ -52,6 +59,100 @@ Apply a default table view:
52
59
  ```
53
60
  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
61
 
62
+ Existing table view: replace only frontend query conditions:
63
+
64
+ ```json
65
+ {
66
+ "tool_name": "app_views_apply",
67
+ "arguments": {
68
+ "profile": "default",
69
+ "app_key": "APP_123",
70
+ "publish": true,
71
+ "patch_views": [
72
+ {
73
+ "view_key": "VIEW_KEY",
74
+ "set": {
75
+ "query_conditions": {
76
+ "enabled": true,
77
+ "exact": false,
78
+ "hide_before_query": false,
79
+ "rows": [["客户名称", "负责人"], ["创建时间"]]
80
+ }
81
+ }
82
+ }
83
+ ],
84
+ "remove_views": []
85
+ }
86
+ }
87
+ ```
88
+
89
+ Meaning:
90
+
91
+ - `filters` are saved view filters and apply immediately when the view opens.
92
+ - `query_conditions` only configures which fields appear in the frontend query panel. The query values come later from the user through the page.
93
+ - `query_conditions.rows` is a matrix of field names and is compiled to backend `queryCondition` field ids.
94
+ - The patch call preserves the view's existing columns, saved filters, type-specific date/card/board config, buttons, visibility, and other backend-required fields.
95
+
96
+ View associated resources example:
97
+
98
+ ```json
99
+ {
100
+ "tool_name": "app_associated_resources_apply",
101
+ "arguments": {
102
+ "profile": "default",
103
+ "app_key": "APP_123",
104
+ "upsert_resources": [],
105
+ "remove_associated_item_ids": [],
106
+ "reorder_associated_item_ids": [],
107
+ "view_configs": [
108
+ {
109
+ "view_key": "VIEW_KEY",
110
+ "visible": true,
111
+ "limit_type": "select",
112
+ "associated_item_ids": [123, 124]
113
+ }
114
+ ]
115
+ }
116
+ }
117
+ ```
118
+
119
+ Use `{"visible": true, "limit_type": "all"}` to show all app-level associated resources, and `{"visible": false}` to hide the area. The ids above must come 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`. 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.
120
+
121
+ Create and show a BI indicator-card report in the same call:
122
+
123
+ ```json
124
+ {
125
+ "tool_name": "app_associated_resources_apply",
126
+ "arguments": {
127
+ "profile": "default",
128
+ "app_key": "APP_123",
129
+ "upsert_resources": [
130
+ {
131
+ "client_key": "total_hours_card",
132
+ "graph_type": "chart",
133
+ "target_app_key": "TIMESHEET_APP",
134
+ "chart_key": "CHART_KEY",
135
+ "report_source": "app",
136
+ "match_mappings": [
137
+ {"target_field": "关联员工", "source_field": "数据ID"},
138
+ {"target_field": "状态", "value": "待提交"}
139
+ ]
140
+ }
141
+ ],
142
+ "remove_associated_item_ids": [],
143
+ "reorder_associated_item_ids": [],
144
+ "view_configs": [
145
+ {
146
+ "view_key": "VIEW_KEY",
147
+ "visible": true,
148
+ "limit_type": "select",
149
+ "associated_item_refs": ["total_hours_card"]
150
+ }
151
+ ]
152
+ }
153
+ }
154
+ ```
155
+
55
156
  Board example:
56
157
 
57
158
  ```json
@@ -151,6 +252,25 @@ Treat this as:
151
252
 
152
253
  Do not tell the user the filter is active until the readback verification matches the intended filter.
153
254
 
255
+ ### `VIEW_QUERY_CONDITION_READBACK_MISMATCH`
256
+
257
+ The view object was created or updated, but the readback config did not keep the intended frontend query-condition settings.
258
+
259
+ Treat this as:
260
+
261
+ - the view exists
262
+ - the query panel configuration is **not yet verified**
263
+
264
+ Do not tell the user the query condition is active until readback matches the intended `query_conditions`.
265
+
266
+ ### `QUERY_CONDITION_FIELD_NOT_FOUND`
267
+
268
+ At least one `query_conditions.rows` field does not exist on the app.
269
+
270
+ ### `INVALID_QUERY_CONDITION_FIELD`
271
+
272
+ 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.
273
+
154
274
  ## Notes
155
275
 
156
276
  - `fields` is accepted as an alias for `columns`, but skill examples should still use `columns`
@@ -158,5 +278,6 @@ Do not tell the user the filter is active until the readback verification matche
158
278
  - `app_get_views` should be treated as canonical readback and now returns `columns`
159
279
  - If `app_views_apply` returns `AMBIGUOUS_VIEW`, stop and re-run `app_get_views`; then retry with the exact `view_key`
160
280
  - `filters` are ANDed together as one flat condition group
281
+ - `query_conditions.rows` does not mean OR; it controls frontend query-field layout
161
282
  - `app_views_apply` publishes by default
162
283
  - For select-style filters, success means the backend preserved the option value in readback, not just that the view name now exists