@josephyan/qingflow-app-builder-mcp 1.1.4 → 1.1.6

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 (98) hide show
  1. package/README.md +6 -5
  2. package/docs/local-agent-install.md +57 -6
  3. package/entry_point.py +1 -1
  4. package/npm/bin/qingflow-app-builder-mcp.mjs +2 -33
  5. package/npm/bin/qingflow-skills.mjs +5 -0
  6. package/npm/lib/runtime.mjs +21 -101
  7. package/npm/scripts/postinstall.mjs +1 -10
  8. package/package.json +3 -2
  9. package/pyproject.toml +1 -1
  10. package/skills/qingflow-app-builder/SKILL.md +51 -34
  11. package/skills/qingflow-app-builder/references/complete-system-development-guide.md +123 -0
  12. package/skills/qingflow-app-builder/references/create-app.md +19 -6
  13. package/skills/qingflow-app-builder/references/environments.md +1 -1
  14. package/skills/qingflow-app-builder/references/gotchas.md +14 -6
  15. package/skills/qingflow-app-builder/references/match-rules.md +15 -0
  16. package/skills/qingflow-app-builder/references/single-app-development-guide.md +58 -0
  17. package/skills/qingflow-app-builder/references/solution-playbooks.md +10 -0
  18. package/skills/qingflow-app-builder/references/tool-selection.md +21 -22
  19. package/skills/qingflow-app-builder/references/update-flow.md +22 -38
  20. package/skills/qingflow-app-builder/references/update-schema.md +3 -0
  21. package/skills/qingflow-app-builder/references/update-views.md +34 -14
  22. package/skills/qingflow-app-builder/scripts/validate_system_build_summary.py +124 -0
  23. package/skills/qingflow-app-builder-code-integrations/SKILL.md +5 -3
  24. package/skills/qingflow-app-builder-code-integrations/references/code-block.md +1 -1
  25. package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +1 -1
  26. package/src/qingflow_mcp/__init__.py +1 -1
  27. package/src/qingflow_mcp/__main__.py +6 -2
  28. package/src/qingflow_mcp/builder_facade/models.py +282 -102
  29. package/src/qingflow_mcp/builder_facade/service.py +4192 -935
  30. package/src/qingflow_mcp/cli/commands/builder.py +316 -298
  31. package/src/qingflow_mcp/cli/commands/chart.py +1 -1
  32. package/src/qingflow_mcp/cli/commands/common.py +12 -3
  33. package/src/qingflow_mcp/cli/commands/exports.py +2 -2
  34. package/src/qingflow_mcp/cli/commands/imports.py +3 -3
  35. package/src/qingflow_mcp/cli/commands/portal.py +2 -2
  36. package/src/qingflow_mcp/cli/commands/record.py +101 -27
  37. package/src/qingflow_mcp/cli/commands/task.py +28 -47
  38. package/src/qingflow_mcp/cli/commands/view.py +1 -1
  39. package/src/qingflow_mcp/cli/context.py +0 -3
  40. package/src/qingflow_mcp/cli/formatters.py +784 -16
  41. package/src/qingflow_mcp/cli/main.py +117 -33
  42. package/src/qingflow_mcp/errors.py +43 -2
  43. package/src/qingflow_mcp/public_surface.py +26 -17
  44. package/src/qingflow_mcp/response_trim.py +81 -17
  45. package/src/qingflow_mcp/server.py +14 -12
  46. package/src/qingflow_mcp/server_app_builder.py +65 -21
  47. package/src/qingflow_mcp/server_app_user.py +22 -16
  48. package/src/qingflow_mcp/session_store.py +11 -7
  49. package/src/qingflow_mcp/solution/compiler/__init__.py +3 -1
  50. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +173 -0
  51. package/src/qingflow_mcp/solution/executor.py +245 -18
  52. package/src/qingflow_mcp/tools/ai_builder_tools.py +1780 -406
  53. package/src/qingflow_mcp/tools/app_tools.py +184 -43
  54. package/src/qingflow_mcp/tools/approval_tools.py +197 -35
  55. package/src/qingflow_mcp/tools/auth_tools.py +92 -16
  56. package/src/qingflow_mcp/tools/code_block_tools.py +298 -40
  57. package/src/qingflow_mcp/tools/custom_button_tools.py +64 -10
  58. package/src/qingflow_mcp/tools/directory_tools.py +236 -72
  59. package/src/qingflow_mcp/tools/export_tools.py +244 -34
  60. package/src/qingflow_mcp/tools/feedback_tools.py +9 -0
  61. package/src/qingflow_mcp/tools/file_tools.py +9 -3
  62. package/src/qingflow_mcp/tools/import_tools.py +336 -49
  63. package/src/qingflow_mcp/tools/navigation_tools.py +91 -12
  64. package/src/qingflow_mcp/tools/package_tools.py +118 -6
  65. package/src/qingflow_mcp/tools/portal_tools.py +39 -3
  66. package/src/qingflow_mcp/tools/qingbi_report_tools.py +116 -7
  67. package/src/qingflow_mcp/tools/record_tools.py +1141 -356
  68. package/src/qingflow_mcp/tools/resource_read_tools.py +188 -39
  69. package/src/qingflow_mcp/tools/role_tools.py +80 -9
  70. package/src/qingflow_mcp/tools/solution_tools.py +59 -45
  71. package/src/qingflow_mcp/tools/task_context_tools.py +662 -158
  72. package/src/qingflow_mcp/tools/task_tools.py +113 -29
  73. package/src/qingflow_mcp/tools/view_tools.py +106 -3
  74. package/src/qingflow_mcp/tools/workflow_tools.py +48 -4
  75. package/src/qingflow_mcp/tools/workspace_tools.py +71 -3
  76. package/skills/qingflow-app-builder/references/build-complete-system.md +0 -428
  77. package/skills/qingflow-app-builder/references/build-single-app.md +0 -530
  78. package/skills/qingflow-mcp-setup/SKILL.md +0 -111
  79. package/skills/qingflow-mcp-setup/agents/openai.yaml +0 -4
  80. package/skills/qingflow-mcp-setup/references/claude-desktop.md +0 -34
  81. package/skills/qingflow-mcp-setup/references/environments.md +0 -61
  82. package/skills/qingflow-mcp-setup/references/generic-stdio.md +0 -31
  83. package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +0 -38
  84. package/skills/qingflow-workflow-builder/SKILL.md +0 -96
  85. package/skills/qingflow-workflow-builder/manifest.yaml +0 -8
  86. package/skills/qingflow-workflow-builder/references/01-overview.md +0 -45
  87. package/skills/qingflow-workflow-builder/references/02-update-mode.md +0 -53
  88. package/skills/qingflow-workflow-builder/references/03-flow-patterns.md +0 -57
  89. package/skills/qingflow-workflow-builder/references/04-stage1-business-modeling.md +0 -131
  90. package/skills/qingflow-workflow-builder/references/05-stage2-members-roles.md +0 -29
  91. package/skills/qingflow-workflow-builder/references/06-stage3-build-spec.md +0 -165
  92. package/skills/qingflow-workflow-builder/references/07-stage4-validate-spec.md +0 -33
  93. package/skills/qingflow-workflow-builder/references/08-stage5-apply-verify.md +0 -51
  94. package/skills/qingflow-workflow-builder/references/09-stage6-summary.md +0 -88
  95. package/skills/qingflow-workflow-builder/references/10-node-config-reference.md +0 -93
  96. package/skills/qingflow-workflow-builder/references/11-troubleshooting.md +0 -15
  97. package/skills/qingflow-workflow-builder/scripts/diff_flow_spec.py +0 -275
  98. package/skills/qingflow-workflow-builder/scripts/validate_flow_spec.py +0 -605
