@josephyan/qingflow-app-builder-mcp 0.2.0-beta.71 → 0.2.0-beta.73

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.
Files changed (28) hide show
  1. package/README.md +2 -2
  2. package/package.json +1 -1
  3. package/pyproject.toml +1 -1
  4. package/skills/qingflow-app-builder/SKILL.md +21 -22
  5. package/skills/qingflow-app-builder/references/create-app.md +1 -1
  6. package/skills/qingflow-app-builder/references/flow-actors-and-permissions.md +3 -3
  7. package/skills/qingflow-app-builder/references/gotchas.md +5 -5
  8. package/skills/qingflow-app-builder/references/solution-playbooks.md +11 -11
  9. package/skills/qingflow-app-builder/references/tool-selection.md +13 -13
  10. package/skills/qingflow-app-builder/references/update-flow.md +5 -5
  11. package/skills/qingflow-app-builder/references/update-layout.md +4 -4
  12. package/skills/qingflow-app-builder/references/update-schema.md +4 -4
  13. package/skills/qingflow-app-builder/references/update-views.md +7 -7
  14. package/src/qingflow_mcp/backend_client.py +1 -0
  15. package/src/qingflow_mcp/builder_facade/models.py +22 -11
  16. package/src/qingflow_mcp/builder_facade/service.py +183 -121
  17. package/src/qingflow_mcp/cli/commands/__init__.py +4 -1
  18. package/src/qingflow_mcp/cli/commands/builder.py +81 -64
  19. package/src/qingflow_mcp/cli/commands/chart.py +18 -0
  20. package/src/qingflow_mcp/cli/commands/portal.py +25 -0
  21. package/src/qingflow_mcp/cli/commands/view.py +18 -0
  22. package/src/qingflow_mcp/cli/context.py +9 -0
  23. package/src/qingflow_mcp/response_trim.py +219 -178
  24. package/src/qingflow_mcp/server_app_builder.py +18 -42
  25. package/src/qingflow_mcp/server_app_user.py +21 -1
  26. package/src/qingflow_mcp/tools/ai_builder_tools.py +244 -124
  27. package/src/qingflow_mcp/tools/app_tools.py +0 -4
  28. package/src/qingflow_mcp/tools/resource_read_tools.py +399 -0
package/README.md CHANGED
@@ -3,13 +3,13 @@
3
3
  Install:
4
4
 
5
5
  ```bash
6
- npm install @josephyan/qingflow-app-builder-mcp@0.2.0-beta.71
6
+ npm install @josephyan/qingflow-app-builder-mcp@0.2.0-beta.73
7
7
  ```
8
8
 
9
9
  Run:
10
10
 
11
11
  ```bash
12
- npx -y -p @josephyan/qingflow-app-builder-mcp@0.2.0-beta.71 qingflow-app-builder-mcp
12
+ npx -y -p @josephyan/qingflow-app-builder-mcp@0.2.0-beta.73 qingflow-app-builder-mcp
13
13
  ```
14
14
 
15
15
  Environment:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@josephyan/qingflow-app-builder-mcp",
3
- "version": "0.2.0-beta.71",
3
+ "version": "0.2.0-beta.73",
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 = "0.2.0b71"
7
+ version = "0.2.0b73"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -48,7 +48,7 @@ Default modeling rules:
48
48
 
49
49
  - Authentication and workspace: `auth_*`, `workspace_*`
50
50
  - File upload: `file_upload_local`
51
- - Resource resolve/read: `package_list`, `package_resolve`, `package_create`, `builder_tool_contract`, `member_search`, `role_search`, `app_resolve`, `app_read_summary`, `app_read_fields`, `app_read_layout_summary`, `app_read_views_summary`, `app_read_flow_summary`, `app_read_charts_summary`, `portal_list`, `portal_get`, `portal_read_summary`, `view_get`, `chart_get`
51
+ - Resource resolve/read: `package_list`, `package_resolve`, `package_create`, `builder_tool_contract`, `member_search`, `role_search`, `app_resolve`, `app_get`, `app_get_fields`, `app_get_layout`, `app_get_views`, `app_get_flow`, `app_get_charts`, `portal_list`, `portal_get`, `view_get`, `chart_get`
52
52
  - Resource patch: `app_schema_apply`, `app_layout_apply`, `app_flow_apply`, `app_views_apply`, `app_charts_apply`, `portal_apply`, `package_attach_app`, `app_release_edit_lock_if_mine`, `role_create`
