@josephyan/qingflow-app-builder-mcp 0.2.0-beta.3 → 0.2.0-beta.4

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 @josephyan/qingflow-app-builder-mcp@0.2.0-beta.3
6
+ npm install @josephyan/qingflow-app-builder-mcp@0.2.0-beta.4
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.3 qingflow-app-builder-mcp
12
+ npx -y -p @josephyan/qingflow-app-builder-mcp@0.2.0-beta.4 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.3",
3
+ "version": "0.2.0-beta.4",
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.0b3"
7
+ version = "0.2.0b4"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -2,4 +2,4 @@ from __future__ import annotations
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "0.2.0b3"
5
+ __version__ = "0.2.0b4"
@@ -762,16 +762,7 @@ class AiBuilderFacade:
762
762
  app_name=str(resolved["app_name"]),
763
763
  tag_ids=_coerce_int_list(resolved.get("tag_ids")),
764
764
  )
765
- schema = self.apps.app_get_form_schema(
766
- profile=profile,
767
- app_key=target.app_key,
768
- form_type=1,
769
- being_draft=True,
770
- being_apply=None,
771
- audit_node_id=None,
772
- include_raw=True,
773
- )
774
- schema_result = schema.get("result") if isinstance(schema.get("result"), dict) else {}
765
+ schema_result, _schema_source = self._read_schema_with_fallback(profile=profile, app_key=target.app_key)
775
766
  parsed = _parse_schema(schema_result)
776
767
  current_fields = parsed["fields"]
777
768
  layout = parsed["layout"]
@@ -938,16 +929,7 @@ class AiBuilderFacade:
938
929
  "sections": [section.model_dump(mode="json") for section in sections],
939
930
  "publish": publish,
940
931
  }
941
- schema = self.apps.app_get_form_schema(
942
- profile=profile,
943
- app_key=app_key,
944
- form_type=1,
945
- being_draft=True,
946
- being_apply=None,
947
- audit_node_id=None,
948
- include_raw=True,
949
- )
950
- schema_result = schema.get("result") if isinstance(schema.get("result"), dict) else {}
932
+ schema_result, _schema_source = self._read_schema_with_fallback(profile=profile, app_key=app_key)
951
933
  parsed = _parse_schema(schema_result)
952
934
  current_fields = parsed["fields"]
953
935
  fields_by_name = {field["name"]: field for field in current_fields}
@@ -1130,15 +1112,7 @@ class AiBuilderFacade:
1130
1112
  suggested_next_call={"tool_name": "app_flow_plan", "arguments": {"profile": profile, **normalized_args}},
1131
1113
  )
1132
1114
  base = self.apps.app_get_base(profile=profile, app_key=app_key, include_raw=True).get("result") or {}
1133
- schema = self.apps.app_get_form_schema(
1134
- profile=profile,
1135
- app_key=app_key,
1136
- form_type=1,
1137
- being_draft=True,
1138
- being_apply=None,
1139
- audit_node_id=None,
1140
- include_raw=True,
1141
- ).get("result") or {}
1115
+ schema, _schema_source = self._read_schema_with_fallback(profile=profile, app_key=app_key)
1142
1116
  entity = _entity_spec_from_app(base_info=base, schema=schema, views=None)
1143
1117
  workflow_spec = _build_public_workflow_spec(nodes=nodes, transitions=transitions)
1144
1118
  if workflow_spec.get("status") == "failed":
@@ -1246,15 +1220,7 @@ class AiBuilderFacade:
1246
1220
  }
1247
1221
  return self._append_publish_result(profile=profile, app_key=app_key, publish=publish, response=response)
1248
1222
  base = self.apps.app_get_base(profile=profile, app_key=app_key, include_raw=True).get("result") or {}
1249
- schema = self.apps.app_get_form_schema(
1250
- profile=profile,
1251
- app_key=app_key,
1252
- form_type=1,
1253
- being_draft=True,
1254
- being_apply=None,
1255
- audit_node_id=None,
1256
- include_raw=True,
1257
- ).get("result") or {}
1223
+ schema, _schema_source = self._read_schema_with_fallback(profile=profile, app_key=app_key)
1258
1224
  existing_views = self.views.view_list_flat(profile=profile, app_key=app_key).get("result") or []
1259
1225
  existing_by_name = {}
1260
1226
  for view in existing_views if isinstance(existing_views, list) else []:
@@ -1527,27 +1493,49 @@ class AiBuilderFacade:
1527
1493
 
1528
1494
  def _load_app_state(self, *, profile: str, app_key: str) -> dict[str, Any]:
1529
1495
  base = self.apps.app_get_base(profile=profile, app_key=app_key, include_raw=True)
1530
- schema = self.apps.app_get_form_schema(
1531
- profile=profile,
1532
- app_key=app_key,
1533
- form_type=1,
1534
- being_draft=True,
1535
- being_apply=None,
1536
- audit_node_id=None,
1537
- include_raw=True,
1538
- )
1496
+ schema_result, schema_source = self._read_schema_with_fallback(profile=profile, app_key=app_key)
1539
1497
  views = self.views.view_list_flat(profile=profile, app_key=app_key)
1540
1498
  workflow = self.workflows.workflow_list_nodes(profile=profile, app_key=app_key)
1541
1499
  base_result = base.get("result") if isinstance(base.get("result"), dict) else {}
1542
- schema_result = schema.get("result") if isinstance(schema.get("result"), dict) else {}
1543
1500
  return {
1544
1501
  "base": base_result,
1545
1502
  "schema": schema_result,
1546
1503
  "parsed": _parse_schema(schema_result),
1547
1504
  "views": views.get("result"),
1548
1505
  "workflow": workflow.get("result"),
1506
+ "schema_source": schema_source,
1549
1507
  }
1550
1508
 
1509
+ def _read_schema_with_fallback(self, *, profile: str, app_key: str) -> tuple[dict[str, Any], str]:
1510
+ attempts = (
1511
+ ("draft", True),
1512
+ ("current", None),
1513
+ ("published", False),
1514
+ )
1515
+ last_error: Exception | None = None
1516
+ for label, being_draft in attempts:
1517
+ try:
1518
+ schema = self.apps.app_get_form_schema(
1519
+ profile=profile,
1520
+ app_key=app_key,
1521
+ form_type=1,
1522
+ being_draft=being_draft,
1523
+ being_apply=None,
1524
+ audit_node_id=None,
1525
+ include_raw=True,
1526
+ )
1527
+ result = schema.get("result")
1528
+ return (result if isinstance(result, dict) else {}), label
1529
+ except (QingflowApiError, RuntimeError) as error:
1530
+ api_error = _coerce_api_error(error)
1531
+ last_error = error
1532
+ if api_error.http_status == 404:
1533
+ continue
1534
+ raise
1535
+ if last_error is not None:
1536
+ raise last_error
1537
+ return {}, "unknown"
1538
+
1551
1539
  def _preview_target_app(
1552
1540
  self,
1553
1541
  *,