@qingflow-tech/qingflow-cli 1.1.61-beta.5 → 1.1.61

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 (85) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/pyproject.toml +1 -2
  4. package/skills/qingflow-cli/SKILL.md +2 -104
  5. package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +2 -2
  6. package/skills/qingflow-cli/reference/builder/30-schema-fields.md +5 -22
  7. package/skills/qingflow-cli/reference/builder/70-portal.md +16 -37
  8. package/skills/qingflow-cli/reference/builder/reference/app-delivery-sop.md +3 -3
  9. package/skills/qingflow-cli/reference/examples/portal/portal_sections_all_types.example.json +7 -6
  10. package/src/qingflow_mcp/app_form_compat.py +0 -3
  11. package/src/qingflow_mcp/app_form_validator.py +20 -89
  12. package/src/qingflow_mcp/builder_facade/app_form.py +112 -629
  13. package/src/qingflow_mcp/builder_facade/models.py +13 -164
  14. package/src/qingflow_mcp/builder_facade/schemas/app_form/manifest.json +1 -15
  15. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/complete.json +38 -112
  16. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/address.json +8 -31
  17. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/amount.json +10 -6
  18. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/attachment.json +10 -33
  19. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/code_block.json +10 -6
  20. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/data_relation.json +18 -11
  21. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/date.json +8 -4
  22. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/datetime.json +8 -4
  23. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/department.json +10 -6
  24. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/email.json +8 -4
  25. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/link.json +8 -4
  26. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/long_text.json +8 -4
  27. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/member.json +10 -6
  28. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/multi_select.json +8 -4
  29. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/number.json +10 -6
  30. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/phone.json +8 -4
  31. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/q_linker.json +12 -8
  32. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/relation.json +15 -73
  33. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_choice.json +8 -4
  34. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_select.json +8 -4
  35. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/subtable.json +29 -90
  36. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/text.json +8 -4
  37. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/formula.json +0 -43
  38. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/summary.json +19 -27
  39. package/src/qingflow_mcp/builder_facade/service.py +72 -1143
  40. package/src/qingflow_mcp/cli/commands/builder.py +5 -114
  41. package/src/qingflow_mcp/cli/main.py +1 -34
  42. package/src/qingflow_mcp/cloud_context.py +1 -16
  43. package/src/qingflow_mcp/public_surface.py +8 -69
  44. package/src/qingflow_mcp/response_trim.py +1 -2
  45. package/src/qingflow_mcp/server.py +6 -12
  46. package/src/qingflow_mcp/server_app_builder.py +6 -41
  47. package/src/qingflow_mcp/server_app_user.py +5 -9
  48. package/src/qingflow_mcp/server_cloud.py +7 -43
  49. package/src/qingflow_mcp/solution/compiler/icon_utils.py +0 -73
  50. package/src/qingflow_mcp/solution/executor.py +2 -5
  51. package/src/qingflow_mcp/tools/ai_builder_tools.py +12 -110
  52. package/src/qingflow_mcp/tools/portal_tools.py +54 -4
  53. package/src/qingflow_mcp/tools/solution_tools.py +0 -2
  54. package/skills/qingflow-cli/reference/examples/portal/portal_apply_v1alpha1.example.json +0 -100
  55. package/src/qingflow_mcp/builder_facade/portals_schema.py +0 -397
  56. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_content_classification.json +0 -146
  57. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_content_extraction.json +0 -146
  58. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_content_generation.json +0 -146
  59. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_document_parsing.json +0 -146
  60. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_image_generation.json +0 -146
  61. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_image_recognition.json +0 -146
  62. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_machine_translation.json +0 -146
  63. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_text_summary.json +0 -146
  64. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/date_duration.json +0 -146
  65. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/embed_field.json +0 -146
  66. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/image_select.json +0 -146
  67. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/location.json +0 -146
  68. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ocr.json +0 -146
  69. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/rich_text.json +0 -146
  70. package/src/qingflow_mcp/builder_facade/schemas/portals/manifest.json +0 -59
  71. package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/complete.json +0 -1397
  72. package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/carousel.json +0 -193
  73. package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/chart.json +0 -643
  74. package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/favourite.json +0 -203
  75. package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/filter.json +0 -268
  76. package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/grid.json +0 -265
  77. package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/link.json +0 -131
  78. package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/task.json +0 -199
  79. package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/text.json +0 -123
  80. package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/view.json +0 -196
  81. package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/summary.json +0 -800
  82. package/src/qingflow_mcp/tool_catalog.py +0 -387
  83. package/src/qingflow_mcp/tool_catalog_i18n.py +0 -185
  84. package/src/qingflow_mcp/tool_policy.py +0 -197
  85. package/src/qingflow_mcp/tools/portal_writer.py +0 -59
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import argparse
4
- import sys
5
4
  from copy import deepcopy
6
5
  from pathlib import Path
7
6
 
@@ -19,7 +18,6 @@ from ...app_form_validator import (
19
18
  validate_declaration,
20
19
  validate_formula_expression,
21
20
  )
22
- from ...builder_facade.portals_schema import PORTALS_SUPPORTED_SCHEMA_KINDS
23
21
  from ...builder_facade.views_schema import VIEWS_SUPPORTED_SCHEMA_KINDS
24
22
  from ..context import CliContext
25
23
  from ..json_io import load_json_value
@@ -262,39 +260,17 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
262
260
  portal_list = portal_subparsers.add_parser("list", help="列出可配置门户")
263
261
  portal_list.set_defaults(handler=_handle_portal_list, format_hint="builder_summary")
264
262
 
265
- portal_schema = portal_subparsers.add_parser("schema", help="读取门户声明 Schema")
266
- portal_schema.add_argument("--schema-version")
267
- portal_schema.add_argument(
268
- "--schema-kind",
269
- choices=PORTALS_SUPPORTED_SCHEMA_KINDS,
270
- )
271
- portal_schema.set_defaults(handler=_handle_portal_schema, format_hint="builder_summary", force_json_output=True)
272
-
273
- portal_get = portal_subparsers.add_parser("get", help="读取门户声明或详情")
263
+ portal_get = portal_subparsers.add_parser("get", help="读取门户详情")
274
264
  portal_get.add_argument("--dash-key", required=True)
275
265
  portal_get.add_argument("--being-draft", action=argparse.BooleanOptionalAction, default=True)
276
- portal_get.add_argument(
277
- "--schema-kind",
278
- choices=("summary", "complete", "raw"),
279
- default="summary",
280
- help="summary/complete=canonical 声明投影(重放提取锦点);raw=原始归一响应",
281
- )
282
- portal_get.add_argument("--schema-version")
283
266
  portal_get.set_defaults(handler=_handle_portal_get, format_hint="builder_summary")
284
267
 
285
- portal_validate = portal_subparsers.add_parser("validate", help="只校验门户声明,不访问后端")
286
- portal_validate.add_argument("--file", required=True, help="门户声明 JSON 文件,- 表示 stdin")
287
- portal_validate.add_argument("--schema-version", help="固定使用的门户 Schema 版本;缺省读文件内 schema_version,再缺省 latest")
288
- portal_validate.set_defaults(handler=_handle_portal_validate, format_hint="builder_summary", force_json_output=True)
289
-
290
268
  portal_apply = portal_subparsers.add_parser("apply", help="更新门户")
291
- portal_apply.add_argument("--file", help="声明文件主形态(完整 portal 声明 JSON,支持 - stdin);与 patch 参数互斥")
292
- portal_apply.add_argument("--schema-version", help="声明使用的 Schema 版本;缺省读文件内 schema_version,再缺省 latest")
293
269
  portal_apply.add_argument("--dash-key", default="")
294
270
  portal_apply.add_argument("--dash-name", default="")
295
271
  portal_apply.add_argument("--package-id", type=int)
296
- portal_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=None, help="发布开关;--file 声明模式下缺省尊重声明内 publish 字段(再缺省 true),显式传参时覆盖声明值")
297
- portal_apply.add_argument("--payload-file", help="兼容:完整门户 payload JSON(裸 DTO 透传);新代码改用 --file")
272
+ portal_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
273
+ portal_apply.add_argument("--payload-file")
298
274
  portal_apply.add_argument("--sections-file")
299
275
  portal_apply.add_argument("--layout-preset", default="")
300
276
  portal_apply.add_argument("--visibility-file")
@@ -1005,36 +981,8 @@ def _handle_portal_list(args: argparse.Namespace, context: CliContext) -> dict:
1005
981
  return context.builder.portal_list(profile=args.profile)
1006
982
 
1007
983
 
1008
- def _handle_portal_schema(args: argparse.Namespace, context: CliContext) -> dict:
1009
- return context.builder.portal_schema(
1010
- schema_version=getattr(args, "schema_version", None),
1011
- schema_kind=getattr(args, "schema_kind", None),
1012
- )
1013
-
1014
-
1015
- def _handle_portal_validate(args: argparse.Namespace, context: CliContext) -> dict:
1016
- payload = load_json_value(
1017
- args.file,
1018
- option_name="--file",
1019
- max_bytes=2 * 1024 * 1024,
1020
- max_depth=64,
1021
- )
1022
- if not isinstance(payload, dict):
1023
- raise_config_error("builder portal validate --file must contain a JSON object")
1024
- return context.builder.portal_validate(
1025
- declaration=payload,
1026
- schema_version=getattr(args, "schema_version", None),
1027
- )
1028
-
1029
-
1030
984
  def _handle_portal_get(args: argparse.Namespace, context: CliContext) -> dict:
