@josephyan/qingflow-cli 0.2.0-beta.70 → 0.2.0-beta.72

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.
@@ -16,6 +16,7 @@ from .tools.directory_tools import DirectoryTools
16
16
  from .tools.feedback_tools import FeedbackTools
17
17
  from .tools.file_tools import FileTools
18
18
  from .tools.import_tools import ImportTools
19
+ from .tools.resource_read_tools import ResourceReadTools
19
20
  from .tools.task_context_tools import TaskContextTools
20
21
  from .tools.workspace_tools import WorkspaceTools
21
22
 
@@ -95,6 +96,8 @@ Analysis answers must include concrete numbers. When applicable, include percent
95
96
  `record_update_schema_get -> record_update`
96
97
  `record_list / record_get -> record_delete`
97
98
  `record_code_block_schema_get -> record_code_block_run`
99
+ `portal_list -> portal_get -> chart_get / view_get`
100
+ `portal_get -> view_get -> record_list`
98
101
 
99
102
  - Use `columns` as `[{{field_id}}]`
100
103
  - Use `where` items as `{{field_id, op, value}}`
@@ -177,6 +180,7 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
177
180
  workspace = wrap_trimmed_methods(WorkspaceTools(sessions, backend), USER_SERVER_METHOD_MAP)
178
181
  file_tools = wrap_trimmed_methods(FileTools(sessions, backend), USER_SERVER_METHOD_MAP)
179
182
  imports = wrap_trimmed_methods(ImportTools(sessions, backend), USER_SERVER_METHOD_MAP)
183
+ resources = wrap_trimmed_methods(ResourceReadTools(sessions, backend), USER_SERVER_METHOD_MAP)
180
184
  feedback = FeedbackTools(backend, mcp_side="App User MCP")
181
185
  code_block_tools = wrap_trimmed_methods(CodeBlockTools(sessions, backend), USER_SERVER_METHOD_MAP)
182
186
  task_context_tools = wrap_trimmed_methods(TaskContextTools(sessions, backend), USER_SERVER_METHOD_MAP)
