@qingflow-tech/qingflow-app-builder-mcp 1.0.44 → 1.1.1

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 (40) hide show
  1. package/README.md +4 -2
  2. package/npm/bin/qingflow-app-builder-mcp.mjs +33 -2
  3. package/npm/lib/runtime.mjs +43 -2
  4. package/package.json +1 -1
  5. package/pyproject.toml +1 -1
  6. package/skills/qingflow-app-builder-code-integrations/SKILL.md +1 -1
  7. package/skills/qingflow-mcp-setup/SKILL.md +115 -0
  8. package/skills/qingflow-mcp-setup/agents/openai.yaml +4 -0
  9. package/skills/qingflow-mcp-setup/references/claude-desktop.md +34 -0
  10. package/skills/qingflow-mcp-setup/references/environments.md +62 -0
  11. package/skills/qingflow-mcp-setup/references/generic-stdio.md +32 -0
  12. package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +38 -0
  13. package/skills/qingflow-workflow-builder/SKILL.md +98 -0
  14. package/skills/qingflow-workflow-builder/manifest.yaml +8 -0
  15. package/skills/qingflow-workflow-builder/references/01-overview.md +45 -0
  16. package/skills/qingflow-workflow-builder/references/02-update-mode.md +53 -0
  17. package/skills/qingflow-workflow-builder/references/03-flow-patterns.md +57 -0
  18. package/skills/qingflow-workflow-builder/references/04-stage1-business-modeling.md +131 -0
  19. package/skills/qingflow-workflow-builder/references/05-stage2-members-roles.md +29 -0
  20. package/skills/qingflow-workflow-builder/references/06-stage3-build-spec.md +165 -0
  21. package/skills/qingflow-workflow-builder/references/07-stage4-validate-spec.md +33 -0
  22. package/skills/qingflow-workflow-builder/references/08-stage5-apply-verify.md +51 -0
  23. package/skills/qingflow-workflow-builder/references/09-stage6-summary.md +88 -0
  24. package/skills/qingflow-workflow-builder/references/10-node-config-reference.md +93 -0
  25. package/skills/qingflow-workflow-builder/references/11-troubleshooting.md +15 -0
  26. package/skills/qingflow-workflow-builder/scripts/diff_flow_spec.py +275 -0
  27. package/skills/qingflow-workflow-builder/scripts/validate_flow_spec.py +605 -0
  28. package/src/qingflow_mcp/__init__.py +1 -1
  29. package/src/qingflow_mcp/builder_facade/models.py +0 -39
  30. package/src/qingflow_mcp/builder_facade/service.py +262 -862
  31. package/src/qingflow_mcp/builder_facade/workflow_spec.py +111 -0
  32. package/src/qingflow_mcp/cli/commands/builder.py +44 -12
  33. package/src/qingflow_mcp/public_surface.py +2 -0
  34. package/src/qingflow_mcp/server_app_builder.py +16 -8
  35. package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
  36. package/src/qingflow_mcp/solution/executor.py +3 -133
  37. package/src/qingflow_mcp/tools/ai_builder_tools.py +92 -233
  38. package/src/qingflow_mcp/tools/solution_tools.py +30 -2
  39. package/src/qingflow_mcp/tools/workflow_tools.py +3 -31
  40. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
@@ -27,10 +27,6 @@ from ..builder_facade.models import (
27
27
  FieldPatch,
28
28
  FieldRemovePatch,
29
29
  FieldUpdatePatch,
30
- FlowPreset,
31
- FlowNodePatch,
32
- FlowPlanRequest,
33
- FlowTransitionPatch,
34
30
  LayoutApplyMode,
35
31
  LayoutPlanRequest,
36
32
  LayoutPreset,
@@ -70,7 +66,6 @@ from .qingbi_report_tools import QingbiReportTools
70
66
  from .role_tools import RoleTools
71
67
  from .solution_tools import SolutionTools
72
68
  from .view_tools import ViewTools
73
- from .workflow_tools import WorkflowTools
74
69
 
75
70
 
76
71
  def _normalize_builder_view_key(value: str) -> str:
@@ -80,7 +75,6 @@ def _normalize_builder_view_key(value: str) -> str:
80
75
  return raw
81
76
 
82
77
 
83
- PUBLIC_STABLE_FLOW_NODE_TYPES = ["start", "approve", "fill", "copy", "webhook", "end"]
84
78
  BUILDER_APPLY_SCHEMA_VERSION = "builder.apply.v1"
85
79
  BUILDER_APPLY_TOOL_NAMES = {
86
80
  "package_apply",
@@ -115,7 +109,6 @@ class AiBuilderTools(ToolBase):
115
109
  buttons=CustomButtonTools(sessions, backend),
116
110
  packages=PackageTools(sessions, backend),
117
111
  views=ViewTools(sessions, backend),
118
- workflows=WorkflowTools(sessions, backend),
119
112
  portals=PortalTools(sessions, backend),
120
113
  charts=QingbiReportTools(sessions, backend),
121
114
  roles=RoleTools(sessions, backend),
@@ -455,22 +448,34 @@ class AiBuilderTools(ToolBase):
455
448
  ) -> JSONObject:
456
449
  return self.app_layout_apply(profile=profile, app_key=app_key, mode=mode, publish=publish, sections=sections or [])
457
450
 
451
+ @mcp.tool()
452
+ def app_flow_get(
453
+ profile: str = DEFAULT_PROFILE,
454
+ app_key: str = "",
455
+ version_id: str = "",
456
+ ) -> JSONObject:
457
+ return self.app_get_flow(profile=profile, app_key=app_key, version_id=version_id or None)
458
+
459
+ @mcp.tool()
460
+ def app_flow_get_schema(profile: str = DEFAULT_PROFILE, schema_version: str = "") -> JSONObject:
461
+ return self.app_flow_get_schema(profile=profile, schema_version=schema_version or None)
462
+
458
463
  @mcp.tool()
459
464
  def app_flow_apply(
460
465
  profile: str = DEFAULT_PROFILE,
461
466
  app_key: str = "",
462
- mode: str = "replace",
463
467
  publish: bool = True,
464
- nodes: list[JSONObject] | None = None,
465
- transitions: list[JSONObject] | None = None,
468
+ spec: JSONObject | None = None,
469
+ idempotency_key: str = "",
470
+ schema_version: str = "",
466
471
  ) -> JSONObject:
467
472
  return self.app_flow_apply(
468
473
  profile=profile,
469
474
  app_key=app_key,
470
- mode=mode,
471
475
  publish=publish,
472
- nodes=nodes or [],
473
- transitions=transitions or [],
476
+ spec=spec or {},
477
+ idempotency_key=idempotency_key or None,
478
+ schema_version=schema_version or None,
474
479
  )
475
480
 
476
481
  @mcp.tool()
@@ -1370,26 +1375,25 @@ class AiBuilderTools(ToolBase):
1370
1375
  suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
1371
1376
  )
1372
1377
 
1373
- @tool_cn_name("应用流程摘要读取")
1374
- def app_read_flow_summary(self, *, profile: str, app_key: str) -> JSONObject:
1375
- """执行应用相关逻辑。"""
1376
- normalized_args = {"app_key": app_key}
1378
+ @tool_cn_name("Workflow Spec Schema")
1379
+ def app_flow_get_schema(self, *, profile: str, schema_version: str | None = None) -> JSONObject:
1380
+ normalized_args = {"schema_version": schema_version}
1377
1381
  return _safe_tool_call(
1378
- lambda: self._facade.app_read_flow_summary(profile=profile, app_key=app_key),
1379
- error_code="FLOW_READ_FAILED",
1382
+ lambda: self._facade.flow_get_schema(profile=profile, schema_version=schema_version),
1383
+ error_code="FLOW_SPEC_SCHEMA_FAILED",
1380
1384
  normalized_args=normalized_args,
1381
- suggested_next_call={"tool_name": "app_get_flow", "arguments": {"profile": profile, "app_key": app_key}},
1385
+ suggested_next_call={"tool_name": "app_flow_get_schema", "arguments": {"profile": profile, **normalized_args}},
1382
1386
  )
1383
1387
 
1384
- @tool_cn_name("应用流程详情查询")
1385
- def app_get_flow(self, *, profile: str, app_key: str) -> JSONObject:
1388
+ @tool_cn_name("Workflow Spec 读取")
1389
+ def app_get_flow(self, *, profile: str, app_key: str, version_id: str | None = None) -> JSONObject:
1386
1390
  """执行应用相关逻辑。"""
1387
- normalized_args = {"app_key": app_key}
1391
+ normalized_args = {"app_key": app_key, "version_id": version_id}
1388
1392
  return _safe_tool_call(
1389
- lambda: self._facade.app_get_flow(profile=profile, app_key=app_key),
1393
+ lambda: self._facade.flow_get(profile=profile, app_key=app_key, version_id=version_id),
1390
1394
  error_code="APP_GET_FLOW_FAILED",
1391
1395
  normalized_args=normalized_args,
1392
- suggested_next_call={"tool_name": "app_get_flow", "arguments": {"profile": profile, "app_key": app_key}},
1396
+ suggested_next_call={"tool_name": "app_flow_get", "arguments": {"profile": profile, "app_key": app_key}},
1393
1397
  )
1394
1398
 
1395
1399
  @tool_cn_name("应用图表摘要读取")
@@ -1600,52 +1604,6 @@ class AiBuilderTools(ToolBase):
1600
1604
  suggested_next_call={"tool_name": "app_layout_plan", "arguments": {"profile": profile, **normalized_request}},
1601
1605
  )
1602
1606
 
1603
- @tool_cn_name("应用流程规划")
1604
- def app_flow_plan(
1605
- self,
1606
- *,
1607
- profile: str,
1608
- app_key: str,
1609
- mode: str = "replace",
1610
- nodes: list[JSONObject] | None = None,
1611
- transitions: list[JSONObject] | None = None,
1612
- preset: str | None = None,
1613
- ) -> JSONObject:
1614
- """执行应用相关逻辑。"""
1615
- try:
1616
- request = FlowPlanRequest.model_validate(
1617
- {
1618
- "app_key": app_key,
1619
- "mode": mode,
1620
- "nodes": nodes or [],
1621
- "transitions": transitions or [],
1622
- "preset": preset,
1623
- }
1624
- )
1625
- except ValidationError as exc:
1626
- return _validation_failure(
1627
- str(exc),
1628
- tool_name="app_flow_plan",
1629
- exc=exc,
1630
- suggested_next_call={
1631
- "tool_name": "app_flow_plan",
1632
- "arguments": {
1633
- "profile": profile,
1634
- "app_key": app_key,
1635
- "mode": "replace",
1636
- "preset": "basic_approval",
1637
- "nodes": [],
1638
- "transitions": [],
1639
- },
1640
- },
1641
- )
1642
- return _safe_tool_call(
1643
- lambda: self._facade.app_flow_plan(profile=profile, request=request),
1644
- error_code="FLOW_PLAN_FAILED",
1645
- normalized_args=request.model_dump(mode="json"),
1646
- suggested_next_call={"tool_name": "app_flow_plan", "arguments": {"profile": profile, **request.model_dump(mode="json")}},
1647
- )
1648
-
1649
1607
  @tool_cn_name("应用视图规划")