1031
- return context.builder.portal_get(
1032
- profile=args.profile,
1033
- dash_key=args.dash_key,
1034
- being_draft=bool(args.being_draft),
1035
- schema_kind=getattr(args, "schema_kind", "summary") or "summary",
1036
- schema_version=getattr(args, "schema_version", None),
1037
- )
985
+ return context.builder.portal_get(profile=args.profile, dash_key=args.dash_key, being_draft=bool(args.being_draft))
1038
986
 
1039
987
 
1040
988
  def _handle_view_get(args: argparse.Namespace, context: CliContext) -> dict:
@@ -1437,62 +1385,6 @@ def _handle_charts_apply(args: argparse.Namespace, context: CliContext) -> dict:
1437
1385
 
1438
1386
 
1439
1387
  def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
1440
- file_declaration = (
1441
- load_json_value(args.file, option_name="--file", max_bytes=4 * 1024 * 1024, max_depth=64)
1442
- if getattr(args, "file", None)
1443
- else None
1444
- )
1445
- file_schema_version: str | None = None
1446
- legacy_args_used = False
1447
- if file_declaration is not None:
1448
- if not isinstance(file_declaration, dict):
1449
- raise_config_error("builder portal apply --file must contain a JSON object")
1450
- legacy_keys = (
1451
- getattr(args, "sections_file", None),
1452
- getattr(args, "patch_sections_file", None),
1453
- getattr(args, "visibility_file", None),
1454
- getattr(args, "auth_file", None),
1455
- getattr(args, "dash_global_config_file", None),
1456
- getattr(args, "config_file", None),
1457
- getattr(args, "payload_file", None),
1458
- getattr(args, "icon", None),
1459
- getattr(args, "color", None),
1460
- (getattr(args, "dash_name", "") or ""),
1461
- (getattr(args, "layout_preset", "") or ""),
1462
- )
1463
- if any(bool(item) for item in legacy_keys):
1464
- raise_config_error(
1465
- "builder portal apply --file cannot be combined with patch parameters.",
1466
- fix_hint="Put the full declaration in the --file JSON; keep only --schema-version/--publish/--dash-key flags.",
1467
- error_code="ARGUMENT_ERROR",
1468
- )
1469
- if getattr(args, "package_id", None) is not None:
1470
- file_declaration.setdefault("package_tag_id", args.package_id)
1471
- if getattr(args, "dash_key", ""):
1472
- file_declaration["dash_key"] = args.dash_key
1473
- # publish 三态对齐 MCP(声明值优先、显式 flag 最高):声明内 publish 弹出后由 flag 解析——
1474
- # flag 未传时尊重声明值(含幂等重放的 publish:false),声明也未写时才缺省 true。
1475
- flag_publish = getattr(args, "publish", None)
1476
- declaration_publish = file_declaration.pop("publish", None)
1477
- effective_publish = (
1478
- bool(flag_publish)
1479
- if flag_publish is not None
1480
- else (bool(declaration_publish) if declaration_publish is not None else True)
1481
- )
1482
- file_schema_version = str(file_declaration.pop("schema_version", None) or file_declaration.pop("schemaVersion", None) or "").strip() or None
1483
- effective_schema_version = getattr(args, "schema_version", None) or file_schema_version
1484
- return context.builder.portal_apply(
1485
- profile=args.profile,
1486
- payload=file_declaration,
1487
- schema_version=effective_schema_version,
1488
- publish=effective_publish,
1489
- )
1490
- legacy_args_used = True
1491
- if legacy_args_used:
1492
- print(
1493
- "[deprecation] builder portal apply patch parameters are in a compatibility window; prefer `--file <declaration.json>`",
1494
- file=sys.stderr,
1495
- )
1496
1388
  payload = load_object_arg(args.payload_file, option_name="--payload-file") if getattr(args, "payload_file", None) else None
1497
1389
  payload_obj = payload if isinstance(payload, dict) else {}
1498
1390
  effective_dash_name = (args.dash_name or str(payload_obj.get("dash_name") or payload_obj.get("dashName") or payload_obj.get("name") or "")).strip()
@@ -1521,7 +1413,7 @@ def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
1521
1413
  dash_key=args.dash_key,
1522
1414
  dash_name=args.dash_name,
1523
1415
  package_id=args.package_id,
1524
- publish=bool(args.publish if args.publish is not None else True),
1416
+ publish=bool(args.publish),
1525
1417
  sections=sections,
1526
1418
  layout_preset=args.layout_preset,
1527
1419
  visibility=load_object_arg(args.visibility_file, option_name="--visibility-file"),