@@ -28,17 +28,18 @@ Canonical rules before any example:
28
28
  - Always use `columns`
29
29
  - Do not emit `column_names`
30
30
  - Treat `fields` only as a legacy alias the MCP may normalize, not as the preferred shape
31
- - Use `filters` with canonical keys `field_name`, `operator`, `value`/`values`
31
+ - Use `filters` with the unified fixed-filter DSL: `field_name`, `operator`, `value`/`values`; do not write raw `judgeType` / `judgeValues`
32
32
  - Use `query_conditions` for the frontend query panel. Do not put query-panel fields into `filters`.
33
- - When creating any new table or card view, always include `query_conditions` with `enabled: true`. In `rows`, list the fields users would most commonly search by — typically the data-title field, any status/type select fields, date fields, and member fields. Do not omit `query_conditions` or leave `rows` empty on a new view.
33
+ - Do not create views named `全部数据`, `我的数据`, `我发起的`, `待办`, `已办`, or `抄送`. These are built-in default/system views. New views must use business-specific names such as `订单台账视图`, `客户跟进视图`, or `逾期任务看板`.
34
+ - To change a built-in default/system view, first read the existing raw `view_key` from `app_get.views` or `app_get_views`, then use `patch_views` or a full `upsert_views` item with that `view_key`.
34
35
  - New views created by `app_views_apply` default the frontend associated report/view area to visible with `limit_type="all"`. Existing views preserve their current associated-resource display unless `associated_resources` is explicitly patched.
