@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.
- package/README.md +3 -3
- package/package.json +1 -1
- package/pyproject.toml +1 -2
- package/skills/qingflow-cli/SKILL.md +2 -104
- package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +2 -2
- package/skills/qingflow-cli/reference/builder/30-schema-fields.md +5 -22
- package/skills/qingflow-cli/reference/builder/70-portal.md +16 -37
- package/skills/qingflow-cli/reference/builder/reference/app-delivery-sop.md +3 -3
- package/skills/qingflow-cli/reference/examples/portal/portal_sections_all_types.example.json +7 -6
- package/src/qingflow_mcp/app_form_compat.py +0 -3
- package/src/qingflow_mcp/app_form_validator.py +20 -89
- package/src/qingflow_mcp/builder_facade/app_form.py +112 -629
- package/src/qingflow_mcp/builder_facade/models.py +13 -164
- package/src/qingflow_mcp/builder_facade/schemas/app_form/manifest.json +1 -15
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/complete.json +38 -112
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/address.json +8 -31
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/amount.json +10 -6
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/attachment.json +10 -33
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/code_block.json +10 -6
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/data_relation.json +18 -11
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/date.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/datetime.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/department.json +10 -6
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/email.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/link.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/long_text.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/member.json +10 -6
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/multi_select.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/number.json +10 -6
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/phone.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/q_linker.json +12 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/relation.json +15 -73
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_choice.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_select.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/subtable.json +29 -90
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/text.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/formula.json +0 -43
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/summary.json +19 -27
- package/src/qingflow_mcp/builder_facade/service.py +72 -1143
- package/src/qingflow_mcp/cli/commands/builder.py +5 -114
- package/src/qingflow_mcp/cli/main.py +1 -34
- package/src/qingflow_mcp/cloud_context.py +1 -16
- package/src/qingflow_mcp/public_surface.py +8 -69
- package/src/qingflow_mcp/response_trim.py +1 -2
- package/src/qingflow_mcp/server.py +6 -12
- package/src/qingflow_mcp/server_app_builder.py +6 -41
- package/src/qingflow_mcp/server_app_user.py +5 -9
- package/src/qingflow_mcp/server_cloud.py +7 -43
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +0 -73
- package/src/qingflow_mcp/solution/executor.py +2 -5
- package/src/qingflow_mcp/tools/ai_builder_tools.py +12 -110
- package/src/qingflow_mcp/tools/portal_tools.py +54 -4
- package/src/qingflow_mcp/tools/solution_tools.py +0 -2
- package/skills/qingflow-cli/reference/examples/portal/portal_apply_v1alpha1.example.json +0 -100
- package/src/qingflow_mcp/builder_facade/portals_schema.py +0 -397
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_content_classification.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_content_extraction.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_content_generation.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_document_parsing.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_image_generation.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_image_recognition.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_machine_translation.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_text_summary.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/date_duration.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/embed_field.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/image_select.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/location.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ocr.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/rich_text.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/portals/manifest.json +0 -59
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/complete.json +0 -1397
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/carousel.json +0 -193
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/chart.json +0 -643
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/favourite.json +0 -203
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/filter.json +0 -268
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/grid.json +0 -265
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/link.json +0 -131
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/task.json +0 -199
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/text.json +0 -123
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/view.json +0 -196
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/summary.json +0 -800
- package/src/qingflow_mcp/tool_catalog.py +0 -387
- package/src/qingflow_mcp/tool_catalog_i18n.py +0 -185
- package/src/qingflow_mcp/tool_policy.py +0 -197
- 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
|
-
|
|
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=
|
|
297
|
-
portal_apply.add_argument("--payload-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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
#:
|
|
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:
|
|
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() ->
|
|
51
|
+
def build_server() -> FastMCP:
|
|
52
52
|
today = date.today()
|
|
53
53
|
current_year = today.year
|
|
54
|
-
server =
|
|
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
|
|
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() ->
|
|
31
|
-
server =
|
|
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
|
|
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) ->
|
|
27
|
+
def build_user_server(*, stateless_http: bool = False) -> FastMCP:
|
|
28
28
|
today = date.today()
|
|
29
29
|
current_year = today.year
|
|
30
|
-
server =
|
|
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
|
|
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()
|