@@ -1532,7 +1424,6 @@ def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
1532
1424
  dash_global_config=load_object_arg(args.dash_global_config_file, option_name="--dash-global-config-file"),
1533
1425
  config=load_object_arg(args.config_file, option_name="--config-file"),
1534
1426
  payload=payload,
1535
- schema_version=getattr(args, "schema_version", None),
1536
1427
  patch_sections=patch_sections,
1537
1428
  )
1538
1429
 
@@ -7,22 +7,11 @@ import sys
7
7
  from typing import Any, Callable, TextIO
8
8
 
9
9
  from ..errors import QingflowApiError, backend_code_value_int, message_looks_like_invalid_token
10
- from ..public_surface import (
11
- NON_TOOL_CLI_HANDLER_ROUTES,
12
- cli_public_tool_spec_from_namespace,
13
- cli_route_from_namespace,
14
- cli_trim_key_from_namespace,
15
- )
10
+ from ..public_surface import cli_public_tool_spec_from_namespace
16
11
  from ..resource_uri import cli_resource_base_url
17
12
  from ..response_trim import resolve_cli_tool_name, trim_error_response, trim_public_response
18
13
  from ..tools.ai_builder_tools import _attach_builder_apply_envelope
19
14
  from ..version import get_cli_version, get_cli_version_info
20
- from ..tool_policy import (
21
- TOOL_POLICY_ENV,
22
- TOOL_POLICY_INVALID,
23
- ToolPolicyViolation,
24
- parse_tool_policy,
25
- )
26
15
  from .context import CliContext, build_cli_context
27
16
  from .formatters import emit_json_result, emit_text_result
28
17
  from .commands import register_all_commands