35
- - Use `app_associated_resources_apply` for the associated report/view resource pool, selected resources, and match rules. `associated_item_id` is the internal associated-resource id from `app_get.associated_resources`; `view_configs`, remove, and reorder may also pass an existing resource's `chart_id`/`chart_key`/`view_key`, which the tool resolves to the internal id. 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.
36
+ - Use `app_associated_resources_apply` for the associated report/view resource pool, selected resources, and match rules. Permission is split like the backend: resource pool writes use EditAppAuth, while `view_configs` uses the view config/DataManageAuth path. `associated_item_id` is the internal associated-resource id from `app_get.associated_resources`; `view_configs`, remove, and reorder may also pass an existing resource's `chart_id`/`chart_key`/`view_key`, which the tool resolves to the internal id. Do not write backend raw `sourceType`; reports default to BI app reports, and dataset reports use `report_source="dataset"`. Use `match_mappings` with `target_field + operator + source_field/value` for associated view/report filters; read `match-rules.md` if field type compatibility is unclear.
36
37
  - For gantt, use `start_field`, `end_field`, and optionally `title_field`
37
38
  - If `app_get.views` or `app_get_views` shows duplicate view names, include `view_key` in `upsert_views[]` and update that exact target
38
39
  - 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`.
39
40
  - 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.
40
41
 
41
- Apply a default table view:
42
+ Apply a business table view:
42
43
 
43
44
  ```json