53
53
  - Publish and verify: `app_publish_verify`
54
54
 
@@ -59,11 +59,10 @@ Note:
59
59
  - `app_schema_apply` / `app_layout_apply` / `app_flow_apply` / `app_views_apply` publish by default; pass `publish=false` only when you intentionally want to leave changes in draft.
60
60
  - `app_charts_apply` is immediate-live and does not publish; it resolves targets by `chart_id` first and then exact unique chart name.
61
61
  - `portal_apply` uses replace semantics for sections; remove a section by omitting it from the next full sections list. `publish=false` only guarantees draft/base-info updates, and `chart_ref/view_ref` resolve by `id/key` first and exact unique name second.
62
- - `app_read_charts_summary` is the compact discovery path for current chart inventory; use it before `app_charts_apply` when you need exact `chart_id` values.
63
- - `chart_get` returns one chart's base info, config, and concrete chart data; prefer it over low-level `qingbi_report_*` reads in public builder flows.
62
+ - `app_get_charts` is the compact discovery path for current chart inventory; use it before `app_charts_apply` when you need exact `chart_id` values.
63
+ - `chart_get` returns one chart's base info and config only; public builder flows should treat data reads as user-side access, not builder config access.
64
64
  - `portal_list` is the discovery path for accessible portals.
65
- - `portal_read_summary` is the compact discovery path for portal draft/live summaries; it returns section summaries, not the raw dash payload.
66
- - `portal_get` returns portal-level detail plus a component inventory; it does not inline chart/view detail.
65
+ - `portal_get` returns portal-level config detail plus a component inventory; it does not inline chart/view detail or user-side chart/view data.
67
66
  - `view_get` returns one view's definition detail only; use `record_list` separately when you need rows from that view.
68
67
  - `app_schema_apply` / `app_layout_apply` / `app_flow_apply` / `app_views_apply` now perform planning, normalization, and dependency checks internally; when prechecks block, read the returned blocking issues and `suggested_next_call` directly from the apply result.
69
68
  - If you are unsure about a public builder tool's keys, aliases, presets, or minimal legal shape, call `builder_tool_contract` instead of guessing.
@@ -108,7 +107,7 @@ For builder work:
108
107
  - one app: continue with `app_resolve`
109
108
  - multi-app package/system: create or resolve the package, then create each app separately before adding relations
110
109
  3. Resolve the target app with `app_resolve` if the request is an update. Use exactly one selector mode: `app_key`, or `app_name + package_tag_id`.
111
- 4. Read only the smallest summary you need: `app_read_summary`, `app_read_fields`, `app_read_layout_summary`, `app_read_views_summary`, `app_read_flow_summary`, `app_read_charts_summary`, `portal_read_summary`.
110
+ 4. Read only the smallest config slice you need: `app_get`, `app_get_fields`, `app_get_layout`, `app_get_views`, `app_get_flow`, `app_get_charts`, `portal_get`.
112
111
  5. Use `app_schema_apply` for create/upsert/remove field work. It publishes by default after the patch lands; noop schema requests do not publish.
113
112
  6. If the app must belong to a package, use `package_attach_app` explicitly after schema work with `tag_id + app_key` unless readback already shows the target `tag_id`.
114
113
  7. Use `app_layout_apply` only when the user is explicitly changing layout. Prefer the default `mode=merge`; use `mode=replace` only when you intend to place every field explicitly. It publishes by default after the patch lands; noop layout requests do not publish.
@@ -120,16 +119,16 @@ For builder work:
120
119
  For view work, keep the order strict:
121
120
 
122
121
  1. `builder_tool_contract`
123
- 2. `app_read_fields`
124
- 3. `app_read_views_summary`
122
+ 2. `app_get_fields`
123
+ 3. `app_get_views`
125
124
  4. `app_views_apply`
126
- 5. `app_read_views_summary` again whenever `app_views_apply` returns `failed` or `partial_success`
125
+ 5. `app_get_views` again whenever `app_views_apply` returns `failed` or `partial_success`
127
126
 
128
127
  For flow work, keep the order strict:
129
128
 
130
129
  1. `builder_tool_contract`
131
- 2. `app_read_fields`
132
- 3. `app_read_flow_summary`
130
+ 2. `app_get_fields`
131
+ 3. `app_get_flow`
133
132
  4. `role_search` or `member_search` if assignees need to come from the directory
134
133
  5. `role_create` if the user wants a reusable role and no suitable role exists yet
135
134
  6. Start from a canonical preset when possible
@@ -143,10 +142,10 @@ For flow work, keep the order strict:
143
142
  - support `assignees.member_names` / `assignees.member_emails` / `assignees.member_uids`
144
143
  10. When a node must edit specific fields, declare `permissions.editable_fields`
145
144
  11. `app_flow_apply`
146
- 12. `app_read_flow_summary` after apply whenever the user asked for verification or apply returns `partial_success`
147
- 13. Use `app_read_charts_summary` before chart work whenever exact current `chart_id` values matter
145
+ 12. `app_get_flow` after apply whenever the user asked for verification or apply returns `partial_success`
146
+ 13. Use `app_get_charts` before chart work whenever exact current `chart_id` values matter
148
147
  14. Use `app_charts_apply` for QingBI chart creation and updates, not raw `qingbi_report_*` writes
149
- 15. Use `portal_read_summary` before portal work whenever exact current draft/live section inventory matters
148
+ 15. Use `portal_get` before portal work whenever exact current portal config and section inventory matter
150
149
  16. Use `portal_apply` for builder-side portal work; treat sections as a full replacement list
151
150
 
152
151
  For additive work on existing systems:
@@ -201,7 +200,7 @@ For additive work on existing systems:
201
200
  - Treat post-write readback as the source of truth, not just write status codes
202
201
  - For views, a top-level `VIEW_APPLY_FAILED` does not prove all requested views failed. Read back the view list and verify which views actually landed.
203
202
  - For views, “view exists” is not the same as “filters are active”. If `app_views_apply` returns `partial_success`, `views_verified=false`, or `details.filter_mismatches`, report the view as created but the filters as unverified until readback confirms them.
204
- - If multiple views share the same name, do not guess which one to update. Read `view_key` from `app_read_views_summary` and pass it explicitly in `upsert_views[]`.
203
+ - If multiple views share the same name, do not guess which one to update. Read `view_key` from `app_get_views` and pass it explicitly in `upsert_views[]`.
205
204
  - In final user-facing summaries, distinguish clearly between:
206
205
  - contract is visible / canonical shape is known
207
206
  - apply precheck succeeded
@@ -218,13 +217,13 @@ For additive work on existing systems:
218
217
  - Create one package: `package_create`
219
218
  - Read one public tool contract: `builder_tool_contract`
220
219
  - Resolve one app: `app_resolve`
221
- - Read one app summary: `app_read_summary`
222
- - Read fields only: `app_read_fields`
223
- - Read layout summary: `app_read_layout_summary`
224
- - Read views summary: `app_read_views_summary`
225
- - Read flow summary: `app_read_flow_summary`
226
- - Read chart summary: `app_read_charts_summary`
227
- - Read portal summary: `portal_read_summary`
220
+ - Read one app summary: `app_get`
221
+ - Read fields only: `app_get_fields`
222
+ - Read layout summary: `app_get_layout`
223
+ - Read views summary: `app_get_views`
224
+ - Read flow summary: `app_get_flow`
225
+ - Read chart summary: `app_get_charts`
226
+ - Read portal config: `portal_get`
228
227
  - Search members for workflow assignees: `member_search`
229
228
  - Search roles for workflow assignees: `role_search`
230
229
  - Create reusable workflow role: `role_create`