@@ -101,14 +90,6 @@ def run(
101
90
  if handler is None:
102
91
  parser.print_help(out)
103
92
  return 2
104
- violation = _cli_tool_policy_violation(args)
105
- if violation is not None:
106
- return _emit_error(
107
- violation.payload(),
108
- json_mode=bool(args.json),
109
- stdout=out,
110
- stderr=err,
111
- )
112
93
  try:
113
94
  if _should_force_json_output(args):
114
95
  setattr(args, "json", True)
@@ -141,20 +122,6 @@ def run(
141
122
  return exit_code
142
123
 
143
124
 
144
- def _cli_tool_policy_violation(args: argparse.Namespace) -> ToolPolicyViolation | None:
145
- stable_id = cli_trim_key_from_namespace(args)
146
- if stable_id is None:
147
- # 仅显式登记的非业务分发器可跳过闸门;无子命令的新增 handler 也必须默认拒绝。
148
- if cli_route_from_namespace(args) in NON_TOOL_CLI_HANDLER_ROUTES:
149
- return None
150
- return ToolPolicyViolation(
151
- TOOL_POLICY_INVALID,
152
- "user:unregistered",
153
- "CLI 业务命令未登记稳定工具 ID",
154
- )
155
- return parse_tool_policy(os.getenv(TOOL_POLICY_ENV)).violation_for(stable_id)
156
-
157
-
158
125
  def _handle_version(_args: argparse.Namespace, _context: CliContext) -> dict[str, Any]:
159
126
  info = get_cli_version_info()
160
127
  return {
@@ -2,14 +2,11 @@ from __future__ import annotations
2
2
 
3
3
  from contextvars import ContextVar
4
4
  from dataclasses import dataclass, field
5
- from typing import TYPE_CHECKING, Callable
5
+ from typing import Callable
6
6
 
7
7
  from .backend_client import BackendRequestContext
8
8
  from .session_store import BackendSession, SessionProfile
9
9
 
10
- if TYPE_CHECKING:
11
- from .tool_policy import ToolPolicySnapshot
12
-
13
10
 
14
11
  CLOUD_PROFILE_NAME = "__cloud_mcp__"
15
12
 
@@ -76,15 +73,3 @@ current_call_platform: ContextVar[str] = ContextVar(
76
73
  "qingflow_mcp_current_call_platform",
77
74
  default="",
78
75
  )
79
-
80
- # 远端 MCP 每个 HTTP 请求独立绑定策略原文;缺头用 None 表示,不回退进程环境变量。
81
- current_tool_policy_header: ContextVar[str | None] = ContextVar(
82
- "qingflow_mcp_current_tool_policy_header",
83
- default=None,
84
- )
85
-
86
- # 在 HTTP 入口合成一次快照,列表与调用共用,避免将 URL 选择保存到会话或共享环境。
87
- current_http_tool_policy: ContextVar[ToolPolicySnapshot | None] = ContextVar(
88
- "qingflow_mcp_current_http_tool_policy",
89
- default=None,
90
- )
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import argparse
4
3
  from argparse import Namespace
5
4
  from dataclasses import dataclass
6
5
 
@@ -110,7 +109,6 @@ USER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
110
109
  PublicToolSpec(USER_DOMAIN, "record_export_direct", ("record_export_direct",), ("export", "direct"), cli_show_effective_context=True),
111
110
  PublicToolSpec(USER_DOMAIN, "record_code_block_run", ("record_code_block_run",), ("record", "code-block-run"), cli_show_effective_context=True, cli_context_write=True),
112
111
  PublicToolSpec(USER_DOMAIN, "task_list", ("task_list",), ("task", "list"), cli_show_effective_context=True),
113
- PublicToolSpec(USER_DOMAIN, "task_workbench", cli_route=("task",), mcp_public=False),
114
112
  PublicToolSpec(USER_DOMAIN, "task_get", ("task_get",), ("task", "get"), cli_show_effective_context=True),
115
113
  PublicToolSpec(USER_DOMAIN, "task_action_execute", ("task_action_execute",), ("task", "action"), cli_show_effective_context=True, cli_context_write=True),
116
114
  PublicToolSpec(
@@ -132,12 +130,12 @@ USER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
132
130
 
133
131
 
134
132
  BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
135
- PublicToolSpec(BUILDER_DOMAIN, "auth_use_credential", ("auth_use_credential",), cli_public=False),
136
- PublicToolSpec(BUILDER_DOMAIN, "auth_whoami", ("auth_whoami",), cli_public=False),
137
- PublicToolSpec(BUILDER_DOMAIN, "auth_logout", ("auth_logout",), cli_public=False),
133
+ PublicToolSpec(BUILDER_DOMAIN, "auth_use_credential", ("auth_use_credential",), ("builder", "auth", "use-credential"), cli_public=False),
134
+ PublicToolSpec(BUILDER_DOMAIN, "auth_whoami", ("auth_whoami",), ("builder", "auth", "whoami"), cli_public=False),
135
+ PublicToolSpec(BUILDER_DOMAIN, "auth_logout", ("auth_logout",), ("builder", "auth", "logout"), cli_public=False),
138
136
  PublicToolSpec(BUILDER_DOMAIN, "get_login_link", ("get_login_link",), mcp_public=True, cli_public=False),
139
- PublicToolSpec(BUILDER_DOMAIN, "workspace_list", ("workspace_list",), cli_public=False),
140
- PublicToolSpec(BUILDER_DOMAIN, "workspace_get", ("workspace_get",), cli_public=False),
137
+ PublicToolSpec(BUILDER_DOMAIN, "workspace_list", ("workspace_list",), ("builder", "workspace", "list"), cli_public=False),
138
+ PublicToolSpec(BUILDER_DOMAIN, "workspace_get", ("workspace_get",), ("builder", "workspace", "get"), cli_public=False),
141
139
  PublicToolSpec(BUILDER_DOMAIN, "file_upload_local", ("file_upload_local",), ("builder", "file", "upload-local"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
142
140
  PublicToolSpec(BUILDER_DOMAIN, "feedback_submit", ("feedback_submit",), ("builder", "feedback", "submit"), has_contract=True),
143
141
  PublicToolSpec(BUILDER_DOMAIN, "builder_tool_contract", ("builder_tool_contract",), ("builder", "contract"), has_contract=False),
@@ -184,8 +182,6 @@ BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
184
182
  PublicToolSpec(BUILDER_DOMAIN, "app_views_get", ("app_views_get",), ("builder", "views", "get"), cli_show_effective_context=True),
185
183
  PublicToolSpec(BUILDER_DOMAIN, "app_views_validate", (), ("builder", "views", "validate"), mcp_public=True),
186
184
  PublicToolSpec(BUILDER_DOMAIN, "app_views_apply", ("app_views_apply",), ("builder", "views", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
187
- PublicToolSpec(BUILDER_DOMAIN, "portal_schema", (), ("builder", "portal", "schema"), mcp_public=True),
188
- PublicToolSpec(BUILDER_DOMAIN, "portal_validate", (), ("builder", "portal", "validate"), mcp_public=True),
189
185
  PublicToolSpec(BUILDER_DOMAIN, "app_charts_apply", ("app_charts_apply",), ("builder", "charts", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
190
186
  PublicToolSpec(BUILDER_DOMAIN, "portal_apply", ("portal_apply",), ("builder", "portal", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
191
187
  PublicToolSpec(BUILDER_DOMAIN, "portal_delete", ("portal_delete",), ("builder", "portal", "delete"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
@@ -198,8 +194,7 @@ PUBLIC_TOOL_BY_KEY: dict[str, PublicToolSpec] = {spec.trim_key: spec for spec in
198
194
  PUBLIC_TOOL_BY_CLI_ROUTE: dict[tuple[str, ...], PublicToolSpec] = {
199
195
  spec.cli_route: spec
200
196
  for spec in ALL_PUBLIC_TOOL_SPECS
201
- # 隐藏命令仍有可达 handler,闸门绑定不能把“帮助不可见”误当成“不可执行”。
202
- if spec.cli_route is not None
197
+ if spec.cli_public and spec.cli_route is not None
203
198
  }
204
199
  PUBLIC_TOOL_BY_CLI_ROUTE.update(
205
200
  {
@@ -210,9 +205,6 @@ PUBLIC_TOOL_BY_CLI_ROUTE.update(
210
205
  }
211
206
  )
212
207
 
213
- # 这些 handler 只负责版本输出或参数迁移提示,不会发起轻流业务请求。
214
- NON_TOOL_CLI_HANDLER_ROUTES = frozenset({("version",), ("record", "schema")})
215
-
216
208
 
217
209
  def public_tool_specs(domain: str | None = None) -> tuple[PublicToolSpec, ...]:
218
210
  if domain is None:
@@ -266,13 +258,11 @@ def cli_route_from_namespace(args: Namespace) -> tuple[str, ...] | None:
266
258
  record_command = getattr(args, "record_command", None)
267
259
  if record_command == "schema":
268
260
  schema_command = getattr(args, "record_schema_command", None)
269
- if isinstance(schema_command, str) and schema_command:
270
- return (command, "schema", schema_command)
271
- return (command, "schema")
261
+ return (command, "schema", schema_command) if isinstance(schema_command, str) and schema_command else None
272
262
  return (command, record_command) if isinstance(record_command, str) and record_command else None
273
263
  if command == "task":
274
264
  task_command = getattr(args, "task_command", None)
275
- return (command, task_command) if isinstance(task_command, str) and task_command else (command,)
265
+ return (command, task_command) if isinstance(task_command, str) and task_command else None
276
266
  if command != "builder":
277
267
  child_attr = f"{command}_command"
278
268
  child = getattr(args, child_attr, None)
@@ -293,54 +283,3 @@ def cli_route_from_namespace(args: Namespace) -> tuple[str, ...] | None:
293
283
  child_attr = f"builder_{builder_command.replace('-', '_')}_command"
294
284
  child = getattr(args, child_attr, None)
295
285
  return ("builder", builder_command, child) if isinstance(child, str) and child else None
296
-
297
-
298
- def discover_cli_handler_routes(
299
- parser: argparse.ArgumentParser,
300
- ) -> dict[tuple[str, ...], tuple[str, ...]]:
301
- """从 argparse 实际 handler 树枚举规范化路由及其实现,包含中间交互入口。"""
302
- discovered: dict[tuple[str, ...], set[str]] = {}
303
-
304
- def walk(current: argparse.ArgumentParser, path: tuple[str, ...], inherited: dict[str, object]) -> None:
305
- attrs = dict(inherited)
306
- for action in current._actions:
307
- if isinstance(action, argparse._SubParsersAction):
308
- continue
309
- if action.dest != argparse.SUPPRESS and action.default != argparse.SUPPRESS:
310
- attrs.setdefault(action.dest, action.default)
311
- attrs.update(current._defaults)
312
-
313
- handler = current._defaults.get("handler")
314
- if callable(handler):
315
- variants = [attrs]
316
- for action in current._actions:
317
- if isinstance(action, argparse._SubParsersAction) or action.choices is None:
318
- continue
319
- variants.extend({**attrs, action.dest: choice} for choice in action.choices)
320
- for variant in variants:
321
- route = cli_route_from_namespace(Namespace(**variant))
322
- if route is None:
323
- route = _normalize_cli_parser_path(path)
324
- if route:
325
- identity = f"{handler.__module__}:{handler.__qualname__}"
326
- discovered.setdefault(route, set()).add(identity)
327
-
328
- for action in current._actions:
329
- if not isinstance(action, argparse._SubParsersAction):
330
- continue
331
- for name, child in action.choices.items():
332
- next_attrs = dict(attrs)
333
- next_attrs[action.dest] = name
334
- walk(child, path + (name,), next_attrs)
335
-
336
- walk(parser, (), {})
337
- return {route: tuple(sorted(handlers)) for route, handlers in discovered.items()}
338
-
339
-
340
- def _normalize_cli_parser_path(path: tuple[str, ...]) -> tuple[str, ...]:
341
- normalized = list(path)
342
- if normalized and normalized[0] == "build":
343
- normalized[0] = "builder"
344
- if len(normalized) > 1 and normalized[0] == "builder" and normalized[1] == "associated-resources":
345
- normalized[1] = "associated-resource"
346
- return tuple(normalized)
@@ -51,9 +51,8 @@ COMMON_ERROR_DROP_TOP = {
51
51
 
52
52
  SUCCESS_POLICY_BY_TOOL: dict[str, TransformFn] = {}
53
53
 
54
- #: 这些校验命令的产出契约明确包含 ok 字段(供阶段 2 SKILL.md 引用),
54
+ #: 这两个校验命令的产出契约明确包含 ok 字段(供阶段 2 SKILL.md 引用),
55
55
  #: 需在 success 裁剪时保留 ok(默认 COMMON_SUCCESS_DROP_TOP 会丢弃 ok)。
56
- #: `builder:app_views_validate`(views 声明式校验)同属校验类工具,ok 同样进产出契约。
57
56
  _KEEP_OK_TOOLS = {
58
57
  "builder:app_form_validate_formula",
59
58
  "builder:app_form_validate_code_block",
@@ -2,6 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  from datetime import date
4
4
 
5
+ from mcp.server.fastmcp import FastMCP
6
+
5
7
  from .backend_client import BackendClient
6
8
  from .session_store import SessionStore
7
9
  from .tools.app_tools import AppTools
@@ -24,11 +26,9 @@ from .tools.task_context_tools import TaskContextTools
24
26
  from .tools.view_tools import ViewTools
25
27
  from .tools.workflow_tools import WorkflowTools
26
28
  from .tools.workspace_tools import WorkspaceTools
27
- from .tool_catalog import stdio_server_bindings
28
- from .tool_policy import PolicyAwareFastMCP, configure_tool_policy
29
29
 
30
30
 
31
- def _register_missing_builder_tools(server: PolicyAwareFastMCP) -> None:
31
+ def _register_missing_builder_tools(server: FastMCP) -> None:
32
32
  """Add current high-level builder tools to the unified MCP without duplicate names."""
33
33
  from .server_app_builder import build_builder_server
34
34
 
@@ -48,10 +48,10 @@ def _register_missing_builder_tools(server: PolicyAwareFastMCP) -> None:
48
48
  )
49
49
 
50
50
 
51
- def build_server() -> PolicyAwareFastMCP:
51
+ def build_server() -> FastMCP:
52
52
  today = date.today()
53
53
  current_year = today.year
54
- server = PolicyAwareFastMCP(
54
+ server = FastMCP(
55
55
  "Qingflow MCP",
56
56
  instructions=f"""Use this unified server for Qingflow operational and builder workflows. Current date: `{today.isoformat()}`.
57
57
 
@@ -259,14 +259,8 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
259
259
  WorkflowTools(sessions, backend).register(server)
260
260
  ViewTools(sessions, backend).register(server)
261
261
  SolutionTools(sessions, backend).register(server)
262
- # 保存补齐 Builder 前的真实注册来源,裸名冲突只能绑定最终保留 callable 的来源域。
263
- legacy_tool_names = set(server._tool_manager._tools)
264
262
  _register_missing_builder_tools(server)
265
- return configure_tool_policy(
266
- server,
267
- stdio_server_bindings(legacy_tool_names, server._tool_manager._tools),
268
- source="environment",
269
- )
263
+ return server
270
264
 
271
265
 
272
266
  mcp = build_server()
@@ -2,6 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
 
5
+ from mcp.server.fastmcp import FastMCP
6
+
5
7
  from .backend_client import BackendClient
6
8
  from .config import DEFAULT_PROFILE
7
9
  from .response_trim import BUILDER_SERVER_METHOD_MAP, trim_error_response, trim_public_response, wrap_trimmed_methods
@@ -12,8 +14,6 @@ from .tools.auth_tools import AuthTools
12
14
  from .tools.feedback_tools import FeedbackTools
13
15
  from .tools.file_tools import FileTools
14
16
  from .tools.workspace_tools import WorkspaceTools
15
- from .tool_catalog import builder_server_bindings
16
- from .tool_policy import PolicyAwareFastMCP, configure_tool_policy
17
17
 
18
18
 
19
19
  def _config_failure(message: str, *, fix_hint: str) -> dict:
@@ -27,8 +27,8 @@ def _config_failure(message: str, *, fix_hint: str) -> dict:
27
27
  }
28
28
 
29
29
 
30
- def build_builder_server() -> PolicyAwareFastMCP:
31
- server = PolicyAwareFastMCP(
30
+ def build_builder_server() -> FastMCP:
31
+ server = FastMCP(
32
32
  "Qingflow App Builder MCP",
33
33
  instructions=(
34
34
  "Use this server for AI-native Qingflow builder workflows. "
@@ -482,36 +482,13 @@ def build_builder_server() -> PolicyAwareFastMCP:
482
482
  def portal_list(profile: str = DEFAULT_PROFILE) -> dict:
483
483
  return ai_builder.portal_list(profile=profile)
484
484
 
485
- @server.tool()
486
- def portal_schema(
487
- schema_version: str | None = None,
488
- schema_kind: str | None = None,
489
- ) -> dict:
490
- """Get a complete portals schema or a focused component schema such as chart, view, grid, or carousel."""
491
- return ai_builder.portal_schema(schema_version=schema_version, schema_kind=schema_kind)
492
-
493
- @server.tool()
494
- def portal_validate(
495
- declaration: dict | None = None,
496
- schema_version: str | None = None,
497
- ) -> dict:
498
- return ai_builder.portal_validate(declaration=declaration or {}, schema_version=schema_version)
499
-
500
485
  @server.tool()
501
486
  def portal_get(
502
487
  profile: str = DEFAULT_PROFILE,
503
488
  dash_key: str = "",
504
489
  being_draft: bool = True,
505
- schema_kind: str = "summary",
506
- schema_version: str | None = None,
507
490
  ) -> dict:
508
- return ai_builder.portal_get(
509
- profile=profile,
510
- dash_key=dash_key,
511
- being_draft=being_draft,
512
- schema_kind=schema_kind,
513
- schema_version=schema_version,
514
- )
491
+ return ai_builder.portal_get(profile=profile, dash_key=dash_key, being_draft=being_draft)
515
492
 
516
493
  @server.tool()
517
494
  def view_get(profile: str = DEFAULT_PROFILE, view_key: str = "") -> dict:
@@ -647,10 +624,6 @@ def build_builder_server() -> PolicyAwareFastMCP:
647
624
  color: str | None = None,
648
625
  hide_copyright: bool | None = None,
649
626
  dash_global_config: dict | None = None,
650
- global_config: dict | None = None,
651
- style_template_id: int | None = None,
652
- overall_style: dict | None = None,
653
- schema_version: str | None = None,
654
627
  config: dict | None = None,
655
628
  payload: dict | None = None,
656
629
  patch_sections: list[dict] | None = None,
@@ -687,10 +660,6 @@ def build_builder_server() -> PolicyAwareFastMCP:
687
660
  color=color,
688
661
  hide_copyright=hide_copyright,
689
662
  dash_global_config=dash_global_config,
690
- global_config=global_config,
691
- style_template_id=style_template_id,
692
- overall_style=overall_style,
693
- schema_version=schema_version,
694
663
  config=config or {},
695
664
  payload=payload,
696
665
  patch_sections=patch_sections,
@@ -720,11 +689,7 @@ def build_builder_server() -> PolicyAwareFastMCP:
720
689
  expected_package_id=expected_package_id,
721
690
  )
722
691
 
723
- return configure_tool_policy(
724
- server,
725
- builder_server_bindings(server._tool_manager._tools),
726
- source="environment",
727
- )
692
+ return server
728
693
 
729
694
 
730
695
  def main() -> None:
@@ -3,6 +3,8 @@ from __future__ import annotations
3
3
  import json
4
4
  from datetime import date
5
5
 
6
+ from mcp.server.fastmcp import FastMCP
7
+
6
8
  from .backend_client import BackendClient
7
9
  from .config import DEFAULT_PROFILE
8
10
  from .response_trim import USER_SERVER_METHOD_MAP, trim_error_response, trim_public_response, wrap_trimmed_methods
@@ -20,14 +22,12 @@ from .tools.login_link_tools import LOGIN_LINK_DESCRIPTION, LoginLinkTools
20
22
  from .tools.resource_read_tools import ResourceReadTools
21
23
  from .tools.task_context_tools import TaskContextTools
22
24
  from .tools.workspace_tools import WorkspaceTools
23
- from .tool_catalog import user_server_bindings
24
- from .tool_policy import PolicyAwareFastMCP, configure_tool_policy
25
25
 
26
26
 
27
- def build_user_server(*, stateless_http: bool = False) -> PolicyAwareFastMCP:
27
+ def build_user_server(*, stateless_http: bool = False) -> FastMCP:
28
28
  today = date.today()
29
29
  current_year = today.year
30
- server = PolicyAwareFastMCP(
30
+ server = FastMCP(
31
31
  "Qingflow App User MCP",
32
32
  stateless_http=stateless_http,
33
33
  instructions=f"""Use this server for Qingflow operational workflows. Current date: `{today.isoformat()}`.
@@ -507,11 +507,7 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
507
507
  task_context_tools.register(server)
508
508
  directory_tools.register_frontend_search(server)
509
509
 
510
- return configure_tool_policy(
511
- server,
512
- user_server_bindings(server._tool_manager._tools),
513
- source="request" if stateless_http else "environment",
514
- )
510
+ return server
515
511
 
516
512
 
517
513
  mcp = build_user_server()