44
45
  {
@@ -49,16 +50,10 @@ Apply a default table view:
49
50
  "publish": true,
50
51
  "upsert_views": [
51
52
  {
52
- "name": "全部订单",
53
+ "name": "订单台账视图",
53
54
  "view_key": "VIEW_KEY_IF_DUPLICATE_NAMES_EXIST",
54
55
  "type": "table",
55
- "columns": ["订单编号", "客户名称", "订单金额", "状态", "负责人", "创建时间"],
56
- "query_conditions": {
57
- "enabled": true,
58
- "exact": false,
59
- "hide_before_query": false,
60
- "rows": [["订单编号", "客户名称"], ["状态", "负责人"], ["创建时间"]]
61
- }
56
+ "columns": ["订单编号", "客户名称", "订单金额", "状态"]
62
57
  }
63
58
  ],
64
59
  "remove_views": []
@@ -277,7 +272,32 @@ At least one `query_conditions.rows` field does not exist on the app.
277
272
 
278
273
  ### `INVALID_QUERY_CONDITION_FIELD`
279
274
 
280
- 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.
275
+ The field exists but cannot be used in the frontend query-condition panel.
276
+
277
+ Supported `query_conditions.rows` fields are query-panel fields:
278
+
279
+ - text / long text
280
+ - number / amount
281
+ - date / datetime
282
+ - single select / multi select
283
+ - member / department
284
+ - phone / email
285
+ - boolean
286
+
287
+ Do not use these fields in `query_conditions`:
288
+
289
+ - relation
290
+ - attachment
291
+ - subtable or subtable subfield
292
+ - address/location
293
+ - Q-Linker
294
+ - code block
295
+
296
+ Fix path:
297
+
298
+ - If you wanted a saved filter that opens with the view, use `filters`.
299
+ - If you wanted a related report/view to match the current record, use `app_associated_resources_apply.match_mappings`.
300
+ - If you only need frontend search fields, remove unsupported fields from `query_conditions.rows` and keep query-panel supported fields only.
281
301
 
282
302
  ## Notes
283
303
 
@@ -286,6 +306,6 @@ The field exists but cannot be used in the frontend query-condition panel, such
286
306
  - `app_get_views` should be treated as canonical readback and now returns `columns`
287
307
  - If `app_views_apply` returns `AMBIGUOUS_VIEW`, stop and re-run `app_get_views`; then retry with the exact `view_key`
288
308
  - `filters` are ANDed together as one flat condition group
289
- - `query_conditions.rows` does not mean OR; it controls frontend query-field layout
309
+ - `query_conditions.rows` does not mean OR; it controls frontend query-field layout and should only contain query-panel supported fields
290
310
  - `app_views_apply` publishes by default
291
311
  - For select-style filters, success means the backend preserved the option value in readback, not just that the view name now exists
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+
11
+ VALID_PORTAL_STATUSES = {"verified", "unverified", "not_created"}
12
+ VALID_APP_VERIFY_STATUSES = {"verified", "unverified", "failed"}
13
+
14
+
15
+ def main(argv: list[str] | None = None) -> int:
16
+ parser = argparse.ArgumentParser(description="Validate a Qingflow complete-system delivery summary JSON file.")
17
+ parser.add_argument("summary_file", nargs="?", default="tmp/qingflow_system_build_summary.json")
18
+ args = parser.parse_args(argv)
19
+ path = Path(args.summary_file)
20
+ issues = validate_summary_file(path)
21
+ if issues:
22
+ _emit({"status": "failed", "error_code": "SYSTEM_BUILD_SUMMARY_INVALID", "path": str(path), "issues": issues})
23
+ return 1
24
+ payload = json.loads(path.read_text(encoding="utf-8"))
25
+ _emit(
26
+ {
27
+ "status": "success",
28
+ "path": str(path),
29
+ "summary": {
30
+ "package_id": payload.get("package_id"),
31
+ "package_name": payload.get("package_name"),
32
+ "portal_dash_key": payload.get("portal_dash_key"),
33
+ "portal_live_status": payload.get("portal_live_status"),
34
+ "app_count": len(payload.get("apps") or []),
35
+ "partial_count": len(payload.get("partial_items") or []),
36
+ "needs_followup_count": len(payload.get("needs_followup") or []),
37
+ },
38
+ }
39
+ )
40
+ return 0
41
+
42
+
43
+ def validate_summary_file(path: Path) -> list[dict[str, Any]]:
44
+ if not path.exists():
45
+ return [_issue("MISSING_FILE", "$", f"summary file does not exist: {path}")]
46
+ try:
47
+ payload = json.loads(path.read_text(encoding="utf-8"))
48
+ except OSError as exc:
49
+ return [_issue("READ_FAILED", "$", str(exc))]
50
+ except json.JSONDecodeError as exc:
51
+ return [_issue("INVALID_JSON", "$", exc.msg)]
52
+ if not isinstance(payload, dict):
53
+ return [_issue("INVALID_ROOT", "$", "summary root must be a JSON object")]
54
+
55
+ issues: list[dict[str, Any]] = []
56
+ _require_positive_int(payload, "package_id", issues)
57
+ _require_nonempty_string(payload, "package_name", issues)
58
+ _require_bool(payload, "front_end_visible", issues)
59
+ _require_string_enum(payload, "portal_live_status", VALID_PORTAL_STATUSES, issues)
60
+ if payload.get("portal_live_status") == "verified":
61
+ _require_nonempty_string(payload, "portal_dash_key", issues)
62
+ elif "portal_dash_key" in payload and payload.get("portal_dash_key") is not None and not isinstance(payload.get("portal_dash_key"), str):
63
+ issues.append(_issue("INVALID_TYPE", "$.portal_dash_key", "portal_dash_key must be a string when present"))
64
+
65
+ apps = payload.get("apps")
66
+ if not isinstance(apps, list) or not apps:
67
+ issues.append(_issue("INVALID_APPS", "$.apps", "apps must be a non-empty array"))
68
+ else:
69
+ for index, app in enumerate(apps):
70
+ prefix = f"$.apps[{index}]"
71
+ if not isinstance(app, dict):
72
+ issues.append(_issue("INVALID_APP_ITEM", prefix, "app item must be an object"))
73
+ continue
74
+ _require_nonempty_string(app, "app_key", issues, prefix)
75
+ _require_nonempty_string(app, "app_name", issues, prefix)
76
+ for key in ("fields_count", "views_count", "flows_count", "charts_count"):
77
+ _require_nonnegative_int(app, key, issues, prefix)
78
+ _require_string_enum(app, "publish_verify_status", VALID_APP_VERIFY_STATUSES, issues, prefix)
79
+
80
+ for key in ("warnings", "partial_items", "needs_followup"):
81
+ if not isinstance(payload.get(key), list):
82
+ issues.append(_issue("INVALID_TYPE", f"$.{key}", f"{key} must be an array"))
83
+ return issues
84
+
85
+
86
+ def _require_positive_int(payload: dict[str, Any], key: str, issues: list[dict[str, Any]], prefix: str = "$") -> None:
87
+ value = payload.get(key)
88
+ if isinstance(value, bool) or not isinstance(value, int) or value <= 0:
89
+ issues.append(_issue("INVALID_TYPE", f"{prefix}.{key}", f"{key} must be a positive integer"))
90
+
91
+
92
+ def _require_nonnegative_int(payload: dict[str, Any], key: str, issues: list[dict[str, Any]], prefix: str = "$") -> None:
93
+ value = payload.get(key)
94
+ if isinstance(value, bool) or not isinstance(value, int) or value < 0:
95
+ issues.append(_issue("INVALID_TYPE", f"{prefix}.{key}", f"{key} must be a non-negative integer"))
96
+
97
+
98
+ def _require_nonempty_string(payload: dict[str, Any], key: str, issues: list[dict[str, Any]], prefix: str = "$") -> None:
99
+ value = payload.get(key)
100
+ if not isinstance(value, str) or not value.strip():
101
+ issues.append(_issue("INVALID_TYPE", f"{prefix}.{key}", f"{key} must be a non-empty string"))
102
+
103
+
104
+ def _require_bool(payload: dict[str, Any], key: str, issues: list[dict[str, Any]], prefix: str = "$") -> None:
105
+ if not isinstance(payload.get(key), bool):
106
+ issues.append(_issue("INVALID_TYPE", f"{prefix}.{key}", f"{key} must be a boolean"))
107
+
108
+
109
+ def _require_string_enum(payload: dict[str, Any], key: str, allowed: set[str], issues: list[dict[str, Any]], prefix: str = "$") -> None:
110
+ value = payload.get(key)
111
+ if value not in allowed:
112
+ issues.append(_issue("INVALID_VALUE", f"{prefix}.{key}", f"{key} must be one of {sorted(allowed)}"))
113
+
114
+
115
+ def _issue(code: str, path: str, message: str) -> dict[str, Any]:
116
+ return {"code": code, "path": path, "message": message}
117
+
118
+
119
+ def _emit(payload: dict[str, Any]) -> None:
120
+ print(json.dumps(payload, ensure_ascii=False, indent=2))
121
+
122
+
123
+ if __name__ == "__main__":
124
+ sys.exit(main())
@@ -1,12 +1,14 @@
1
1
  ---
2
2
  name: qingflow-app-builder-code-integrations
3
- description: Configure Qingflow code block and Q-Linker fields through the builder surface when the task involves input field insertion, alias parsing, target field binding, or troubleshooting broken code/q-linker form configurations. Use after MCP is connected and authenticated, especially for CRM-style scoring, lookup, and enrichment forms.
3
+ description: Configure Qingflow code block and Q-Linker fields through the builder surface when the task involves input field insertion, alias parsing, target field binding, or troubleshooting broken code/q-linker form configurations. In Wingent Momo runtime, use the injected MCP session and recover auth/workspace only after a tool error.
4
4
  metadata:
5
5
  short-description: Configure Qingflow code blocks and Q-Linkers safely
6
6
  ---
7
7
 
8
8
  # Qingflow App Builder Code Integrations
9
9
 
10
+ > **Skill 版本**:`qingflow-skills-2026.06.24.2`(入口文档版本;如需确认 CLI 包版本,使用 `qingflow --version` 或 `qingflow --json version`)。
11
+
10
12
  Use this skill when the user wants to build or repair:
11
13
  - `code_block` fields
12
14
  - `q_linker` fields
@@ -58,7 +60,7 @@ For code block or Q-Linker work, use this order:
58
60
 
59
61
  1. Resolve the app and read fields:
60
62
  - `app_resolve`
61
- - `app_read_fields`
63
+ - `app_get_fields`
62
64
  2. Confirm the target field set already exists.
63
65
  3. Apply schema updates with `app_schema_apply`.
64
66
  4. Read fields again and verify:
@@ -115,7 +117,7 @@ Hard rules:
115
117
 
116
118
  After each code block or Q-Linker change, verify all of these:
117
119
 
118
- - `app_read_fields` shows the intended field type
120
+ - `app_get_fields` shows the intended field type
119
121
  - the high-level config is readable and stable
120
122
  - target fields still have valid types
121
123
  - insert schema can be opened
@@ -59,7 +59,7 @@ Do not bind code block outputs to:
59
59
 
60
60
  ## Safe Verification
61
61
 
62
- - `app_read_fields`
62
+ - `app_get_fields`
63
63
  - `record_insert_schema_get`
64
64
  - optional `record_code_block_run`
65
65
 
@@ -70,7 +70,7 @@ Avoid:
70
70
 
71
71
  ## Safe Verification
72
72
 
73
- - `app_read_fields`
73
+ - `app_get_fields`
74
74
  - `record_insert_schema_get`
75
75
  - `record_insert`
76
76
 
@@ -5,7 +5,7 @@ from pathlib import Path
5
5
 
6
6
  __all__ = ["__version__"]
7
7
 
8
- _FALLBACK_VERSION = "0.2.0b1099"
8
+ _FALLBACK_VERSION = "0.2.0b1017"
9
9
 
10
10
 
11
11
  def _resolve_local_pyproject_version() -> str | None:
@@ -1,5 +1,9 @@
1
- """Entry point for running qingflow_mcp as a module."""
2
- from qingflow_mcp.server import main
1
+ """Entry point for running qingflow_mcp as a module.
2
+
3
+ Default to the curated user-facing MCP surface. Split package and console
4
+ entrypoints can still start the builder server explicitly.
5
+ """
6
+ from qingflow_mcp.server_app_user import main
3
7
 
4
8
  if __name__ == "__main__":
5
9
  main()