@josephyan/qingflow-app-builder-mcp 0.2.0-beta.7 → 0.2.0-beta.71
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 +5 -3
- package/docs/local-agent-install.md +21 -5
- package/npm/bin/qingflow-app-builder-mcp.mjs +1 -1
- package/npm/lib/runtime.mjs +168 -12
- package/package.json +1 -1
- package/pyproject.toml +4 -1
- package/skills/qingflow-app-builder/SKILL.md +155 -22
- package/skills/qingflow-app-builder/references/create-app.md +51 -21
- package/skills/qingflow-app-builder/references/environments.md +1 -1
- package/skills/qingflow-app-builder/references/flow-actors-and-permissions.md +123 -0
- package/skills/qingflow-app-builder/references/gotchas.md +28 -1
- package/skills/qingflow-app-builder/references/solution-playbooks.md +14 -12
- package/skills/qingflow-app-builder/references/tool-selection.md +47 -19
- package/skills/qingflow-app-builder/references/update-flow.md +112 -25
- package/skills/qingflow-app-builder/references/update-layout.md +11 -24
- package/skills/qingflow-app-builder/references/update-schema.md +1 -23
- package/skills/qingflow-app-builder/references/update-views.md +87 -21
- package/skills/qingflow-app-builder-code-integrations/SKILL.md +137 -0
- package/skills/qingflow-app-builder-code-integrations/agents/openai.yaml +4 -0
- package/skills/qingflow-app-builder-code-integrations/references/code-block.md +66 -0
- package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +77 -0
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/backend_client.py +210 -0
- package/src/qingflow_mcp/builder_facade/models.py +1252 -3
- package/src/qingflow_mcp/builder_facade/service.py +11367 -2389
- package/src/qingflow_mcp/cli/__init__.py +1 -0
- package/src/qingflow_mcp/cli/commands/__init__.py +15 -0
- package/src/qingflow_mcp/cli/commands/app.py +40 -0
- package/src/qingflow_mcp/cli/commands/auth.py +78 -0
- package/src/qingflow_mcp/cli/commands/builder.py +515 -0
- package/src/qingflow_mcp/cli/commands/common.py +62 -0
- package/src/qingflow_mcp/cli/commands/imports.py +96 -0
- package/src/qingflow_mcp/cli/commands/record.py +304 -0
- package/src/qingflow_mcp/cli/commands/task.py +89 -0
- package/src/qingflow_mcp/cli/commands/workspace.py +33 -0
- package/src/qingflow_mcp/cli/context.py +48 -0
- package/src/qingflow_mcp/cli/formatters.py +355 -0
- package/src/qingflow_mcp/cli/json_io.py +50 -0
- package/src/qingflow_mcp/cli/main.py +149 -0
- package/src/qingflow_mcp/config.py +39 -0
- package/src/qingflow_mcp/import_store.py +121 -0
- package/src/qingflow_mcp/list_type_labels.py +24 -0
- package/src/qingflow_mcp/response_trim.py +668 -0
- package/src/qingflow_mcp/server.py +160 -18
- package/src/qingflow_mcp/server_app_builder.py +275 -68
- package/src/qingflow_mcp/server_app_user.py +219 -191
- package/src/qingflow_mcp/session_store.py +41 -1
- package/src/qingflow_mcp/solution/compiler/form_compiler.py +43 -4
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +119 -45
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +41 -2
- package/src/qingflow_mcp/solution/executor.py +107 -11
- package/src/qingflow_mcp/solution/spec_models.py +2 -0
- package/src/qingflow_mcp/tools/ai_builder_tools.py +2032 -127
- package/src/qingflow_mcp/tools/app_tools.py +419 -12
- package/src/qingflow_mcp/tools/approval_tools.py +571 -72
- package/src/qingflow_mcp/tools/auth_tools.py +398 -2
- package/src/qingflow_mcp/tools/code_block_tools.py +756 -0
- package/src/qingflow_mcp/tools/custom_button_tools.py +179 -0
- package/src/qingflow_mcp/tools/directory_tools.py +203 -31
- package/src/qingflow_mcp/tools/feedback_tools.py +230 -0
- package/src/qingflow_mcp/tools/file_tools.py +1 -0
- package/src/qingflow_mcp/tools/import_tools.py +2150 -0
- package/src/qingflow_mcp/tools/package_tools.py +18 -4
- package/src/qingflow_mcp/tools/portal_tools.py +31 -0
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +109 -7
- package/src/qingflow_mcp/tools/record_tools.py +9894 -1104
- package/src/qingflow_mcp/tools/solution_tools.py +115 -3
- package/src/qingflow_mcp/tools/task_context_tools.py +2040 -0
- package/src/qingflow_mcp/tools/task_tools.py +376 -225
- package/src/qingflow_mcp/tools/workspace_tools.py +163 -19
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from copy import deepcopy
|
|
4
|
+
import json
|
|
4
5
|
from typing import Any
|
|
5
6
|
from uuid import uuid4
|
|
6
7
|
|
|
@@ -8,6 +9,7 @@ from mcp.server.fastmcp import FastMCP
|
|
|
8
9
|
from pydantic import BaseModel, ValidationError
|
|
9
10
|
|
|
10
11
|
from ..config import DEFAULT_PROFILE
|
|
12
|
+
from ..errors import QingflowApiError
|
|
11
13
|
from ..list_type_labels import get_record_list_type_label
|
|
12
14
|
from ..solution.build_assembly_store import BuildAssemblyStore, default_manifest
|
|
13
15
|
from ..solution.compiler import CompiledSolution, ExecutionPlan, ExecutionStep, build_execution_plan, compile_solution
|
|
@@ -1850,7 +1852,35 @@ class SolutionTools(ToolBase):
|
|
|
1850
1852
|
packages = PackageTools(self.sessions, self.backend)
|
|
1851
1853
|
normalized_name = package_name.strip()
|
|
1852
1854
|
if package_tag_id > 0:
|
|
1853
|
-
|
|
1855
|
+
try:
|
|
1856
|
+
result = packages.package_get(profile=profile, tag_id=package_tag_id, include_raw=False)
|
|
1857
|
+
except (QingflowApiError, RuntimeError) as exc:
|
|
1858
|
+
error = _coerce_solution_api_error(exc)
|
|
1859
|
+
if error.backend_code in {40002, 40027}:
|
|
1860
|
+
return {
|
|
1861
|
+
"status": "resolved",
|
|
1862
|
+
"matched_via": "tag_id",
|
|
1863
|
+
"tag_id": package_tag_id,
|
|
1864
|
+
"tag_name": None,
|
|
1865
|
+
"candidates": [],
|
|
1866
|
+
"metadata_unverified": True,
|
|
1867
|
+
"lookup_permission_blocked": {
|
|
1868
|
+
"scope": "package",
|
|
1869
|
+
"target": {"tag_id": package_tag_id},
|
|
1870
|
+
"transport_error": {
|
|
1871
|
+
"http_status": error.http_status,
|
|
1872
|
+
"backend_code": error.backend_code,
|
|
1873
|
+
"category": error.category,
|
|
1874
|
+
"request_id": error.request_id,
|
|
1875
|
+
},
|
|
1876
|
+
},
|
|
1877
|
+
}
|
|
1878
|
+
return _builder_package_resolution_failed(
|
|
1879
|
+
package_name=normalized_name,
|
|
1880
|
+
package_tag_id=package_tag_id,
|
|
1881
|
+
error=error,
|
|
1882
|
+
retried=False,
|
|
1883
|
+
)
|
|
1854
1884
|
summary = result.get("result") if isinstance(result.get("result"), dict) else {}
|
|
1855
1885
|
return {
|
|
1856
1886
|
"status": "resolved",
|
|
@@ -1869,12 +1899,28 @@ class SolutionTools(ToolBase):
|
|
|
1869
1899
|
"source_shape": None,
|
|
1870
1900
|
"retried": False,
|
|
1871
1901
|
}
|
|
1872
|
-
|
|
1902
|
+
try:
|
|
1903
|
+
result = packages.package_list(profile=profile, trial_status="all", include_raw=False)
|
|
1904
|
+
except (QingflowApiError, RuntimeError) as exc:
|
|
1905
|
+
return _builder_package_resolution_failed(
|
|
1906
|
+
package_name=normalized_name,
|
|
1907
|
+
package_tag_id=package_tag_id,
|
|
1908
|
+
error=_coerce_solution_api_error(exc),
|
|
1909
|
+
retried=False,
|
|
1910
|
+
)
|
|
1873
1911
|
items = result.get("items") if isinstance(result.get("items"), list) else []
|
|
1874
1912
|
source_shape = result.get("source_shape")
|
|
1875
1913
|
retried = False
|
|
1876
1914
|
if not items:
|
|
1877
|
-
|
|
1915
|
+
try:
|
|
1916
|
+
retry_result = packages.package_list(profile=profile, trial_status="all", include_raw=False)
|
|
1917
|
+
except (QingflowApiError, RuntimeError) as exc:
|
|
1918
|
+
return _builder_package_resolution_failed(
|
|
1919
|
+
package_name=normalized_name,
|
|
1920
|
+
package_tag_id=package_tag_id,
|
|
1921
|
+
error=_coerce_solution_api_error(exc),
|
|
1922
|
+
retried=True,
|
|
1923
|
+
)
|
|
1878
1924
|
retry_items = retry_result.get("items") if isinstance(retry_result.get("items"), list) else []
|
|
1879
1925
|
if retry_items:
|
|
1880
1926
|
items = retry_items
|
|
@@ -2982,6 +3028,72 @@ def _solution_error_fields(
|
|
|
2982
3028
|
}
|
|
2983
3029
|
|
|
2984
3030
|
|
|
3031
|
+
def _builder_package_resolution_failed(
|
|
3032
|
+
*,
|
|
3033
|
+
package_name: str,
|
|
3034
|
+
package_tag_id: int,
|
|
3035
|
+
error: QingflowApiError,
|
|
3036
|
+
retried: bool,
|
|
3037
|
+
) -> dict[str, Any]:
|
|
3038
|
+
if package_tag_id > 0:
|
|
3039
|
+
detail = f"failed to resolve package_tag_id '{package_tag_id}': {error.message}"
|
|
3040
|
+
elif error.backend_code in {40002, 40027}:
|
|
3041
|
+
detail = (
|
|
3042
|
+
f"failed to resolve package '{package_name}' because package listing is permission-restricted; "
|
|
3043
|
+
"provide package_tag_id explicitly or use an account that can list packages"
|
|
3044
|
+
)
|
|
3045
|
+
else:
|
|
3046
|
+
detail = f"failed to resolve package '{package_name}': {error.message}"
|
|
3047
|
+
error_fields = _solution_error_fields(category="config", detail=detail, suggested_next_call=None, stage="app")
|
|
3048
|
+
error_fields["error_code"] = "PACKAGE_RESOLVE_FAILED"
|
|
3049
|
+
return {
|
|
3050
|
+
"status": "failed",
|
|
3051
|
+
"response": {
|
|
3052
|
+
"status": "failed",
|
|
3053
|
+
"mode": "plan",
|
|
3054
|
+
"stage": "app",
|
|
3055
|
+
"errors": [{"category": "config", "detail": detail}],
|
|
3056
|
+
"package_resolution": {
|
|
3057
|
+
"status": "failed",
|
|
3058
|
+
"requested_name": package_name or None,
|
|
3059
|
+
"requested_tag_id": package_tag_id or None,
|
|
3060
|
+
"resolution_policy": "exact_name_only" if package_name else "tag_id_only",
|
|
3061
|
+
"candidates": [],
|
|
3062
|
+
"source_shape": None,
|
|
3063
|
+
"retried": retried,
|
|
3064
|
+
"transport_error": {
|
|
3065
|
+
"http_status": error.http_status,
|
|
3066
|
+
"backend_code": error.backend_code,
|
|
3067
|
+
"category": error.category,
|
|
3068
|
+
"request_id": error.request_id,
|
|
3069
|
+
},
|
|
3070
|
+
},
|
|
3071
|
+
**error_fields,
|
|
3072
|
+
},
|
|
3073
|
+
}
|
|
3074
|
+
|
|
3075
|
+
|
|
3076
|
+
def _coerce_solution_api_error(error: Exception) -> QingflowApiError:
|
|
3077
|
+
if isinstance(error, QingflowApiError):
|
|
3078
|
+
return error
|
|
3079
|
+
if isinstance(error, RuntimeError):
|
|
3080
|
+
try:
|
|
3081
|
+
payload = json.loads(str(error))
|
|
3082
|
+
except json.JSONDecodeError:
|
|
3083
|
+
payload = None
|
|
3084
|
+
if isinstance(payload, dict) and payload.get("category") and payload.get("message"):
|
|
3085
|
+
details = payload.get("details")
|
|
3086
|
+
return QingflowApiError(
|
|
3087
|
+
category=str(payload.get("category")),
|
|
3088
|
+
message=str(payload.get("message")),
|
|
3089
|
+
backend_code=payload.get("backend_code"),
|
|
3090
|
+
request_id=payload.get("request_id"),
|
|
3091
|
+
http_status=payload.get("http_status"),
|
|
3092
|
+
details=details if isinstance(details, dict) else None,
|
|
3093
|
+
)
|
|
3094
|
+
return QingflowApiError(category="runtime", message=str(error))
|
|
3095
|
+
|
|
3096
|
+
|
|
2985
3097
|
def _coerce_count(value: Any) -> int | None:
|
|
2986
3098
|
if isinstance(value, bool) or value is None:
|
|
2987
3099
|
return None
|