@@ -256,6 +260,22 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
256
260
  def app_get(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
257
261
  return apps.app_get(profile=profile, app_key=app_key)
258
262
 
263
+ @server.tool()
264
+ def portal_list(profile: str = DEFAULT_PROFILE) -> dict:
265
+ return resources.portal_list(profile=profile)
266
+
267
+ @server.tool()
268
+ def portal_get(profile: str = DEFAULT_PROFILE, dash_key: str = "") -> dict:
269
+ return resources.portal_get(profile=profile, dash_key=dash_key)
270
+
271
+ @server.tool()
272
+ def view_get(profile: str = DEFAULT_PROFILE, view_id: str = "") -> dict:
273
+ return resources.view_get(profile=profile, view_id=view_id)
274
+
275
+ @server.tool()
276
+ def chart_get(profile: str = DEFAULT_PROFILE, chart_id: str = "") -> dict:
277
+ return resources.chart_get(profile=profile, chart_id=chart_id)
278
+
259
279
  @server.tool()
260
280
  def file_get_upload_info(
261
281
  profile: str = DEFAULT_PROFILE,
@@ -320,7 +340,7 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
320
340
  ) -> dict:
321
341
  try:
322
342
  return trim_public_response(
323
- "feedback_submit",
343
+ "user:feedback_submit",
324
344
  feedback.feedback_submit(
325
345
  category=category,
326
346
  title=title,
@@ -209,28 +209,28 @@ class AiBuilderTools(ToolBase):
209
209
  return self.app_custom_button_delete(profile=profile, app_key=app_key, button_id=button_id)
210
210
 
211
211
  @mcp.tool()
212
- def app_read_summary(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
213
- return self.app_read_summary(profile=profile, app_key=app_key)
212
+ def app_get(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
213
+ return self.app_get(profile=profile, app_key=app_key)
214
214
 
215
215
  @mcp.tool()
216
- def app_read_fields(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
217
- return self.app_read_fields(profile=profile, app_key=app_key)
216
+ def app_get_fields(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
217
+ return self.app_get_fields(profile=profile, app_key=app_key)
218
218
 
219
219
  @mcp.tool()
220
- def app_read_layout_summary(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
221
- return self.app_read_layout_summary(profile=profile, app_key=app_key)
220
+ def app_get_layout(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
221
+ return self.app_get_layout(profile=profile, app_key=app_key)
222
222
 
223
223
  @mcp.tool()
224
- def app_read_views_summary(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
225
- return self.app_read_views_summary(profile=profile, app_key=app_key)
224
+ def app_get_views(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
225
+ return self.app_get_views(profile=profile, app_key=app_key)
226
226
 
227
227
  @mcp.tool()
228
- def app_read_flow_summary(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
229
- return self.app_read_flow_summary(profile=profile, app_key=app_key)
228
+ def app_get_flow(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
229
+ return self.app_get_flow(profile=profile, app_key=app_key)
230
230
 
231
231
  @mcp.tool()
232
- def app_read_charts_summary(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
233
- return self.app_read_charts_summary(profile=profile, app_key=app_key)
232
+ def app_get_charts(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
233
+ return self.app_get_charts(profile=profile, app_key=app_key)
234
234
 
235
235
  @mcp.tool()
236
236
  def portal_list(profile: str = DEFAULT_PROFILE) -> JSONObject:
@@ -245,36 +245,15 @@ class AiBuilderTools(ToolBase):
245
245
  return self.portal_get(profile=profile, dash_key=dash_key, being_draft=being_draft)
246
246
 
247
247
  @mcp.tool()
248
- def portal_read_summary(
249
- profile: str = DEFAULT_PROFILE,
250
- dash_key: str = "",
251
- being_draft: bool = True,
252
- ) -> JSONObject:
253
- return self.portal_read_summary(profile=profile, dash_key=dash_key, being_draft=being_draft)
254
-
255
- @mcp.tool()
256
- def view_get(profile: str = DEFAULT_PROFILE, viewgraph_key: str = "") -> JSONObject:
257
- return self.view_get(profile=profile, viewgraph_key=viewgraph_key)
248
+ def view_get(profile: str = DEFAULT_PROFILE, view_key: str = "") -> JSONObject:
249
+ return self.view_get(profile=profile, view_key=view_key)
258
250
 
259
251
  @mcp.tool()
260
252
  def chart_get(
261
253
  profile: str = DEFAULT_PROFILE,
262
254
  chart_id: str = "",
263
- data_payload: JSONObject | None = None,
264
- page_num: int | None = None,
265
- page_size: int | None = None,
266
- page_num_y: int | None = None,
267
- page_size_y: int | None = None,
268
255
  ) -> JSONObject:
269
- return self.chart_get(
270
- profile=profile,
271
- chart_id=chart_id,
272
- data_payload=data_payload or {},
273
- page_num=page_num,
274
- page_size=page_size,
275
- page_num_y=page_num_y,
276
- page_size_y=page_size_y,
277
- )
256
+ return self.chart_get(profile=profile, chart_id=chart_id)
278
257
 
279
258
  @mcp.tool()
280
259
  def app_schema_apply(
@@ -759,7 +738,16 @@ class AiBuilderTools(ToolBase):
759
738
  lambda: self._facade.app_read_summary(profile=profile, app_key=app_key),
760
739
  error_code="APP_READ_FAILED",
761
740
  normalized_args=normalized_args,
762
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
741
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
742
+ )
743
+
744
+ def app_get(self, *, profile: str, app_key: str) -> JSONObject:
745
+ normalized_args = {"app_key": app_key}
746
+ return _safe_tool_call(
747
+ lambda: self._facade.app_get(profile=profile, app_key=app_key),
748
+ error_code="APP_GET_FAILED",
749
+ normalized_args=normalized_args,
750
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
763
751
  )
764
752
 
765
753
  def app_read_fields(self, *, profile: str, app_key: str) -> JSONObject:
@@ -768,7 +756,16 @@ class AiBuilderTools(ToolBase):
768
756
  lambda: self._facade.app_read_fields(profile=profile, app_key=app_key),
769
757
  error_code="FIELDS_READ_FAILED",
770
758
  normalized_args=normalized_args,
771
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": app_key}},
759
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": app_key}},
760
+ )
761
+
762
+ def app_get_fields(self, *, profile: str, app_key: str) -> JSONObject:
763
+ normalized_args = {"app_key": app_key}
764
+ return _safe_tool_call(
765
+ lambda: self._facade.app_get_fields(profile=profile, app_key=app_key),
766
+ error_code="APP_GET_FIELDS_FAILED",
767
+ normalized_args=normalized_args,
768
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": app_key}},
772
769
  )
773
770
 
774
771
  def app_read_layout_summary(self, *, profile: str, app_key: str) -> JSONObject:
@@ -777,7 +774,16 @@ class AiBuilderTools(ToolBase):
777
774
  lambda: self._facade.app_read_layout_summary(profile=profile, app_key=app_key),
778
775
  error_code="LAYOUT_READ_FAILED",
779
776
  normalized_args=normalized_args,
780
- suggested_next_call={"tool_name": "app_read_layout_summary", "arguments": {"profile": profile, "app_key": app_key}},
777
+ suggested_next_call={"tool_name": "app_get_layout", "arguments": {"profile": profile, "app_key": app_key}},
778
+ )
779
+
780
+ def app_get_layout(self, *, profile: str, app_key: str) -> JSONObject:
781
+ normalized_args = {"app_key": app_key}
782
+ return _safe_tool_call(
783
+ lambda: self._facade.app_get_layout(profile=profile, app_key=app_key),
784
+ error_code="APP_GET_LAYOUT_FAILED",
785
+ normalized_args=normalized_args,
786
+ suggested_next_call={"tool_name": "app_get_layout", "arguments": {"profile": profile, "app_key": app_key}},
781
787
  )
782
788
 
783
789
  def app_read_views_summary(self, *, profile: str, app_key: str) -> JSONObject:
@@ -786,7 +792,16 @@ class AiBuilderTools(ToolBase):
786
792
  lambda: self._facade.app_read_views_summary(profile=profile, app_key=app_key),
787
793
  error_code="VIEWS_READ_FAILED",
788
794
  normalized_args=normalized_args,
789
- suggested_next_call={"tool_name": "app_read_views_summary", "arguments": {"profile": profile, "app_key": app_key}},
795
+ suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
796
+ )
797
+
798
+ def app_get_views(self, *, profile: str, app_key: str) -> JSONObject:
799
+ normalized_args = {"app_key": app_key}
800
+ return _safe_tool_call(
801
+ lambda: self._facade.app_get_views(profile=profile, app_key=app_key),
802
+ error_code="APP_GET_VIEWS_FAILED",
803
+ normalized_args=normalized_args,
804
+ suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
790
805
  )
791
806
 
792
807
  def app_read_flow_summary(self, *, profile: str, app_key: str) -> JSONObject:
@@ -795,7 +810,16 @@ class AiBuilderTools(ToolBase):
795
810
  lambda: self._facade.app_read_flow_summary(profile=profile, app_key=app_key),
796
811
  error_code="FLOW_READ_FAILED",
797
812
  normalized_args=normalized_args,
798
- suggested_next_call={"tool_name": "app_read_flow_summary", "arguments": {"profile": profile, "app_key": app_key}},
813
+ suggested_next_call={"tool_name": "app_get_flow", "arguments": {"profile": profile, "app_key": app_key}},
814
+ )
815
+
816
+ def app_get_flow(self, *, profile: str, app_key: str) -> JSONObject:
817
+ normalized_args = {"app_key": app_key}
818
+ return _safe_tool_call(
819
+ lambda: self._facade.app_get_flow(profile=profile, app_key=app_key),
820
+ error_code="APP_GET_FLOW_FAILED",
821
+ normalized_args=normalized_args,
822
+ suggested_next_call={"tool_name": "app_get_flow", "arguments": {"profile": profile, "app_key": app_key}},
799
823
  )
800
824
 
801
825
  def app_read_charts_summary(self, *, profile: str, app_key: str) -> JSONObject:
@@ -804,7 +828,16 @@ class AiBuilderTools(ToolBase):
804
828
  lambda: self._facade.app_read_charts_summary(profile=profile, app_key=app_key),
805
829
  error_code="CHARTS_READ_FAILED",
806
830
  normalized_args=normalized_args,
807
- suggested_next_call={"tool_name": "app_read_charts_summary", "arguments": {"profile": profile, "app_key": app_key}},
831
+ suggested_next_call={"tool_name": "app_get_charts", "arguments": {"profile": profile, "app_key": app_key}},
832
+ )
833
+
834
+ def app_get_charts(self, *, profile: str, app_key: str) -> JSONObject:
835
+ normalized_args = {"app_key": app_key}
836
+ return _safe_tool_call(
837
+ lambda: self._facade.app_get_charts(profile=profile, app_key=app_key),
838
+ error_code="APP_GET_CHARTS_FAILED",
839
+ normalized_args=normalized_args,
840
+ suggested_next_call={"tool_name": "app_get_charts", "arguments": {"profile": profile, "app_key": app_key}},
808
841
  )
809
842
 
810
843
  def portal_list(self, *, profile: str) -> JSONObject:
@@ -830,13 +863,14 @@ class AiBuilderTools(ToolBase):
830
863
  lambda: self._facade.portal_read_summary(profile=profile, dash_key=dash_key, being_draft=being_draft),
831
864
  error_code="PORTAL_READ_FAILED",
832
865
  normalized_args=normalized_args,
833
- suggested_next_call={"tool_name": "portal_read_summary", "arguments": {"profile": profile, **normalized_args}},
866
+ suggested_next_call={"tool_name": "portal_get", "arguments": {"profile": profile, **normalized_args}},
834
867
  )
835
868
 
836
- def view_get(self, *, profile: str, viewgraph_key: str) -> JSONObject:
837
- normalized_args = {"viewgraph_key": viewgraph_key}
869
+ def view_get(self, *, profile: str, view_key: str = "", viewgraph_key: str = "") -> JSONObject:
870
+ resolved_view_key = str(view_key or viewgraph_key or "").strip()
871
+ normalized_args = {"view_key": resolved_view_key}
838
872
  return _safe_tool_call(
839
- lambda: self._facade.view_get(profile=profile, viewgraph_key=viewgraph_key),
873
+ lambda: self._facade.view_get(profile=profile, view_key=resolved_view_key),
840
874
  error_code="VIEW_GET_FAILED",
841
875
  normalized_args=normalized_args,
842
876
  suggested_next_call={"tool_name": "view_get", "arguments": {"profile": profile, **normalized_args}},
@@ -847,30 +881,10 @@ class AiBuilderTools(ToolBase):
847
881
  *,
848
882
  profile: str,
849
883
  chart_id: str,
850
- data_payload: JSONObject | None = None,
851
- page_num: int | None = None,
852
- page_size: int | None = None,
853
- page_num_y: int | None = None,
854
- page_size_y: int | None = None,
855
884
  ) -> JSONObject:
856
- normalized_args = {
857
- "chart_id": chart_id,
858
- "data_payload": deepcopy(data_payload) if isinstance(data_payload, dict) else {},
859
- "page_num": page_num,
860
- "page_size": page_size,
861
- "page_num_y": page_num_y,
862
- "page_size_y": page_size_y,
863
- }
885
+ normalized_args = {"chart_id": chart_id}
864
886
  return _safe_tool_call(
865
- lambda: self._facade.chart_get(
866
- profile=profile,
867
- chart_id=chart_id,
868
- data_payload=normalized_args["data_payload"],
869
- page_num=page_num,
870
- page_size=page_size,
871
- page_num_y=page_num_y,
872
- page_size_y=page_size_y,
873
- ),
887
+ lambda: self._facade.chart_get(profile=profile, chart_id=chart_id),
874
888
  error_code="CHART_GET_FAILED",
875
889
  normalized_args=normalized_args,
876
890
  suggested_next_call={"tool_name": "chart_get", "arguments": {"profile": profile, "chart_id": chart_id}},
@@ -2517,7 +2531,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2517
2531
  "execution_notes": [
2518
2532
  "apply may return partial_success when some views land and others fail",
2519
2533
  "when duplicate view names exist, supply view_key to target the exact view",
2520
- "read back app_read_views_summary after any failed or partial view apply",
2534
+ "read back app_get_views after any failed or partial view apply",
2521
2535
  "view existence verification and saved-filter verification are separate; treat filters as unverified until verification.view_filters_verified is true",
2522
2536
  "buttons omitted preserves existing button config; buttons=[] clears all buttons; buttons=[...] replaces the full button config",
2523
2537
  ],
@@ -2546,7 +2560,73 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2546
2560
  "remove_views": [],
2547
2561
  },
2548
2562
  },
2549
- "app_read_charts_summary": {
2563
+ "app_get": {
2564
+ "allowed_keys": ["app_key"],
2565
+ "aliases": {},
2566
+ "allowed_values": {},
2567
+ "execution_notes": [
2568
+ "returns builder-side app configuration summary and editability",
2569
+ "use this as the default builder discovery read before fields/layout/views/flow/charts detail reads",
2570
+ "editability reflects builder permissions, not end-user data visibility",
2571
+ ],
2572
+ "minimal_example": {
2573
+ "profile": "default",
2574
+ "app_key": "APP_KEY",
2575
+ },
2576
+ },
2577
+ "app_get_fields": {
2578
+ "allowed_keys": ["app_key"],
2579
+ "aliases": {},
2580
+ "allowed_values": {},
2581
+ "execution_notes": [
2582
+ "returns compact current field configuration for one app",
2583
+ "use this before app_schema_apply when you need exact field definitions",
2584
+ ],
2585
+ "minimal_example": {
2586
+ "profile": "default",
2587
+ "app_key": "APP_KEY",
2588
+ },
2589
+ },
2590
+ "app_get_layout": {
2591
+ "allowed_keys": ["app_key"],
2592
+ "aliases": {},
2593
+ "allowed_values": {},
2594
+ "execution_notes": [
2595
+ "returns compact current layout configuration for one app",
2596
+ "use this before app_layout_apply when you need paragraph and row structure",
2597
+ ],
2598
+ "minimal_example": {
2599
+ "profile": "default",
2600
+ "app_key": "APP_KEY",
2601
+ },
2602
+ },
2603
+ "app_get_views": {
2604
+ "allowed_keys": ["app_key"],
2605
+ "aliases": {},
2606
+ "allowed_values": {},
2607
+ "execution_notes": [
2608
+ "returns compact current view inventory for one app",
2609
+ "use this before app_views_apply when you need exact current view keys",
2610
+ ],
2611
+ "minimal_example": {
2612
+ "profile": "default",
2613
+ "app_key": "APP_KEY",
2614
+ },
2615
+ },
2616
+ "app_get_flow": {
2617
+ "allowed_keys": ["app_key"],
2618
+ "aliases": {},
2619
+ "allowed_values": {},
2620
+ "execution_notes": [
2621
+ "returns workflow configuration summary for one app",
2622
+ "use this before app_flow_apply when you need the current node structure",
2623
+ ],
2624
+ "minimal_example": {
2625
+ "profile": "default",
2626
+ "app_key": "APP_KEY",
2627
+ },
2628
+ },
2629
+ "app_get_charts": {
2550
2630
  "allowed_keys": ["app_key"],
2551
2631
  "aliases": {},
2552
2632
  "allowed_values": {},
@@ -2565,8 +2645,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2565
2645
  "aliases": {},
2566
2646
  "allowed_values": {},
2567
2647
  "execution_notes": [
2568
- "returns the current user's accessible portal list",
2569
- "use this as the portal discovery path before portal_get",
2648
+ "returns builder-configurable portal list items only",
2649
+ "use this as the builder portal discovery path before portal_get",
2570
2650
  "results are compact list items, not raw dash payloads",
2571
2651
  ],
2572
2652
  "minimal_example": {
@@ -2578,8 +2658,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2578
2658
  "aliases": {"beingDraft": "being_draft"},
2579
2659
  "allowed_values": {},
2580
2660
  "execution_notes": [
2581
- "returns portal-level detail plus a component inventory",
2582
- "chart and view components are returned as refs only; use chart_get or view_get for more detail",
2661
+ "returns builder-side portal configuration detail plus a normalized component inventory",
2662
+ "chart and view components are returned as refs only; use builder chart_get or builder view_get for configuration detail",
2583
2663
  "being_draft=true reads the current draft view; being_draft=false reads live",
2584
2664
  ],
2585
2665
  "minimal_example": {
@@ -2616,69 +2696,32 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2616
2696
  "reorder_chart_ids": [],
2617
2697
  },
2618
2698
  },
2619
- "chart_apply": {
2620
- "allowed_keys": ["app_key", "upsert_charts", "remove_chart_ids", "reorder_chart_ids"],
2621
- "aliases": {
2622
- "legacy_tool_name": "app_charts_apply",
2623
- },
2624
- "allowed_values": {
2625
- "chart.chart_type": [member.value for member in PublicChartType],
2626
- "chart.filter.operator": [member.value for member in ViewFilterOperator],
2627
- },
2628
- "execution_notes": [
2629
- "legacy compatibility alias; prefer app_charts_apply in new builder flows",
2630
- "behavior matches app_charts_apply exactly",
2631
- ],
2632
- "minimal_example": {
2633
- "profile": "default",
2634
- "app_key": "APP_KEY",
2635
- "upsert_charts": [{"name": "数据总量", "chart_type": "target", "indicator_field_ids": []}],
2636
- "remove_chart_ids": [],
2637
- "reorder_chart_ids": [],
2638
- },
2639
- },
2640
- "portal_read_summary": {
2641
- "allowed_keys": ["dash_key", "being_draft"],
2642
- "aliases": {"beingDraft": "being_draft"},
2643
- "allowed_values": {},
2644
- "execution_notes": [
2645
- "returns a compact portal summary instead of the raw dash payload",
2646
- "being_draft=true reads the current draft view; being_draft=false reads live",
2647
- "use this before portal_apply when you need the current section inventory or target dash metadata",
2648
- ],
2649
- "minimal_example": {
2650
- "profile": "default",
2651
- "dash_key": "DASH_KEY",
2652
- "being_draft": True,
2653
- },
2654
- },
2655
2699
  "view_get": {
2656
- "allowed_keys": ["viewgraph_key"],
2657
- "aliases": {"viewKey": "viewgraph_key"},
2700
+ "allowed_keys": ["view_key"],
2701
+ "aliases": {},
2658
2702
  "allowed_values": {},
2659
2703
  "execution_notes": [
2660
- "returns one view's definition detail",
2661
- "does not return record data; use record_list with app_key + view_id for rows",
2662
- "use this after portal_get when a component references a view_ref.view_key",
2704
+ "returns one builder-side view definition detail",
2705
+ "does not return record data; use user-side view_get or record_list for runtime rows",
2706
+ "use this after builder portal_get when a component references a view_ref.view_key",
2663
2707
  ],
2664
2708
  "minimal_example": {
2665
2709
  "profile": "default",
2666
- "viewgraph_key": "VIEW_KEY",
2710
+ "view_key": "VIEW_KEY",
2667
2711
  },
2668
2712
  },
2669
2713
  "chart_get": {
2670
- "allowed_keys": ["chart_id", "data_payload", "page_num", "page_size", "page_num_y", "page_size_y"],
2671
- "aliases": {"payload": "data_payload"},
2714
+ "allowed_keys": ["chart_id"],
2715
+ "aliases": {},
2672
2716
  "allowed_values": {},
2673
2717
  "execution_notes": [
2674
- "returns chart base info, chart config, and chart data together",
2718
+ "returns builder-side chart base info and chart config only",
2675
2719
  "chart_id is required; chart names are not accepted here",
2676
- "data_payload defaults to {} so chart_get queries concrete chart data by default",
2720
+ "does not return chart data; use user-side chart_get for runtime data access",
2677
2721
  ],
2678
2722
  "minimal_example": {
2679
2723
  "profile": "default",
2680
2724
  "chart_id": "CHART_ID",
2681
- "data_payload": {},
2682
2725
  },
2683
2726
  },
2684
2727
  "portal_apply": {
@@ -2745,6 +2788,4 @@ _PRIVATE_BUILDER_TOOL_CONTRACTS = {
2745
2788
  "app_views_plan",
2746
2789
  }
2747
2790
 
2748
- _BUILDER_TOOL_CONTRACT_ALIASES = {
2749
- "chart_apply": "app_charts_apply",
2750
- }
2791
+ _BUILDER_TOOL_CONTRACT_ALIASES = {}
@@ -179,9 +179,6 @@ class AppTools(ToolBase):
179
179
  accessible_views.extend(self._resolve_accessible_custom_views(context, app_key))
180
180
  import_capability, import_warnings = _derive_import_capability(base_info)
181
181
  warnings.extend(import_warnings)
182
- editability, editability_warnings = _derive_editability(base_info)
183
- warnings.extend(editability_warnings)
184
-
185
182
  return {
186
183
  "profile": profile,
187
184
  "ws_id": session_profile.selected_ws_id,
@@ -193,7 +190,6 @@ class AppTools(ToolBase):
193
190
  "app_name": app_name,
194
191
  "can_create": can_create,
195
192
  "import_capability": import_capability,
196
- "editability": editability,
197
193
  "accessible_views": accessible_views,
198
194
  },
199
195
  }
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import json
3
4
  from uuid import uuid4
4
5
 
5
6
  from mcp.server.fastmcp import FastMCP
@@ -18,6 +19,37 @@ def _qingbi_base_url(base_url: str) -> str:
18
19
  return normalized[:-4] if normalized.endswith("/api") else normalized
19
20
 
20
21
 
22
+ def _should_retry_qflow_base(error: QingflowApiError) -> bool:
23
+ return int(getattr(error, "backend_code", 0) or 0) == 81007
24
+
25
+
26
+ def _should_retry_asos_data(error: QingflowApiError) -> bool:
27
+ backend_code = int(getattr(error, "backend_code", 0) or 0)
28
+ http_status = getattr(error, "http_status", None)
29
+ return backend_code in {44011, 81007} or http_status == 404
30
+
31
+
32
+ def _coerce_tool_error(error: RuntimeError | QingflowApiError) -> QingflowApiError | None:
33
+ if isinstance(error, QingflowApiError):
34
+ return error
35
+ if not isinstance(error, RuntimeError):
36
+ return None
37
+ try:
38
+ payload = json.loads(str(error))
39
+ except Exception:
40
+ return None
41
+ if not isinstance(payload, dict):
42
+ return None
43
+ return QingflowApiError(
44
+ category=str(payload.get("category") or "runtime"),
45
+ message=str(payload.get("message") or str(error)),
46
+ backend_code=payload.get("backend_code"),
47
+ request_id=payload.get("request_id"),
48
+ http_status=payload.get("http_status"),
49
+ details=payload.get("details") if isinstance(payload.get("details"), dict) else None,
50
+ )
51
+
52
+
21
53
  class QingbiReportTools(ToolBase):
22
54
  def register(self, mcp: FastMCP) -> None:
23
55
  @mcp.tool()
@@ -127,7 +159,13 @@ class QingbiReportTools(ToolBase):
127
159
 
128
160
  def qingbi_report_get_base(self, *, profile: str, chart_id: str) -> JSONObject:
129
161
  self._require_chart_id(chart_id)
130
- return self._request(profile, "GET", f"/qingbi/charts/baseinfo/{chart_id}", chart_id=chart_id)
162
+ try:
163
+ return self._request(profile, "GET", f"/qingbi/charts/baseinfo/{chart_id}", chart_id=chart_id)
164
+ except (QingflowApiError, RuntimeError) as raw_error:
165
+ error = _coerce_tool_error(raw_error)
166
+ if error is None or not _should_retry_qflow_base(error):
167
+ raise
168
+ return self._request(profile, "GET", f"/qingbi/charts/qflow/baseinfo/{chart_id}", chart_id=chart_id)
131
169
 
132
170
  def qingbi_report_update_base(self, *, profile: str, chart_id: str, payload: JSONObject) -> JSONObject:
133
171
  self._require_chart_id(chart_id)
@@ -166,21 +204,34 @@ class QingbiReportTools(ToolBase):
166
204
  params["pageNumY"] = page_num_y
167
205
  if page_size_y is not None:
168
206
  params["pageSizeY"] = page_size_y
169
- if payload:
207
+ try:
208
+ if payload:
209
+ return self._request(
210
+ profile,
211
+ "POST",
212
+ f"/qingbi/charts/data/qflow/{chart_id}/detail",
213
+ chart_id=chart_id,
214
+ params=params,
215
+ json_body=payload,
216
+ )
170
217
  return self._request(
171
218
  profile,
172
- "POST",
173
- f"/qingbi/charts/data/qflow/{chart_id}/detail",
219
+ "GET",
220
+ f"/qingbi/charts/data/qflow/{chart_id}",
174
221
  chart_id=chart_id,
175
222
  params=params,
176
- json_body=payload,
177
223
  )
224
+ except (QingflowApiError, RuntimeError) as raw_error:
225
+ error = _coerce_tool_error(raw_error)
226
+ if error is None or not _should_retry_asos_data(error):
227
+ raise
178
228
  return self._request(
179
229
  profile,
180
- "GET",
181
- f"/qingbi/charts/data/qflow/{chart_id}",
230
+ "POST",
231
+ f"/qingbi/charts/data/qflow/{chart_id}/asos",
182
232
  chart_id=chart_id,
183
233
  params=params,
234
+ json_body=payload or {},
184
235
  )
185
236
 
186
237
  def qingbi_report_delete(self, *, profile: str, chart_id: str) -> JSONObject: