@qingflow-tech/qingflow-app-builder-mcp 1.0.2 → 1.0.3

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 (42) hide show
  1. package/README.md +2 -2
  2. package/docs/local-agent-install.md +9 -3
  3. package/npm/lib/runtime.mjs +10 -3
  4. package/package.json +1 -1
  5. package/pyproject.toml +1 -1
  6. package/skills/qingflow-app-builder/SKILL.md +88 -184
  7. package/skills/qingflow-app-builder/references/create-app.md +15 -34
  8. package/skills/qingflow-app-builder/references/gotchas.md +3 -3
  9. package/skills/qingflow-app-builder/references/solution-playbooks.md +1 -2
  10. package/skills/qingflow-app-builder/references/tool-selection.md +9 -10
  11. package/src/qingflow_mcp/__init__.py +33 -1
  12. package/src/qingflow_mcp/builder_facade/models.py +14 -4
  13. package/src/qingflow_mcp/builder_facade/service.py +1582 -124
  14. package/src/qingflow_mcp/cli/commands/auth.py +63 -0
  15. package/src/qingflow_mcp/cli/commands/builder.py +4 -3
  16. package/src/qingflow_mcp/cli/commands/record.py +5 -5
  17. package/src/qingflow_mcp/cli/commands/task.py +74 -22
  18. package/src/qingflow_mcp/cli/commands/workspace.py +22 -0
  19. package/src/qingflow_mcp/cli/formatters.py +287 -48
  20. package/src/qingflow_mcp/cli/main.py +6 -1
  21. package/src/qingflow_mcp/cli/qingflow_login.py +116 -0
  22. package/src/qingflow_mcp/config.py +1 -1
  23. package/src/qingflow_mcp/errors.py +2 -2
  24. package/src/qingflow_mcp/id_utils.py +49 -0
  25. package/src/qingflow_mcp/public_surface.py +11 -1
  26. package/src/qingflow_mcp/response_trim.py +380 -9
  27. package/src/qingflow_mcp/server.py +4 -0
  28. package/src/qingflow_mcp/server_app_builder.py +11 -1
  29. package/src/qingflow_mcp/server_app_user.py +24 -0
  30. package/src/qingflow_mcp/session_store.py +69 -15
  31. package/src/qingflow_mcp/solution/compiler/form_compiler.py +2 -2
  32. package/src/qingflow_mcp/solution/executor.py +2 -2
  33. package/src/qingflow_mcp/tools/ai_builder_tools.py +48 -18
  34. package/src/qingflow_mcp/tools/app_tools.py +1 -0
  35. package/src/qingflow_mcp/tools/auth_tools.py +217 -9
  36. package/src/qingflow_mcp/tools/base.py +6 -2
  37. package/src/qingflow_mcp/tools/code_block_tools.py +2 -2
  38. package/src/qingflow_mcp/tools/import_tools.py +36 -2
  39. package/src/qingflow_mcp/tools/record_tools.py +410 -156
  40. package/src/qingflow_mcp/tools/resource_read_tools.py +114 -32
  41. package/src/qingflow_mcp/tools/task_context_tools.py +899 -141
  42. package/src/qingflow_mcp/tools/workspace_tools.py +141 -0
@@ -4,7 +4,7 @@ Use the smallest v2 builder tool chain that can finish the task.
4
4
 
5
5
  ## Default path
6
6
 
7
- `resolve -> summary read -> apply -> attach -> publish_verify`
7
+ `summary read -> apply -> publish_verify`
8
8
 
9
9
  Public builder `apply` tools already perform server-side planning, normalization, and dependency checks internally. Do not route normal public builder work through explicit `*_plan` tools.
10
10
 
@@ -21,13 +21,12 @@ If the user asks for multiple forms/modules that relate to each other, this is a
21
21
 
22
22
  ## Resolve
23
23
 
24
- - `package_create`: create a new package only after the user confirms package creation; exact-name duplicates return `noop=true`
25
- - `package_resolve`: exact package lookup by name
26
- - `package_list`: read-only fallback when package resolution is ambiguous
24
+ - `package_get`: read one known package by `package_id`
25
+ - `package_apply`: create or update one package; use `create_if_missing=true` only after explicit user intent
27
26
  - `member_search`: resolve named people from the directory
28
27
  - `role_search`: resolve reusable roles from the directory
29
28
  - `role_create`: create a reusable role when the business owner wants role-based routing
30
- - `app_resolve`: locate an existing app by exactly one selector mode: `app_key`, or `app_name + package_tag_id`
29
+ - `app_resolve`: locate an existing app by exactly one selector mode: `app_key`, or `app_name + package_id`
31
30
 
32
31
  ## Summary reads
33
32
 
@@ -48,21 +47,20 @@ These execute normalized patches and publish by default unless `publish=false`.
48
47
  - `app_flow_apply`: replace workflow
49
48
  - `app_views_apply`: upsert or remove views
50
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
51
- - `portal_apply`: create or replace-update portal pages; use `dash_key` for update mode or `package_tag_id + dash_name` for create mode; sections are replace-only and omission deletes old sections; `publish=false` only guarantees draft/base-info updates
50
+ - `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
52
51
 
53
52
  ## Explicit post-apply tools
54
53
 
55
- - `package_attach_app`: attach an app to a package with `tag_id + app_key`; do not assume create or publish attaches it
56
54
  - `app_publish_verify`: explicit final publish verification when the user asks for live confirmation
57
55
 
58
56
  ## Decision shortcuts
59
57
 
60
58
  - Create one app inside an existing package:
61
- `package_resolve -> app_resolve -> app_schema_apply -> package_attach_app`
59
+ `package_get -> app_resolve -> app_schema_apply`
62
60
  - Create a brand new package, then create one app in it:
63
- `package_create -> package_resolve -> app_schema_apply -> package_attach_app`
61
+ `package_apply(create_if_missing=true) -> app_schema_apply`
64
62
  - Create a brand new multi-app system/package:
65
- `package_create/resolve -> per-app app_schema_apply -> package_attach_app per app -> relation field patches`
63
+ `package_apply(create_if_missing=true) -> per-app app_schema_apply -> relation field patches`
66
64
  - Update fields on an existing app:
67
65
  `app_resolve -> app_get_fields -> app_schema_apply`
68
66
  - Tidy layout:
@@ -81,6 +79,7 @@ These execute normalized patches and publish by default unless `publish=false`.
81
79
  - Do not handcraft raw Qingflow schema payloads
82
80
  - Do not rely on internal `solution_*` tools in public builder flows
83
81
  - Do not create a new package without first asking the user to confirm package creation
82
+ - Do not regress to `package_create` or `package_attach_app` as the public default story
84
83
  - Do not treat a package/system name as `app_name` when the user clearly wants multiple apps inside it
85
84
  - Do not compress multiple business objects into one app with several text fields
86
85
  - Do not skip summary reads before flow or view work
@@ -1,5 +1,37 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from importlib.metadata import PackageNotFoundError, packages_distributions, version as _dist_version
4
+ from pathlib import Path
5
+
3
6
  __all__ = ["__version__"]
4
7
 
5
- __version__ = "0.2.0b87"
8
+ _FALLBACK_VERSION = "0.2.0b998"
9
+
10
+
11
+ def _resolve_local_pyproject_version() -> str | None:
12
+ module_path = Path(__file__).resolve()
13
+ for parent in module_path.parents:
14
+ candidate = parent / "pyproject.toml"
15
+ if not candidate.is_file():
16
+ continue
17
+ for line in candidate.read_text(encoding="utf-8").splitlines():
18
+ stripped = line.strip()
19
+ if stripped.startswith("version = "):
20
+ return stripped.split("=", 1)[1].strip().strip('"')
21
+ break
22
+ return None
23
+
24
+
25
+ def _resolve_runtime_version() -> str:
26
+ local_version = _resolve_local_pyproject_version()
27
+ if local_version:
28
+ return local_version
29
+ for dist_name in packages_distributions().get("qingflow_mcp", []):
30
+ try:
31
+ return _dist_version(dist_name)
32
+ except PackageNotFoundError:
33
+ continue
34
+ return _FALLBACK_VERSION
35
+
36
+
37
+ __version__ = _resolve_runtime_version()
@@ -819,6 +819,10 @@ class FieldMutation(StrictModel):
819
819
  validation_alias=AliasChoices("custom_button_text", "customButtonText", "custom_btn_text", "customBtnText"),
820
820
  )
821
821
  subfields: list[FieldPatch] | None = None
822
+ subfield_updates: list["FieldUpdatePatch"] | None = Field(
823
+ default=None,
824
+ validation_alias=AliasChoices("subfield_updates", "subfieldUpdates"),
825
+ )
822
826
 
823
827
  @model_validator(mode="after")
824
828
  def validate_shape(self) -> "FieldMutation":
@@ -848,8 +852,12 @@ class FieldMutation(StrictModel):
848
852
  or self.custom_button_text is not None
849
853
  ):
850
854
  raise ValueError("code_block_config, code_block_binding, auto_trigger, custom_button_text_enabled, and custom_button_text are only allowed for code_block fields")
851
- if self.type == PublicFieldType.subtable and not self.subfields:
852
- raise ValueError("subtable field requires subfields")
855
+ if self.type == PublicFieldType.subtable and not self.subfields and not self.subfield_updates:
856
+ raise ValueError("subtable field requires subfields or subfield_updates")
857
+ if self.type is not None and self.type != PublicFieldType.subtable and self.subfield_updates:
858
+ raise ValueError("subfield_updates are only allowed for subtable fields")
859
+ if self.subfields and self.subfield_updates:
860
+ raise ValueError("subfields and subfield_updates cannot be used together")
853
861
  return self
854
862
 
855
863
  @model_validator(mode="before")
@@ -1528,14 +1536,16 @@ class PortalApplyRequest(StrictModel):
1528
1536
  raise ValueError("package_tag_id is required when dash_key is empty")
1529
1537
  if not self.dash_key and not self.dash_name:
1530
1538
  raise ValueError("dash_name is required when creating a portal")
1531
- if not self.sections:
1532
- raise ValueError("portal apply requires a non-empty sections list")
1539
+ if not self.dash_key and not self.sections:
1540
+ raise ValueError("portal apply requires a non-empty sections list when creating a portal")
1533
1541
  if self.visibility is not None and self.auth is not None:
1534
1542
  raise ValueError("visibility and auth cannot be provided together")
1535
1543
  return self
1536
1544
 
1537
1545
 
1538
1546
  FieldPatch.model_rebuild()
1547
+ FieldMutation.model_rebuild()
1548
+ FieldUpdatePatch.model_rebuild()
1539
1549
 
1540
1550
 
1541
1551
  class AppGetResponse(StrictModel):