1650
1608
  def app_views_plan(
1651
1609
  self,
@@ -2376,111 +2334,51 @@ class AiBuilderTools(ToolBase):
2376
2334
  *,
2377
2335
  profile: str,
2378
2336
  app_key: str,
2379
- mode: str = "replace",
2337
+ spec: JSONObject,
2380
2338
  publish: bool = True,
2381
- nodes: list[JSONObject],
2382
- transitions: list[JSONObject],
2339
+ idempotency_key: str | None = None,
2340
+ schema_version: str | None = None,
2383
2341
  ) -> JSONObject:
2384
2342
  """执行应用相关逻辑。"""
2385
- result = self._app_flow_apply_once(
2386
- profile=profile,
2387
- app_key=app_key,
2388
- mode=mode,
2389
- publish=publish,
2390
- nodes=nodes,
2391
- transitions=transitions,
2392
- )
2393
- result = self._retry_after_self_lock_release(
2394
- profile=profile,
2395
- result=result,
2396
- retry_call=lambda: self._app_flow_apply_once(
2397
- profile=profile,
2398
- app_key=app_key,
2399
- mode=mode,
2400
- publish=publish,
2401
- nodes=nodes,
2402
- transitions=transitions,
2403
- ),
2404
- )
2405
- return _attach_builder_apply_envelope("app_flow_apply", result)
2406
-
2407
- def _app_flow_apply_once(
2408
- self,
2409
- *,
2410
- profile: str,
2411
- app_key: str,
2412
- mode: str = "replace",
2413
- publish: bool = True,
2414
- nodes: list[JSONObject],
2415
- transitions: list[JSONObject],
2416
- ) -> JSONObject:
2417
- """执行内部辅助逻辑。"""
2418
- plan_result = self._rewrite_plan_result_for_apply(
2419
- result=self.app_flow_plan(
2420
- profile=profile,
2421
- app_key=app_key,
2422
- mode=mode,
2423
- nodes=nodes,
2424
- transitions=transitions,
2425
- preset=None,
2426
- ),
2427
- profile=profile,
2428
- publish=publish,
2429
- plan_tool_name="app_flow_plan",
2430
- apply_tool_name="app_flow_apply",
2431
- )
2432
- if not isinstance(plan_result, dict) or plan_result.get("status") != "success":
2433
- return plan_result
2434
- plan_args = plan_result.get("normalized_args")
2435
- if not isinstance(plan_args, dict):
2436
- plan_args = {}
2437
- try:
2438
- request = FlowPlanRequest.model_validate(
2439
- {
2440
- "app_key": plan_args.get("app_key") or app_key,
2441
- "mode": plan_args.get("mode") or mode,
2442
- "nodes": plan_args.get("nodes") or [],
2443
- "transitions": plan_args.get("transitions") or [],
2444
- "preset": None,
2445
- }
2446
- )
2447
- except ValidationError as exc:
2448
- return _validation_failure(
2449
- str(exc),
2343
+ if not isinstance(spec, dict) or not spec:
2344
+ return _config_failure(
2450
2345
  tool_name="app_flow_apply",
2451
- exc=exc,
2452
- suggested_next_call={
2453
- "tool_name": "app_flow_apply",
2454
- "arguments": {
2455
- "profile": profile,
2456
- "app_key": str(plan_args.get("app_key") or app_key),
2457
- "mode": str(plan_args.get("mode") or "replace"),
2458
- "publish": publish,
2459
- "nodes": plan_args.get("nodes") or [{"id": "start", "type": "start", "name": "发起"}],
2460
- "transitions": plan_args.get("transitions") or [],
2461
- },
2462
- },
2346
+ message="app_flow_apply requires a non-empty WorkflowSpecDTO `spec` object.",
2347
+ fix_hint="Call app_flow_get_schema, then app_flow_get for GET-first baseline, patch spec, and apply.",
2463
2348
  )
2464
2349
  normalized_args = {
2465
- "app_key": request.app_key,
2466
- "mode": request.mode,
2350
+ "app_key": app_key,
2467
2351
  "publish": publish,
2468
- "nodes": [node.model_dump(mode="json") for node in request.nodes],
2469
- "transitions": [transition.model_dump(mode="json", by_alias=True) for transition in request.transitions],
2352
+ "spec": spec,
2353
+ "idempotency_key": idempotency_key,
2354
+ "schema_version": schema_version,
2470
2355
  }
2471
- return _safe_tool_call(
2472
- lambda: self._facade.app_flow_apply(
2356
+ result = _safe_tool_call(
2357
+ lambda: self._facade.flow_apply(
2473
2358
  profile=profile,
2474
- app_key=request.app_key,
2475
- mode=request.mode,
2359
+ app_key=app_key,
2360
+ spec=spec,
2476
2361
  publish=publish,
2477
- nodes=[node.model_dump(mode="json") for node in request.nodes],
2478
- transitions=[transition.model_dump(mode="json", by_alias=True) for transition in request.transitions],
2362
+ idempotency_key=idempotency_key,
2363
+ schema_version=schema_version,
2479
2364
  ),
2480
2365
  error_code="FLOW_APPLY_FAILED",
2481
2366
  normalized_args=normalized_args,
2482
2367
  suggested_next_call={"tool_name": "app_flow_apply", "arguments": {"profile": profile, **normalized_args}},
2483
2368
  )
2369
+ result = self._retry_after_self_lock_release(
2370
+ profile=profile,
2371
+ result=result,
2372
+ retry_call=lambda: self._facade.flow_apply(
2373
+ profile=profile,
2374
+ app_key=app_key,
2375
+ spec=spec,
2376
+ publish=publish,
2377
+ idempotency_key=idempotency_key,
2378
+ schema_version=schema_version,
2379
+ ),
2380
+ )
2381
+ return _attach_builder_apply_envelope("app_flow_apply", result)
2484
2382
 
2485
2383
  @tool_cn_name("应用视图应用")
2486
2384
  def app_views_apply(
@@ -5805,94 +5703,56 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
5805
5703
  "sections": [{"type": "paragraph", "paragraph_id": "basic", "title": "基础信息", "rows": [["项目名称", "项目负责人", "项目阶段", "优先级"]]}],
5806
5704
  },
5807
5705
  },
5808
- "app_flow_plan": {
5809
- "allowed_keys": ["app_key", "mode", "nodes", "transitions", "preset"],
5810
- "aliases": {
5811
- "overwrite": "replace",
5812
- "base_preset": "preset",
5813
- "default_approval": "basic_approval",
5814
- "node.role_names": "node.assignees.role_names",
5815
- "node.role_ids": "node.assignees.role_ids",
5816
- "node.member_names": "node.assignees.member_names",
5817
- "node.member_emails": "node.assignees.member_emails",
5818
- "node.member_uids": "node.assignees.member_uids",
5819
- "node.editable_fields": "node.permissions.editable_fields",
5820
- "default_approval": "basic_approval",
5821
- },
5822
- "allowed_values": {
5823
- "mode": ["replace"],
5824
- "preset": [member.value for member in FlowPreset],
5825
- "node.type": PUBLIC_STABLE_FLOW_NODE_TYPES,
5826
- },
5827
- "dependency_hints": [
5828
- "approval-style workflows require an explicit business status select field before app_flow_apply, such as 状态 / 处理状态 / 审批状态 / 工单状态",
5829
- "approve/fill/copy nodes require at least one assignee",
5706
+ "app_flow_get_schema": {
5707
+ "allowed_keys": ["schema_version"],
5708
+ "aliases": {"schemaVersion": "schema_version"},
5709
+ "allowed_values": {},
5710
+ "execution_notes": [
5711
+ "returns WorkflowSpec JSON Schema from /workflow/spec/schema",
5712
+ "call this before authoring a new spec or validating spec shape",
5713
+ "worksheet-level approval deduplication toggles (legacy global settings) are not yet part of WorkflowSpec",
5830
5714
  ],
5715
+ "minimal_example": {
5716
+ "profile": "default",
5717
+ "schema_version": "vnext-2026-06",
5718
+ },
5719
+ },
5720
+ "app_flow_get": {
5721
+ "allowed_keys": ["app_key", "version_id"],
5722
+ "aliases": {"versionId": "version_id"},
5723
+ "allowed_values": {},
5831
5724
  "execution_notes": [
5832
- "public flow building is intentionally limited to linear workflows",
5833
- "branch and condition nodes are disabled because the backend workflow route is not front-end stable for these node types",
5834
- "do not create platform system workflow fields such as 当前流程状态 / 当前处理人 / 当前处理节点 / 流程标题; use an explicit business status field instead",
5725
+ "returns current WorkflowSpecDTO for one app via GET /workflow/spec",
5726
+ "use GET-first before patching and app_flow_apply",
5835
5727
  ],
5836
5728
  "minimal_example": {
5837
5729
  "profile": "default",
5838
5730
  "app_key": "APP_KEY",
5839
- "mode": "replace",
5840
- "preset": "basic_approval",
5841
- "nodes": [
5842
- {
5843
- "id": "approve_1",
5844
- "type": "approve",
5845
- "name": "部门审批",
5846
- "assignees": {"role_names": ["项目经理"]},
5847
- "permissions": {"editable_fields": ["状态", "审批意见"]},
5848
- }
5849
- ],
5850
- "transitions": [],
5851
5731
  },
5852
5732
  },
5853
5733
  "app_flow_apply": {
5854
- "allowed_keys": ["app_key", "mode", "publish", "nodes", "transitions"],
5734
+ "allowed_keys": ["app_key", "publish", "spec", "idempotency_key", "schema_version"],
5855
5735
  "aliases": {
5856
- "overwrite": "replace",
5857
- "node.role_names": "node.assignees.role_names",
5858
- "node.role_ids": "node.assignees.role_ids",
5859
- "node.member_names": "node.assignees.member_names",
5860
- "node.member_emails": "node.assignees.member_emails",
5861
- "node.member_uids": "node.assignees.member_uids",
5862
- "node.editable_fields": "node.permissions.editable_fields",
5863
- },
5864
- "allowed_values": {
5865
- "mode": ["replace"],
5866
- "node.type": PUBLIC_STABLE_FLOW_NODE_TYPES,
5736
+ "schemaVersion": "schema_version",
5737
+ "idempotencyKey": "idempotency_key",
5867
5738
  },
5739
+ "allowed_values": {},
5868
5740
  "dependency_hints": [
5869
- "approval-style workflows require an explicit business status select field before app_flow_apply, such as 状态 / 处理状态 / 审批状态 / 工单状态",
5870
- "approve/fill/copy nodes require at least one assignee",
5741
+ "spec must be a complete WorkflowSpecDTO object (replace-only apply)",
5871
5742
  ],
5872
5743
  "execution_notes": [
5873
- "public flow building is intentionally limited to linear workflows",
5874
- "app_flow_apply is replace-only; do not treat node snippets as partial patches",
5875
- "branch and condition nodes are disabled because the backend workflow route is not front-end stable for these node types",
5876
- "do not create platform system workflow fields such as 当前流程状态 / 当前处理人 / 当前处理节点 / 流程标题; use an explicit business status field instead",
5877
- "workflow verification only covers linear node structure in the public tool surface",
5744
+ "posts to /workflow/spec:apply with appKey, idempotencyKey, schemaVersion, and spec",
5745
+ "verification uses appliedSpec, diffSummary, and semanticLint from the apply response",
5746
+ "publish=false keeps changes in draft when supported by the backend",
5878
5747
  ],
5879
5748
  "minimal_example": {
5880
5749
  "profile": "default",
5881
5750
  "app_key": "APP_KEY",
5882
- "mode": "replace",
5883
5751
  "publish": True,
5884
- "nodes": [
5885
- {"id": "start", "type": "start", "name": "发起"},
5886
- {
5887
- "id": "approve_1",
5888
- "type": "approve",
5889
- "name": "部门审批",
5890
- "assignees": {"role_names": ["项目经理"]},
5891
- "permissions": {"editable_fields": ["状态", "审批意见"]},
5892
- },
5893
- {"id": "end", "type": "end", "name": "结束"},
5894
- ],
5895
- "transitions": [{"from": "start", "to": "approve_1"}, {"from": "approve_1", "to": "end"}],
5752
+ "spec": {
5753
+ "nodes": [{"id": "n1", "type": "APPLICANT", "name": "发起"}],
5754
+ "transitions": [],
5755
+ },
5896
5756
  },
5897
5757
  },
5898
5758
  "app_views_plan": {
@@ -6242,8 +6102,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
6242
6102
  "aliases": {},
6243
6103
  "allowed_values": {},
6244
6104
  "execution_notes": [
6245
- "returns workflow configuration summary for one app",
6246
- "use this before app_flow_apply when you need the current node structure",
6105
+ "returns WorkflowSpecDTO for one app (alias of app_flow_get)",
6106
+ "use app_flow_get_schema then app_flow_get before app_flow_apply",
6247
6107
  ],
6248
6108
  "minimal_example": {
6249
6109
  "profile": "default",
@@ -6549,7 +6409,6 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
6549
6409
  _PRIVATE_BUILDER_TOOL_CONTRACTS = {
6550
6410
  "app_schema_plan",
6551
6411
  "app_layout_plan",
6552
- "app_flow_plan",
6553
6412
  "app_views_plan",
6554
6413
  }
6555
6414
 
@@ -38,7 +38,6 @@ from .qingbi_report_tools import QingbiReportTools
38
38
  from .record_tools import RecordTools
39
39
  from .role_tools import RoleTools
40
40
  from .view_tools import ViewTools
41
- from .workflow_tools import WorkflowTools
42
41
  from .workspace_tools import WorkspaceTools
43
42
 
44
43
  STAGED_BUILD_MODES = {"preflight", "plan", "apply", "repair"}
@@ -737,6 +736,36 @@ class SolutionTools(ToolBase):
737
736
  ) -> dict[str, Any]:
738
737
  """执行方案相关逻辑。"""
739
738
  mode = _normalize_staged_build_mode(mode)
739
+ app_key = str(flow_spec.get("app_key") or "").strip()
740
+ spec_payload = flow_spec.get("spec")
741
+ if app_key and isinstance(spec_payload, dict) and spec_payload:
742
+ from .ai_builder_tools import AiBuilderTools
743
+
744
+ builder = AiBuilderTools(self.sessions, self.backend)
745
+ if mode in {"preflight", "plan"}:
746
+ return {
747
+ "build_id": build_id or _generate_build_id(run_label=run_label, stage_name="flow"),
748
+ "mode": mode,
749
+ "stage": "flow",
750
+ "status": "planned" if mode == "plan" else "preflighted",
751
+ "normalized_args": {"app_key": app_key, "spec": spec_payload},
752
+ "tool_name": "solution_build_flow",
753
+ }
754
+ result = builder.app_flow_apply(
755
+ profile=profile,
756
+ app_key=app_key,
757
+ spec=spec_payload,
758
+ publish=publish,
759
+ schema_version=flow_spec.get("schema_version") or flow_spec.get("schemaVersion"),
760
+ )
761
+ return {
762
+ "build_id": build_id,
763
+ "mode": mode,
764
+ "stage": "flow",
765
+ "status": result.get("status"),
766
+ "result": result,
767
+ "tool_name": "solution_build_flow",
768
+ }
740
769
  return self._stage_build(
741
770
  profile=profile,
742
771
  mode=mode,
@@ -1409,7 +1438,6 @@ class SolutionTools(ToolBase):
1409
1438
  role_tools=RoleTools(self.sessions, self.backend),
1410
1439
  app_tools=AppTools(self.sessions, self.backend),
1411
1440
  record_tools=RecordTools(self.sessions, self.backend),
1412
- workflow_tools=WorkflowTools(self.sessions, self.backend),
1413
1441
  view_tools=ViewTools(self.sessions, self.backend),
1414
1442
  chart_tools=QingbiReportTools(self.sessions, self.backend),
1415
1443
  portal_tools=PortalTools(self.sessions, self.backend),
@@ -14,25 +14,13 @@ class WorkflowTools(ToolBase):
14
14
 
15
15
  类型:流程配置工具。
16
16
  主要职责:
17
- 1. 查询流程节点与流程图配置;
18
- 2. 读取与更新流程规则;
19
- 3. 支持流程发布与流程调试辅助能力。
17
+ 1. 支持流程运行时校验与调试辅助能力;
18
+ 2. 保留 legacy 节点写路径供内部/测试直接调用(非 MCP 公开)。
19
+ 设计期流程配置读取统一走 WorkflowSpec(app_flow_get / GET /api/workflow/spec)。
20
20
  """
21
21
 
22
22
  def register(self, mcp: FastMCP) -> None:
23
23
  """注册当前工具到 MCP 服务。"""
24
- @mcp.tool()
25
- def workflow_list_nodes(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
26
- return self.workflow_list_nodes(profile=profile, app_key=app_key)
27
-
28
- @mcp.tool()
29
- def workflow_get_node_detail(profile: str = DEFAULT_PROFILE, app_key: str = "", audit_node_id: int = 0) -> JSONObject:
30
- return self.workflow_get_node_detail(profile=profile, app_key=app_key, audit_node_id=audit_node_id)
31
-
32
- @mcp.tool()
33
- def workflow_get_global_settings(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
34
- return self.workflow_get_global_settings(profile=profile, app_key=app_key)
35
-
36
24
  @mcp.tool()
37
25
  def workflow_get_future_nodes(profile: str = DEFAULT_PROFILE, app_key: str = "", apply_id: int = 0) -> JSONObject:
38
26
  return self.workflow_get_future_nodes(profile=profile, app_key=app_key, apply_id=apply_id)
@@ -53,22 +41,6 @@ class WorkflowTools(ToolBase):
53
41
  audit_node_id=audit_node_id,
54
42
  )
55
43
 
56
- @mcp.tool()
57
- def workflow_get_qsource_active(profile: str = DEFAULT_PROFILE, app_key: str = "", qsource_id: int = 0) -> JSONObject:
58
- return self.workflow_get_qsource_active(profile=profile, app_key=app_key, qsource_id=qsource_id)
59
-
60
- @mcp.tool()
61
- def workflow_get_qsource_passive(profile: str = DEFAULT_PROFILE, app_key: str = "", qsource_id: int = 0) -> JSONObject:
62
- return self.workflow_get_qsource_passive(profile=profile, app_key=app_key, qsource_id=qsource_id)
63
-
64
- @mcp.tool()
65
- def workflow_get_editable_question_ids(profile: str = DEFAULT_PROFILE, app_key: str = "", audit_node_id: int = 0) -> JSONObject:
66
- return self.workflow_get_editable_question_ids(profile=profile, app_key=app_key, audit_node_id=audit_node_id)
67
-
68
- @mcp.tool()
69
- def workflow_get_print_nodes(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
70
- return self.workflow_get_print_nodes(profile=profile, app_key=app_key)
71
-
72
44
  @tool_cn_name("流程节点列表")
73
45
  def workflow_list_nodes(self, *, profile: str, app_key: str) -> JSONObject:
74
46
  """执行流程相关逻辑。"""