@josephyan/qingflow-mcp 0.1.0-beta.2
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 +517 -0
- package/docs/local-agent-install.md +213 -0
- package/entry_point.py +13 -0
- package/npm/bin/qingflow-mcp.mjs +7 -0
- package/npm/lib/runtime.mjs +146 -0
- package/npm/scripts/postinstall.mjs +12 -0
- package/package.json +34 -0
- package/pyproject.toml +63 -0
- package/qingflow-mcp +15 -0
- package/src/qingflow_mcp/__init__.py +5 -0
- package/src/qingflow_mcp/__main__.py +5 -0
- package/src/qingflow_mcp/backend_client.py +336 -0
- package/src/qingflow_mcp/config.py +166 -0
- package/src/qingflow_mcp/errors.py +66 -0
- package/src/qingflow_mcp/json_types.py +18 -0
- package/src/qingflow_mcp/server.py +70 -0
- package/src/qingflow_mcp/session_store.py +235 -0
- package/src/qingflow_mcp/solution/__init__.py +6 -0
- package/src/qingflow_mcp/solution/build_assembly_store.py +137 -0
- package/src/qingflow_mcp/solution/compiler/__init__.py +265 -0
- package/src/qingflow_mcp/solution/compiler/chart_compiler.py +96 -0
- package/src/qingflow_mcp/solution/compiler/form_compiler.py +456 -0
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +113 -0
- package/src/qingflow_mcp/solution/compiler/navigation_compiler.py +57 -0
- package/src/qingflow_mcp/solution/compiler/package_compiler.py +19 -0
- package/src/qingflow_mcp/solution/compiler/portal_compiler.py +60 -0
- package/src/qingflow_mcp/solution/compiler/view_compiler.py +51 -0
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +134 -0
- package/src/qingflow_mcp/solution/design_session.py +222 -0
- package/src/qingflow_mcp/solution/design_store.py +100 -0
- package/src/qingflow_mcp/solution/executor.py +2064 -0
- package/src/qingflow_mcp/solution/normalizer.py +23 -0
- package/src/qingflow_mcp/solution/run_store.py +221 -0
- package/src/qingflow_mcp/solution/spec_models.py +755 -0
- package/src/qingflow_mcp/tools/__init__.py +1 -0
- package/src/qingflow_mcp/tools/app_tools.py +239 -0
- package/src/qingflow_mcp/tools/approval_tools.py +481 -0
- package/src/qingflow_mcp/tools/auth_tools.py +496 -0
- package/src/qingflow_mcp/tools/base.py +81 -0
- package/src/qingflow_mcp/tools/directory_tools.py +476 -0
- package/src/qingflow_mcp/tools/file_tools.py +375 -0
- package/src/qingflow_mcp/tools/navigation_tools.py +177 -0
- package/src/qingflow_mcp/tools/package_tools.py +142 -0
- package/src/qingflow_mcp/tools/portal_tools.py +100 -0
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +258 -0
- package/src/qingflow_mcp/tools/record_tools.py +4305 -0
- package/src/qingflow_mcp/tools/role_tools.py +94 -0
- package/src/qingflow_mcp/tools/solution_tools.py +1860 -0
- package/src/qingflow_mcp/tools/task_tools.py +677 -0
- package/src/qingflow_mcp/tools/view_tools.py +324 -0
- package/src/qingflow_mcp/tools/workflow_tools.py +311 -0
- package/src/qingflow_mcp/tools/workspace_tools.py +143 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
|
|
7
|
+
from ..config import DEFAULT_PROFILE
|
|
8
|
+
from ..errors import QingflowApiError, raise_tool_error
|
|
9
|
+
from ..json_types import JSONObject
|
|
10
|
+
from .base import ToolBase
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PackageTools(ToolBase):
|
|
14
|
+
def register(self, mcp: FastMCP) -> None:
|
|
15
|
+
@mcp.tool()
|
|
16
|
+
def package_list(profile: str = DEFAULT_PROFILE, trial_status: str = "all") -> JSONObject:
|
|
17
|
+
return self.package_list(profile=profile, trial_status=trial_status)
|
|
18
|
+
|
|
19
|
+
@mcp.tool()
|
|
20
|
+
def package_get(profile: str = DEFAULT_PROFILE, tag_id: int = 0) -> JSONObject:
|
|
21
|
+
return self.package_get(profile=profile, tag_id=tag_id)
|
|
22
|
+
|
|
23
|
+
@mcp.tool()
|
|
24
|
+
def package_get_base(profile: str = DEFAULT_PROFILE, tag_id: int = 0) -> JSONObject:
|
|
25
|
+
return self.package_get_base(profile=profile, tag_id=tag_id)
|
|
26
|
+
|
|
27
|
+
@mcp.tool()
|
|
28
|
+
def package_create(profile: str = DEFAULT_PROFILE, payload: JSONObject | None = None) -> JSONObject:
|
|
29
|
+
return self.package_create(profile=profile, payload=payload or {})
|
|
30
|
+
|
|
31
|
+
@mcp.tool(description=self._high_risk_tool_description(operation="update", target="app package settings"))
|
|
32
|
+
def package_update(profile: str = DEFAULT_PROFILE, tag_id: int = 0, payload: JSONObject | None = None) -> JSONObject:
|
|
33
|
+
return self.package_update(profile=profile, tag_id=tag_id, payload=payload or {})
|
|
34
|
+
|
|
35
|
+
@mcp.tool(description=self._high_risk_tool_description(operation="delete", target="app package and linked data"))
|
|
36
|
+
def package_delete(profile: str = DEFAULT_PROFILE, tag_id: int = 0, deleted_all_data: bool = False) -> JSONObject:
|
|
37
|
+
return self.package_delete(profile=profile, tag_id=tag_id, deleted_all_data=deleted_all_data)
|
|
38
|
+
|
|
39
|
+
def package_list(self, *, profile: str, trial_status: str = "all") -> JSONObject:
|
|
40
|
+
def runner(session_profile, context):
|
|
41
|
+
result = self.backend.request("GET", context, "/tag", params={"trialStatus": trial_status})
|
|
42
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "items": result}
|
|
43
|
+
|
|
44
|
+
return self._run(profile, runner)
|
|
45
|
+
|
|
46
|
+
def package_get(self, *, profile: str, tag_id: int) -> JSONObject:
|
|
47
|
+
self._require_tag_id(tag_id)
|
|
48
|
+
|
|
49
|
+
def runner(session_profile, context):
|
|
50
|
+
result = self.backend.request("GET", context, f"/tag/{tag_id}")
|
|
51
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "tag_id": tag_id, "result": result}
|
|
52
|
+
|
|
53
|
+
return self._run(profile, runner)
|
|
54
|
+
|
|
55
|
+
def package_get_base(self, *, profile: str, tag_id: int) -> JSONObject:
|
|
56
|
+
self._require_tag_id(tag_id)
|
|
57
|
+
|
|
58
|
+
def runner(session_profile, context):
|
|
59
|
+
result = self.backend.request("GET", context, f"/tag/{tag_id}/baseInfo")
|
|
60
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "tag_id": tag_id, "result": result}
|
|
61
|
+
|
|
62
|
+
return self._run(profile, runner)
|
|
63
|
+
|
|
64
|
+
def package_create(self, *, profile: str, payload: JSONObject) -> JSONObject:
|
|
65
|
+
body = self._require_dict(payload)
|
|
66
|
+
|
|
67
|
+
def runner(session_profile, context):
|
|
68
|
+
result = self.backend.request("POST", context, "/tag", json_body=self._normalize_package_payload(body))
|
|
69
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "result": result}
|
|
70
|
+
|
|
71
|
+
return self._run(profile, runner)
|
|
72
|
+
|
|
73
|
+
def package_update(self, *, profile: str, tag_id: int, payload: JSONObject) -> JSONObject:
|
|
74
|
+
self._require_tag_id(tag_id)
|
|
75
|
+
body = self._require_dict(payload)
|
|
76
|
+
|
|
77
|
+
def runner(session_profile, context):
|
|
78
|
+
current = self.backend.request("GET", context, f"/tag/{tag_id}")
|
|
79
|
+
result = self.backend.request(
|
|
80
|
+
"PUT",
|
|
81
|
+
context,
|
|
82
|
+
f"/tag/{tag_id}",
|
|
83
|
+
json_body=self._normalize_package_payload(body, existing=current),
|
|
84
|
+
)
|
|
85
|
+
return self._attach_human_review_notice(
|
|
86
|
+
{"profile": profile, "ws_id": session_profile.selected_ws_id, "tag_id": tag_id, "result": result},
|
|
87
|
+
operation="update",
|
|
88
|
+
target="app package settings",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return self._run(profile, runner)
|
|
92
|
+
|
|
93
|
+
def package_delete(self, *, profile: str, tag_id: int, deleted_all_data: bool = False) -> JSONObject:
|
|
94
|
+
self._require_tag_id(tag_id)
|
|
95
|
+
|
|
96
|
+
def runner(session_profile, context):
|
|
97
|
+
result = self.backend.request(
|
|
98
|
+
"DELETE",
|
|
99
|
+
context,
|
|
100
|
+
"/tag",
|
|
101
|
+
json_body={"tagId": tag_id, "deletedAllData": deleted_all_data},
|
|
102
|
+
)
|
|
103
|
+
return self._attach_human_review_notice(
|
|
104
|
+
{"profile": profile, "ws_id": session_profile.selected_ws_id, "tag_id": tag_id, "deleted_all_data": deleted_all_data, "result": result},
|
|
105
|
+
operation="delete",
|
|
106
|
+
target="app package and linked data",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return self._run(profile, runner)
|
|
110
|
+
|
|
111
|
+
def _require_tag_id(self, tag_id: int) -> None:
|
|
112
|
+
if tag_id <= 0:
|
|
113
|
+
raise_tool_error(QingflowApiError.config_error("tag_id must be positive"))
|
|
114
|
+
|
|
115
|
+
def _normalize_package_payload(self, payload: JSONObject, existing: JSONObject | None = None) -> JSONObject:
|
|
116
|
+
data = deepcopy(existing) if isinstance(existing, dict) else {}
|
|
117
|
+
data.update(deepcopy(payload))
|
|
118
|
+
data.pop("tagId", None)
|
|
119
|
+
data.pop("createTime", None)
|
|
120
|
+
data.pop("creator", None)
|
|
121
|
+
data.pop("publishStatus", None)
|
|
122
|
+
data.pop("beingTrial", None)
|
|
123
|
+
data.pop("trialExpireDate", None)
|
|
124
|
+
data.setdefault("tagIcon", "")
|
|
125
|
+
data.setdefault("tagItems", [])
|
|
126
|
+
data.setdefault("auth", _default_package_auth())
|
|
127
|
+
return data
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _default_package_auth() -> JSONObject:
|
|
131
|
+
members = {
|
|
132
|
+
"depart": [],
|
|
133
|
+
"dynamic": [],
|
|
134
|
+
"includeSubDeparts": None,
|
|
135
|
+
"member": [],
|
|
136
|
+
"role": [],
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
"type": "WORKSPACE",
|
|
140
|
+
"contactAuth": {"type": "WORKSPACE_ALL", "authMembers": deepcopy(members)},
|
|
141
|
+
"externalMemberAuth": {"type": "NOT", "authMembers": deepcopy(members)},
|
|
142
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from mcp.server.fastmcp import FastMCP
|
|
4
|
+
|
|
5
|
+
from ..config import DEFAULT_PROFILE
|
|
6
|
+
from ..errors import QingflowApiError, raise_tool_error
|
|
7
|
+
from ..json_types import JSONObject
|
|
8
|
+
from .base import ToolBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PortalTools(ToolBase):
|
|
12
|
+
def register(self, mcp: FastMCP) -> None:
|
|
13
|
+
@mcp.tool()
|
|
14
|
+
def portal_list(profile: str = DEFAULT_PROFILE) -> JSONObject:
|
|
15
|
+
return self.portal_list(profile=profile)
|
|
16
|
+
|
|
17
|
+
@mcp.tool()
|
|
18
|
+
def portal_get(profile: str = DEFAULT_PROFILE, dash_key: str = "", being_draft: bool = False) -> JSONObject:
|
|
19
|
+
return self.portal_get(profile=profile, dash_key=dash_key, being_draft=being_draft)
|
|
20
|
+
|
|
21
|
+
@mcp.tool()
|
|
22
|
+
def portal_create(profile: str = DEFAULT_PROFILE, payload: JSONObject | None = None) -> JSONObject:
|
|
23
|
+
return self.portal_create(profile=profile, payload=payload or {})
|
|
24
|
+
|
|
25
|
+
@mcp.tool(description=self._high_risk_tool_description(operation="update", target="portal configuration"))
|
|
26
|
+
def portal_update(profile: str = DEFAULT_PROFILE, dash_key: str = "", payload: JSONObject | None = None) -> JSONObject:
|
|
27
|
+
return self.portal_update(profile=profile, dash_key=dash_key, payload=payload or {})
|
|
28
|
+
|
|
29
|
+
@mcp.tool(description=self._high_risk_tool_description(operation="delete", target="portal configuration"))
|
|
30
|
+
def portal_delete(profile: str = DEFAULT_PROFILE, dash_key: str = "") -> JSONObject:
|
|
31
|
+
return self.portal_delete(profile=profile, dash_key=dash_key)
|
|
32
|
+
|
|
33
|
+
@mcp.tool()
|
|
34
|
+
def portal_publish(profile: str = DEFAULT_PROFILE, dash_key: str = "") -> JSONObject:
|
|
35
|
+
return self.portal_publish(profile=profile, dash_key=dash_key)
|
|
36
|
+
|
|
37
|
+
def portal_list(self, *, profile: str) -> JSONObject:
|
|
38
|
+
def runner(session_profile, context):
|
|
39
|
+
result = self.backend.request("GET", context, "/dash")
|
|
40
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "items": result}
|
|
41
|
+
|
|
42
|
+
return self._run(profile, runner)
|
|
43
|
+
|
|
44
|
+
def portal_get(self, *, profile: str, dash_key: str, being_draft: bool = False) -> JSONObject:
|
|
45
|
+
self._require_dash_key(dash_key)
|
|
46
|
+
|
|
47
|
+
def runner(session_profile, context):
|
|
48
|
+
result = self.backend.request("GET", context, f"/dash/{dash_key}", params={"beingDraft": being_draft})
|
|
49
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "dash_key": dash_key, "result": result}
|
|
50
|
+
|
|
51
|
+
return self._run(profile, runner)
|
|
52
|
+
|
|
53
|
+
def portal_create(self, *, profile: str, payload: JSONObject) -> JSONObject:
|
|
54
|
+
body = self._require_dict(payload)
|
|
55
|
+
|
|
56
|
+
def runner(session_profile, context):
|
|
57
|
+
result = self.backend.request("POST", context, "/dash", json_body=body)
|
|
58
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "result": result}
|
|
59
|
+
|
|
60
|
+
return self._run(profile, runner)
|
|
61
|
+
|
|
62
|
+
def portal_update(self, *, profile: str, dash_key: str, payload: JSONObject) -> JSONObject:
|
|
63
|
+
self._require_dash_key(dash_key)
|
|
64
|
+
body = self._require_dict(payload)
|
|
65
|
+
|
|
66
|
+
def runner(session_profile, context):
|
|
67
|
+
result = self.backend.request("POST", context, f"/dash/{dash_key}", json_body=body)
|
|
68
|
+
return self._attach_human_review_notice(
|
|
69
|
+
{"profile": profile, "ws_id": session_profile.selected_ws_id, "dash_key": dash_key, "result": result},
|
|
70
|
+
operation="update",
|
|
71
|
+
target="portal configuration",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return self._run(profile, runner)
|
|
75
|
+
|
|
76
|
+
def portal_delete(self, *, profile: str, dash_key: str) -> JSONObject:
|
|
77
|
+
self._require_dash_key(dash_key)
|
|
78
|
+
|
|
79
|
+
def runner(session_profile, context):
|
|
80
|
+
result = self.backend.request("DELETE", context, f"/dash/{dash_key}")
|
|
81
|
+
return self._attach_human_review_notice(
|
|
82
|
+
{"profile": profile, "ws_id": session_profile.selected_ws_id, "dash_key": dash_key, "result": result},
|
|
83
|
+
operation="delete",
|
|
84
|
+
target="portal configuration",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return self._run(profile, runner)
|
|
88
|
+
|
|
89
|
+
def portal_publish(self, *, profile: str, dash_key: str) -> JSONObject:
|
|
90
|
+
self._require_dash_key(dash_key)
|
|
91
|
+
|
|
92
|
+
def runner(session_profile, context):
|
|
93
|
+
result = self.backend.request("POST", context, f"/dash/{dash_key}/publish")
|
|
94
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "dash_key": dash_key, "result": result}
|
|
95
|
+
|
|
96
|
+
return self._run(profile, runner)
|
|
97
|
+
|
|
98
|
+
def _require_dash_key(self, dash_key: str) -> None:
|
|
99
|
+
if not dash_key:
|
|
100
|
+
raise_tool_error(QingflowApiError.config_error("dash_key is required"))
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from uuid import uuid4
|
|
4
|
+
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
|
|
7
|
+
from ..backend_client import BackendRequestContext
|
|
8
|
+
from ..config import DEFAULT_PROFILE, normalize_base_url
|
|
9
|
+
from ..errors import QingflowApiError, raise_tool_error
|
|
10
|
+
from ..json_types import JSONObject, JSONValue
|
|
11
|
+
from .base import ToolBase
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _qingbi_base_url(base_url: str) -> str:
|
|
15
|
+
normalized = normalize_base_url(base_url)
|
|
16
|
+
if not normalized:
|
|
17
|
+
raise QingflowApiError.config_error("base_url is required")
|
|
18
|
+
return normalized[:-4] if normalized.endswith("/api") else normalized
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class QingbiReportTools(ToolBase):
|
|
22
|
+
def register(self, mcp: FastMCP) -> None:
|
|
23
|
+
@mcp.tool()
|
|
24
|
+
def qingbi_report_list(
|
|
25
|
+
profile: str = DEFAULT_PROFILE,
|
|
26
|
+
app_key: str = "",
|
|
27
|
+
) -> JSONObject:
|
|
28
|
+
return self.qingbi_report_list(profile=profile, app_key=app_key)
|
|
29
|
+
|
|
30
|
+
@mcp.tool()
|
|
31
|
+
def qingbi_report_create(
|
|
32
|
+
profile: str = DEFAULT_PROFILE,
|
|
33
|
+
payload: JSONObject | None = None,
|
|
34
|
+
) -> JSONObject:
|
|
35
|
+
return self.qingbi_report_create(profile=profile, payload=payload or {})
|
|
36
|
+
|
|
37
|
+
@mcp.tool()
|
|
38
|
+
def qingbi_report_get_base(
|
|
39
|
+
profile: str = DEFAULT_PROFILE,
|
|
40
|
+
chart_id: str = "",
|
|
41
|
+
) -> JSONObject:
|
|
42
|
+
return self.qingbi_report_get_base(profile=profile, chart_id=chart_id)
|
|
43
|
+
|
|
44
|
+
@mcp.tool(description=self._high_risk_tool_description(operation="update", target="report base settings"))
|
|
45
|
+
def qingbi_report_update_base(
|
|
46
|
+
profile: str = DEFAULT_PROFILE,
|
|
47
|
+
chart_id: str = "",
|
|
48
|
+
payload: JSONObject | None = None,
|
|
49
|
+
) -> JSONObject:
|
|
50
|
+
return self.qingbi_report_update_base(profile=profile, chart_id=chart_id, payload=payload or {})
|
|
51
|
+
|
|
52
|
+
@mcp.tool()
|
|
53
|
+
def qingbi_report_get_config(
|
|
54
|
+
profile: str = DEFAULT_PROFILE,
|
|
55
|
+
chart_id: str = "",
|
|
56
|
+
) -> JSONObject:
|
|
57
|
+
return self.qingbi_report_get_config(profile=profile, chart_id=chart_id)
|
|
58
|
+
|
|
59
|
+
@mcp.tool(description=self._high_risk_tool_description(operation="update", target="report chart config"))
|
|
60
|
+
def qingbi_report_update_config(
|
|
61
|
+
profile: str = DEFAULT_PROFILE,
|
|
62
|
+
chart_id: str = "",
|
|
63
|
+
payload: JSONObject | None = None,
|
|
64
|
+
) -> JSONObject:
|
|
65
|
+
return self.qingbi_report_update_config(profile=profile, chart_id=chart_id, payload=payload or {})
|
|
66
|
+
|
|
67
|
+
@mcp.tool()
|
|
68
|
+
def qingbi_report_get_data(
|
|
69
|
+
profile: str = DEFAULT_PROFILE,
|
|
70
|
+
chart_id: str = "",
|
|
71
|
+
payload: JSONObject | None = None,
|
|
72
|
+
page_num: int | None = None,
|
|
73
|
+
page_size: int | None = None,
|
|
74
|
+
page_num_y: int | None = None,
|
|
75
|
+
page_size_y: int | None = None,
|
|
76
|
+
) -> JSONObject:
|
|
77
|
+
return self.qingbi_report_get_data(
|
|
78
|
+
profile=profile,
|
|
79
|
+
chart_id=chart_id,
|
|
80
|
+
payload=payload or {},
|
|
81
|
+
page_num=page_num,
|
|
82
|
+
page_size=page_size,
|
|
83
|
+
page_num_y=page_num_y,
|
|
84
|
+
page_size_y=page_size_y,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
@mcp.tool(description=self._high_risk_tool_description(operation="delete", target="report chart"))
|
|
88
|
+
def qingbi_report_delete(
|
|
89
|
+
profile: str = DEFAULT_PROFILE,
|
|
90
|
+
chart_id: str = "",
|
|
91
|
+
) -> JSONObject:
|
|
92
|
+
return self.qingbi_report_delete(profile=profile, chart_id=chart_id)
|
|
93
|
+
|
|
94
|
+
@mcp.tool()
|
|
95
|
+
def qingbi_report_reorder(
|
|
96
|
+
profile: str = DEFAULT_PROFILE,
|
|
97
|
+
app_key: str = "",
|
|
98
|
+
chart_ids: list[str] | None = None,
|
|
99
|
+
) -> JSONObject:
|
|
100
|
+
return self.qingbi_report_reorder(profile=profile, app_key=app_key, chart_ids=chart_ids or [])
|
|
101
|
+
|
|
102
|
+
@mcp.tool()
|
|
103
|
+
def qingbi_report_list_fields(
|
|
104
|
+
profile: str = DEFAULT_PROFILE,
|
|
105
|
+
app_key: str = "",
|
|
106
|
+
) -> JSONObject:
|
|
107
|
+
return self.qingbi_report_list_fields(profile=profile, app_key=app_key)
|
|
108
|
+
|
|
109
|
+
@mcp.tool()
|
|
110
|
+
def qingbi_report_list_field_options(
|
|
111
|
+
profile: str = DEFAULT_PROFILE,
|
|
112
|
+
field_id: str = "",
|
|
113
|
+
chart_id: str = "",
|
|
114
|
+
) -> JSONObject:
|
|
115
|
+
return self.qingbi_report_list_field_options(profile=profile, field_id=field_id, chart_id=chart_id)
|
|
116
|
+
|
|
117
|
+
def qingbi_report_list(self, *, profile: str, app_key: str) -> JSONObject:
|
|
118
|
+
self._require_app_key(app_key)
|
|
119
|
+
return self._request(profile, "GET", f"/qingbi/charts/data/bichart/{app_key}", app_key=app_key, items_key="items")
|
|
120
|
+
|
|
121
|
+
def qingbi_report_create(self, *, profile: str, payload: JSONObject) -> JSONObject:
|
|
122
|
+
body = self._require_dict(payload)
|
|
123
|
+
return self._request(profile, "POST", "/qingbi/charts", json_body=body)
|
|
124
|
+
|
|
125
|
+
def qingbi_report_get_base(self, *, profile: str, chart_id: str) -> JSONObject:
|
|
126
|
+
self._require_chart_id(chart_id)
|
|
127
|
+
return self._request(profile, "GET", f"/qingbi/charts/baseinfo/{chart_id}", chart_id=chart_id)
|
|
128
|
+
|
|
129
|
+
def qingbi_report_update_base(self, *, profile: str, chart_id: str, payload: JSONObject) -> JSONObject:
|
|
130
|
+
self._require_chart_id(chart_id)
|
|
131
|
+
body = self._require_dict(payload)
|
|
132
|
+
return self._request(profile, "PUT", f"/qingbi/charts/baseinfo/{chart_id}", chart_id=chart_id, json_body=body, risk_operation="update", risk_target="report base settings")
|
|
133
|
+
|
|
134
|
+
def qingbi_report_get_config(self, *, profile: str, chart_id: str) -> JSONObject:
|
|
135
|
+
self._require_chart_id(chart_id)
|
|
136
|
+
return self._request(profile, "GET", f"/qingbi/charts/{chart_id}/configs", chart_id=chart_id)
|
|
137
|
+
|
|
138
|
+
def qingbi_report_update_config(self, *, profile: str, chart_id: str, payload: JSONObject) -> JSONObject:
|
|
139
|
+
self._require_chart_id(chart_id)
|
|
140
|
+
body = self._require_dict(payload)
|
|
141
|
+
return self._request(profile, "PUT", f"/qingbi/charts/{chart_id}/configs", chart_id=chart_id, json_body=body, risk_operation="update", risk_target="report chart config")
|
|
142
|
+
|
|
143
|
+
def qingbi_report_get_data(
|
|
144
|
+
self,
|
|
145
|
+
*,
|
|
146
|
+
profile: str,
|
|
147
|
+
chart_id: str,
|
|
148
|
+
payload: JSONObject,
|
|
149
|
+
page_num: int | None = None,
|
|
150
|
+
page_size: int | None = None,
|
|
151
|
+
page_num_y: int | None = None,
|
|
152
|
+
page_size_y: int | None = None,
|
|
153
|
+
) -> JSONObject:
|
|
154
|
+
self._require_chart_id(chart_id)
|
|
155
|
+
params = {"qfUUID": uuid4().hex}
|
|
156
|
+
if page_num is not None:
|
|
157
|
+
params["pageNum"] = page_num
|
|
158
|
+
if page_size is not None:
|
|
159
|
+
params["pageSize"] = page_size
|
|
160
|
+
if page_num_y is not None:
|
|
161
|
+
params["pageNumY"] = page_num_y
|
|
162
|
+
if page_size_y is not None:
|
|
163
|
+
params["pageSizeY"] = page_size_y
|
|
164
|
+
return self._request(profile, "POST", f"/qingbi/charts/data/{chart_id}", chart_id=chart_id, params=params, json_body=payload)
|
|
165
|
+
|
|
166
|
+
def qingbi_report_delete(self, *, profile: str, chart_id: str) -> JSONObject:
|
|
167
|
+
self._require_chart_id(chart_id)
|
|
168
|
+
return self._request(profile, "DELETE", f"/qingbi/charts/{chart_id}", chart_id=chart_id, risk_operation="delete", risk_target="report chart")
|
|
169
|
+
|
|
170
|
+
def qingbi_report_reorder(self, *, profile: str, app_key: str, chart_ids: list[str]) -> JSONObject:
|
|
171
|
+
self._require_app_key(app_key)
|
|
172
|
+
if not isinstance(chart_ids, list) or not chart_ids:
|
|
173
|
+
raise_tool_error(QingflowApiError.config_error("chart_ids must be a non-empty array"))
|
|
174
|
+
if any(not chart_id for chart_id in chart_ids):
|
|
175
|
+
raise_tool_error(QingflowApiError.config_error("chart_ids cannot contain empty values"))
|
|
176
|
+
return self._request(
|
|
177
|
+
profile,
|
|
178
|
+
"POST",
|
|
179
|
+
"/qingbi/charts/sort/chart",
|
|
180
|
+
app_key=app_key,
|
|
181
|
+
chart_ids=chart_ids,
|
|
182
|
+
json_body={"appKey": app_key, "sortChartIdList": chart_ids},
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
def qingbi_report_list_fields(self, *, profile: str, app_key: str) -> JSONObject:
|
|
186
|
+
self._require_app_key(app_key)
|
|
187
|
+
return self._request(
|
|
188
|
+
profile,
|
|
189
|
+
"GET",
|
|
190
|
+
"/qingbi/datasets/datasource/fields",
|
|
191
|
+
app_key=app_key,
|
|
192
|
+
params={"dataSourceId": app_key, "dataSourceType": "qingflow"},
|
|
193
|
+
items_key="items",
|
|
194
|
+
result_transform=_extract_dataset_fields,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
def qingbi_report_list_field_options(self, *, profile: str, field_id: str, chart_id: str) -> JSONObject:
|
|
198
|
+
if not field_id:
|
|
199
|
+
raise_tool_error(QingflowApiError.config_error("field_id is required"))
|
|
200
|
+
path = f"/qingbi/charts/datasource/{chart_id}/question/option" if chart_id else "/qingbi/charts/datasource/question/option"
|
|
201
|
+
params = {"fieldId": field_id}
|
|
202
|
+
if chart_id:
|
|
203
|
+
self._require_chart_id(chart_id)
|
|
204
|
+
return self._request(profile, "GET", path, chart_id=chart_id or None, field_id=field_id, params=params, items_key="items")
|
|
205
|
+
|
|
206
|
+
def _request(
|
|
207
|
+
self,
|
|
208
|
+
profile: str,
|
|
209
|
+
method: str,
|
|
210
|
+
path: str,
|
|
211
|
+
*,
|
|
212
|
+
json_body: JSONValue = None,
|
|
213
|
+
params: JSONObject | None = None,
|
|
214
|
+
items_key: str | None = None,
|
|
215
|
+
result_transform: object | None = None,
|
|
216
|
+
risk_operation: str | None = None,
|
|
217
|
+
risk_target: str | None = None,
|
|
218
|
+
**extra: JSONValue,
|
|
219
|
+
) -> JSONObject:
|
|
220
|
+
def runner(session_profile, context):
|
|
221
|
+
qingbi_context = BackendRequestContext(
|
|
222
|
+
base_url=_qingbi_base_url(context.base_url),
|
|
223
|
+
token=context.token,
|
|
224
|
+
ws_id=context.ws_id,
|
|
225
|
+
qf_request_id=context.qf_request_id,
|
|
226
|
+
qf_version=context.qf_version,
|
|
227
|
+
qf_version_source=context.qf_version_source,
|
|
228
|
+
)
|
|
229
|
+
result = self.backend.request(method, qingbi_context, path, json_body=json_body, params=params)
|
|
230
|
+
if callable(result_transform):
|
|
231
|
+
result = result_transform(result)
|
|
232
|
+
payload: JSONObject = {"profile": profile, "ws_id": session_profile.selected_ws_id}
|
|
233
|
+
if items_key:
|
|
234
|
+
payload[items_key] = result
|
|
235
|
+
else:
|
|
236
|
+
payload["result"] = result
|
|
237
|
+
payload.update(extra)
|
|
238
|
+
if risk_operation and risk_target:
|
|
239
|
+
return self._attach_human_review_notice(payload, operation=risk_operation, target=risk_target)
|
|
240
|
+
return payload
|
|
241
|
+
|
|
242
|
+
return self._run(profile, runner)
|
|
243
|
+
|
|
244
|
+
def _require_app_key(self, app_key: str) -> None:
|
|
245
|
+
if not app_key:
|
|
246
|
+
raise_tool_error(QingflowApiError.config_error("app_key is required"))
|
|
247
|
+
|
|
248
|
+
def _require_chart_id(self, chart_id: str) -> None:
|
|
249
|
+
if not chart_id:
|
|
250
|
+
raise_tool_error(QingflowApiError.config_error("chart_id is required"))
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _extract_dataset_fields(result: JSONValue) -> list[JSONObject]:
|
|
254
|
+
if isinstance(result, dict) and isinstance(result.get("fields"), list):
|
|
255
|
+
return result["fields"]
|
|
256
|
+
if isinstance(result, list):
|
|
257
|
+
return result
|
|
258
|
+
return []
|