@@ -117,7 +117,7 @@ The create route did not resolve in the current backend route context. Re-run `w
117
117
 
118
118
  ### `PACKAGE_ATTACH_FAILED`
119
119
 
120
- Do not retry schema creation. Re-run only `package_attach_app`, then verify with `app_read_summary`.
120
+ Do not retry schema creation. Re-run only `package_attach_app`, then verify with `app_get`.
121
121
 
122
122
  ### Hierarchy modeling mistake
123
123
 
@@ -17,8 +17,8 @@ Use this when the workflow needs real assignees or node-level editable field per
17
17
 
18
18
  ## Recommended order
19
19
 
20
- 1. `app_read_fields`
21
- 2. `app_read_flow_summary`
20
+ 1. `app_get_fields`
21
+ 2. `app_get_flow`
22
22
  3. `role_search`
23
23
  4. `member_search` when the user explicitly names people
24
24
  5. `role_create` when no reusable role exists and the user wants role-based routing
@@ -119,5 +119,5 @@ Use this when the workflow needs real assignees or node-level editable field per
119
119
 
120
120
  ### `UNKNOWN_FLOW_FIELD`
121
121
 
122
- - reread fields with `app_read_fields`
122
+ - reread fields with `app_get_fields`
123
123
  - only pass real current field names to `permissions.editable_fields`
@@ -27,10 +27,10 @@
27
27
  ## Readback scope
28
28
 
29
29
  - Prefer summary reads over large raw payloads
30
- - Use `app_read_fields` before schema or view work
31
- - Use `app_read_layout_summary` before layout work
32
- - Use `app_read_flow_summary` before workflow work
33
- - Use `app_read_views_summary` before view work
30
+ - Use `app_get_fields` before schema or view work
31
+ - Use `app_get_layout` before layout work
32
+ - Use `app_get_flow` before workflow work
33
+ - Use `app_get_views` before view work
34
34
 
35
35
  ## Workflow dependencies
36
36
 
@@ -53,7 +53,7 @@
53
53
  - For flow work, do not replay internal keys from old logs or plan outputs. Public builder calls should stay on:
54
54
  - `assignees.role_ids` / `assignees.member_uids` / `assignees.member_emails`
55
55
  - `permissions.editable_fields`
56
- - For view work, treat `columns` as the only canonical public key. `app_read_views_summary` and `app_views_apply` should all be read and written in that shape.
56
+ - 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.
57
57
  - 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
58
  - 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
59
  - If duplicate view names exist, do not retry by name. Read the exact `view_key` and target that one.
@@ -13,20 +13,20 @@ Use these when you need a quick reminder of the standard v2 builder sequences.
13
13
  ## Update fields on an existing app
14
14
 
15
15
  1. `app_resolve`
16
- 2. `app_read_fields`
16
+ 2. `app_get_fields`
17
17
  3. `app_schema_apply`
18
18
 
19
19
  ## Rework layout
20
20
 
21
- 1. `app_read_layout_summary`
21
+ 1. `app_get_layout`
22
22
  2. `app_layout_apply`
23
23
 
24
24
  Prefer `mode=merge`. Use `mode=replace` only when every field placement is intentional.
25
25
 
26
26
  ## Add or update workflow
27
27
 
28
- 1. `app_read_fields`
29
- 2. `app_read_flow_summary`
28
+ 1. `app_get_fields`
29
+ 2. `app_get_flow`
30
30
  3. `role_search` or `member_search`
31
31
  4. `role_create` if the business wants a reusable role and no good exact role exists
32
32
  5. `app_flow_apply`
@@ -36,8 +36,8 @@ If it reports `FLOW_ASSIGNEE_REQUIRED`, resolve roles or members first and retry
36
36
 
37
37
  ## Add or update views
38
38
 
39
- 1. `app_read_fields`
40
- 2. `app_read_views_summary`
39
+ 1. `app_get_fields`
40
+ 2. `app_get_views`
41
41
  3. `app_views_apply`
42
42
 
43
43
  For both workflow and view work, prefer `suggested_next_call` over re-guessing arguments after a validation failure.
@@ -46,8 +46,8 @@ For both workflow and view work, prefer `suggested_next_call` over re-guessing a
46
46
 
47
47
  Prefer these fields after writes:
48
48
 
49
- - `app_read_summary.tag_ids`
50
- - `app_read_summary.publish_status`
51
- - `app_read_layout_summary.unplaced_fields`
52
- - `app_read_views_summary.views`
53
- - `app_read_flow_summary.nodes`
49
+ - `app_get.tag_ids`
50
+ - `app_get.publish_status`
51
+ - `app_get_layout.unplaced_fields`
52
+ - `app_get_views.views`
53
+ - `app_get_flow.nodes`
@@ -31,13 +31,13 @@ If the user asks for multiple forms/modules that relate to each other, this is a
31
31
 
32
32
  ## Summary reads
33
33
 
34
- - `app_read_summary`: overall app health, publish state, counts
35
- - `app_read_fields`: field names, types, required flags, section ids
36
- - `app_read_layout_summary`: sections, rows, unplaced fields
37
- - `app_read_views_summary`: current view names, types, columns, group-by
38
- - `app_read_flow_summary`: workflow enabled state, nodes, transitions
39
- - `app_read_charts_summary`: current chart ids, names, types, order
40
- - `portal_read_summary`: current draft/live portal metadata and compact section inventory
34
+ - `app_get`: overall app config health, publish state, counts, and builder editability
35
+ - `app_get_fields`: field names, types, required flags, section ids
36
+ - `app_get_layout`: sections, rows, unplaced fields
37
+ - `app_get_views`: current view names, types, columns, group-by
38
+ - `app_get_flow`: workflow enabled state, nodes, transitions
39
+ - `app_get_charts`: current chart ids, names, types, order
40
+ - `portal_get`: current portal config detail and component inventory
41
41
 
42
42
  ## Apply tools
43
43
 
@@ -64,17 +64,17 @@ These execute normalized patches and publish by default unless `publish=false`.
64
64
  - Create a brand new multi-app system/package:
65
65
  `package_create/resolve -> per-app app_schema_apply -> package_attach_app per app -> relation field patches`
66
66
  - Update fields on an existing app:
67
- `app_resolve -> app_read_fields -> app_schema_apply`
67
+ `app_resolve -> app_get_fields -> app_schema_apply`
68
68
  - Tidy layout:
69
- `app_read_fields -> app_read_layout_summary -> builder_tool_contract (if shape is unclear) -> app_layout_apply`
69
+ `app_get_fields -> app_get_layout -> builder_tool_contract (if shape is unclear) -> app_layout_apply`
70
70
  - Add workflow:
71
- `builder_tool_contract -> app_read_fields -> app_read_flow_summary -> role_search/member_search -> app_flow_apply -> app_read_flow_summary`
71
+ `builder_tool_contract -> app_get_fields -> app_get_flow -> role_search/member_search -> app_flow_apply -> app_get_flow`
72
72
  - Add views:
73
- `builder_tool_contract -> app_read_fields -> app_read_views_summary -> app_views_apply -> app_read_views_summary`
73
+ `builder_tool_contract -> app_get_fields -> app_get_views -> app_views_apply -> app_get_views`
74
74
  - Add QingBI charts:
75
- `builder_tool_contract -> app_read_fields -> app_read_charts_summary -> app_charts_apply -> app_read_charts_summary`
75
+ `builder_tool_contract -> app_get_fields -> app_get_charts -> app_charts_apply -> app_get_charts`
76
76
  - Create or update a portal:
77
- `builder_tool_contract -> portal_read_summary -> portal_apply -> portal_read_summary`
77
+ `builder_tool_contract -> portal_get -> portal_apply -> portal_get`
78
78
 
79
79
  ## Avoid
80
80
 
@@ -5,8 +5,8 @@ Use this when the app already exists and the task is only about workflow.
5
5
  ## Minimal sequence
6
6
 
7
7
  1. `builder_tool_contract(tool_name="app_flow_apply")`
8
- 2. `app_read_fields`
9
- 3. `app_read_flow_summary`
8
+ 2. `app_get_fields`
9
+ 3. `app_get_flow`
10
10
  4. `role_search` or `member_search`
11
11
  5. `role_create` if the user wants a reusable directory role and no good role exists
12
12
  6. start from a canonical preset when possible
@@ -17,7 +17,7 @@ Use this when the app already exists and the task is only about workflow.
17
17
  Do not add a second approval/fill node with a new id unless you are intentionally replacing the skeleton.
18
18
  The MCP now auto-aligns the simplest single-node preset overrides, but still prefer explicit preset ids so the merged graph stays predictable.
19
19
  9. `app_flow_apply`
20
- 10. `app_read_flow_summary` when apply returns `partial_success` or the user asked for verification
20
+ 10. `app_get_flow` when apply returns `partial_success` or the user asked for verification
21
21
 
22
22
  If you are unsure about presets or node shapes, call `builder_tool_contract(tool_name="app_flow_apply")` before guessing.
23
23
 
@@ -102,7 +102,7 @@ Preferred recovery:
102
102
 
103
103
  1. use the returned `suggested_next_call`
104
104
  2. apply the minimal schema patch
105
- 3. rerun `app_read_fields`
105
+ 3. rerun `app_get_fields`
106
106
  4. rerun `app_flow_apply`
107
107
 
108
108
  ### `INVALID_FLOW_EDGE`
@@ -115,7 +115,7 @@ The workflow referenced a field name that does not exist, often in:
115
115
 
116
116
  - `permissions.editable_fields`
117
117
 
118
- Call `app_read_fields` and retry with the exact field names returned by the app.
118
+ Call `app_get_fields` and retry with the exact field names returned by the app.
119
119
 
120
120
  ### `FLOW_NODE_TYPE_UNSUPPORTED`
121
121
 
@@ -4,8 +4,8 @@ Use this when fields already exist and the task is only about form grouping or o
4
4
 
5
5
  ## Minimal sequence
6
6
 
7
- 1. `app_read_fields`
8
- 2. `app_read_layout_summary`
7
+ 1. `app_get_fields`
8
+ 2. `app_get_layout`
9
9
  3. `builder_tool_contract` when the section shape is not already obvious
10
10
  4. `app_layout_apply`
11
11
 
@@ -38,7 +38,7 @@ Apply a custom layout with the canonical public section shape:
38
38
 
39
39
  ### `UNKNOWN_LAYOUT_FIELD`
40
40
 
41
- At least one field name does not exist. Re-read with `app_read_fields`.
41
+ At least one field name does not exist. Re-read with `app_get_fields`.
42
42
 
43
43
  ### `DUPLICATE_LAYOUT_FIELD`
44
44
 
@@ -50,7 +50,7 @@ Only happens in `mode=replace`. Switch to `mode=merge` unless you intend to plac
50
50
 
51
51
  ### `LAYOUT_APPLY_FAILED`
52
52
 
53
- Re-read with `app_read_layout_summary`. Check `current_field_names`, `request_id`, and `suggested_next_call`.
53
+ Re-read with `app_get_layout`. Check `current_field_names`, `request_id`, and `suggested_next_call`.
54
54
 
55
55
  ### `VALIDATION_ERROR`
56
56
 
@@ -5,7 +5,7 @@ Use this when the app already exists and the task is only about fields.
5
5
  ## Minimal sequence
6
6
 
7
7
  1. `app_resolve`
8
- 2. `app_read_fields`
8
+ 2. `app_get_fields`
9
9
  3. `app_schema_apply`
10
10
 
11
11
  ## Example
@@ -14,7 +14,7 @@ Read current fields first:
14
14
 
15
15
  ```json
16
16
  {
17
- "tool_name": "app_read_fields",
17
+ "tool_name": "app_get_fields",
18
18
  "arguments": {
19
19
  "profile": "default",
20
20
  "app_key": "APP_123"
@@ -48,7 +48,7 @@ Apply the patch:
48
48
 
49
49
  ### `FIELD_NOT_FOUND`
50
50
 
51
- The selector did not match current schema. Re-read with `app_read_fields` and use `name`, `field_id`, or `que_id`.
51
+ The selector did not match current schema. Re-read with `app_get_fields` and use `name`, `field_id`, or `que_id`.
52
52
 
53
53
  ### `DUPLICATE_FIELD`
54
54
 
@@ -56,7 +56,7 @@ The field already exists with a different shape. Switch from `add_fields` to `up
56
56
 
57
57
  ### `SCHEMA_APPLY_FAILED`
58
58
 
59
- Treat it as uncertain write state. Read back with `app_read_fields` before retrying.
59
+ Treat it as uncertain write state. Read back with `app_get_fields` before retrying.
60
60
 
61
61
  ## Notes
62
62
 
@@ -5,10 +5,10 @@ Use this when the task is only about table, card, board, or gantt views.
5
5
  ## Minimal sequence
6
6
 
7
7
  1. `builder_tool_contract(tool_name="app_views_apply")`
8
- 2. `app_read_fields`
9
- 3. `app_read_views_summary`
8
+ 2. `app_get_fields`
9
+ 3. `app_get_views`
10
10
  4. `app_views_apply`
11
- 5. `app_read_views_summary` again whenever apply returns `failed` or `partial_success`
11
+ 5. `app_get_views` again whenever apply returns `failed` or `partial_success`
12
12
 
13
13
  If you are unsure about keys or view types, call `builder_tool_contract(tool_name="app_views_apply")` before guessing.
14
14
 
@@ -27,7 +27,7 @@ Canonical rules before any example:
27
27
  - Treat `fields` only as a legacy alias the MCP may normalize, not as the preferred shape
28
28
  - Use `filters` with canonical keys `field_name`, `operator`, `value`/`values`
29
29
  - For gantt, use `start_field`, `end_field`, and optionally `title_field`
30
- - If `app_read_views_summary` shows duplicate view names, include `view_key` in `upsert_views[]` and update that exact target
30
+ - If `app_get_views` shows duplicate view names, include `view_key` in `upsert_views[]` and update that exact target
31
31
 
32
32
  Apply a default table view:
33
33
 
@@ -136,7 +136,7 @@ Do not repeat `app_views_apply` with guessed keys. First:
136
136
 
137
137
  1. check `suggested_next_call`
138
138
  2. reuse `canonical_arguments` if present
139
- 3. call `app_read_views_summary` to see whether any requested views landed anyway
139
+ 3. call `app_get_views` to see whether any requested views landed anyway
140
140
  4. if needed, call `builder_tool_contract`
141
141
  5. retry only the minimal failed view patch
142
142
 
@@ -155,8 +155,8 @@ Do not tell the user the filter is active until the readback verification matche
155
155
 
156
156
  - `fields` is accepted as an alias for `columns`, but skill examples should still use `columns`
157
157
  - `column_names` should not appear in skill examples
158
- - `app_read_views_summary` should be treated as canonical readback and now returns `columns`
159
- - If `app_views_apply` returns `AMBIGUOUS_VIEW`, stop and re-run `app_read_views_summary`; then retry with the exact `view_key`
158
+ - `app_get_views` should be treated as canonical readback and now returns `columns`
159
+ - If `app_views_apply` returns `AMBIGUOUS_VIEW`, stop and re-run `app_get_views`; then retry with the exact `view_key`
160
160
  - `filters` are ANDed together as one flat condition group
161
161
  - `app_views_apply` publishes by default
162
162
  - For select-style filters, success means the backend preserved the option value in readback, not just that the view name now exists
@@ -4,6 +4,7 @@ from dataclasses import dataclass
4
4
  from threading import Event
5
5
  from time import sleep
6
6
  from typing import Any
7
+ from urllib.parse import urlsplit, urlunsplit
7
8
  from uuid import uuid4
8
9
 
9
10
  import httpx
@@ -187,7 +187,8 @@ class FlowConditionRulePatch(StrictModel):
187
187
  return value
188
188
  payload = dict(value)
189
189
  if "value" in payload and "values" not in payload:
190
- payload["values"] = [payload.pop("value")]
190
+ raw_value = payload.pop("value")
191
+ payload["values"] = list(raw_value) if isinstance(raw_value, list) else [raw_value]
191
192
  raw_operator = payload.get("operator", payload.get("op"))
192
193
  if isinstance(raw_operator, str):
193
194
  normalized = raw_operator.strip().lower()
@@ -238,7 +239,8 @@ class ViewFilterRulePatch(StrictModel):
238
239
  return value
239
240
  payload = dict(value)
240
241
  if "value" in payload and "values" not in payload:
241
- payload["values"] = [payload.pop("value")]
242
+ raw_value = payload.pop("value")
243
+ payload["values"] = list(raw_value) if isinstance(raw_value, list) else [raw_value]
242
244
  raw_operator = payload.get("operator", payload.get("op"))
243
245
  if isinstance(raw_operator, str):
244
246
  normalized = raw_operator.strip().lower()
@@ -1092,7 +1094,8 @@ class ChartFilterRulePatch(StrictModel):
1092
1094
  return value
1093
1095
  payload = dict(value)
1094
1096
  if "value" in payload and "values" not in payload:
1095
- payload["values"] = [payload.pop("value")]
1097
+ raw_value = payload.pop("value")
1098
+ payload["values"] = list(raw_value) if isinstance(raw_value, list) else [raw_value]
1096
1099
  raw_operator = payload.get("operator", payload.get("op"))
1097
1100
  if isinstance(raw_operator, str):
1098
1101
  normalized = raw_operator.strip().lower()
@@ -1338,7 +1341,7 @@ class PortalApplyRequest(StrictModel):
1338
1341
  FieldPatch.model_rebuild()
1339
1342
 
1340
1343
 
1341
- class AppReadSummaryResponse(StrictModel):
1344
+ class AppGetResponse(StrictModel):
1342
1345
  app_key: str
1343
1346
  title: str | None = None
1344
1347
  app_icon: str | None = None
@@ -1349,39 +1352,48 @@ class AppReadSummaryResponse(StrictModel):
1349
1352
  view_count: int = 0
1350
1353
  workflow_enabled: bool = False
1351
1354
  verification_hints: list[str] = Field(default_factory=list)
1355
+ editability: dict[str, bool | None] = Field(default_factory=dict)
1352
1356
 
1353
1357
 
1354
- class AppFieldsReadResponse(StrictModel):
1358
+ class AppGetFieldsResponse(StrictModel):
1355
1359
  app_key: str
1356
1360
  fields: list[dict[str, Any]] = Field(default_factory=list)
1357
1361
  field_count: int = 0
1358
1362
 
1359
1363
 
1360
- class AppLayoutReadResponse(StrictModel):
1364
+ class AppGetLayoutResponse(StrictModel):
1361
1365
  app_key: str
1362
1366
  sections: list[dict[str, Any]] = Field(default_factory=list)
1363
1367
  unplaced_fields: list[str] = Field(default_factory=list)
1364
1368
  layout_mode_detected: str = "empty"
1365
1369
 
1366
1370
 
1367
- class AppViewsReadResponse(StrictModel):
1371
+ class AppGetViewsResponse(StrictModel):
1368
1372
  app_key: str
1369
1373
  views: list[dict[str, Any]] = Field(default_factory=list)
1370
1374
 
1371
1375
 
1372
- class AppFlowReadResponse(StrictModel):
1376
+ class AppGetFlowResponse(StrictModel):
1373
1377
  app_key: str
1374
1378
  enabled: bool = False
1375
1379
  nodes: list[dict[str, Any]] = Field(default_factory=list)
1376
1380
  transitions: list[dict[str, Any]] = Field(default_factory=list)
1377
1381
 
1378
1382
 
1379
- class AppChartsReadResponse(StrictModel):
1383
+ class AppGetChartsResponse(StrictModel):
1380
1384
  app_key: str
1381
1385
  charts: list[dict[str, Any]] = Field(default_factory=list)
1382
1386
  chart_count: int = 0
1383
1387
 
1384
1388
 
1389
+ AppReadSummaryResponse = AppGetResponse
1390
+ AppFieldsReadResponse = AppGetFieldsResponse
1391
+ AppLayoutReadResponse = AppGetLayoutResponse
1392
+ AppViewsReadResponse = AppGetViewsResponse
1393
+ AppFlowReadResponse = AppGetFlowResponse
1394
+ AppChartsReadResponse = AppGetChartsResponse
1395
+
1396
+
1385
1397
  class PortalListResponse(StrictModel):
1386
1398
  items: list[dict[str, Any]] = Field(default_factory=list)
1387
1399
  total: int = 0
@@ -1415,7 +1427,7 @@ class PortalGetResponse(StrictModel):
1415
1427
 
1416
1428
 
1417
1429
  class ViewGetResponse(StrictModel):
1418
- viewgraph_key: str
1430
+ view_key: str
1419
1431
  base_info: dict[str, Any] = Field(default_factory=dict)
1420
1432
  config: dict[str, Any] = Field(default_factory=dict)
1421
1433
  questions: list[dict[str, Any]] = Field(default_factory=list)
@@ -1426,7 +1438,6 @@ class ChartGetResponse(StrictModel):
1426
1438
  chart_id: str
1427
1439
  base: dict[str, Any] = Field(default_factory=dict)
1428
1440
  config: dict[str, Any] = Field(default_factory=dict)
1429
- data: dict[str, Any] = Field(default_factory=dict)
1430
1441
 
1431
1442
 
1432
1443
  class SchemaPlanRequest(StrictModel):