@josephyan/qingflow-app-builder-mcp 0.1.0-beta.9

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 (55) hide show
  1. package/README.md +21 -0
  2. package/docs/local-agent-install.md +228 -0
  3. package/entry_point.py +13 -0
  4. package/npm/bin/qingflow-app-builder-mcp.mjs +7 -0
  5. package/npm/lib/runtime.mjs +146 -0
  6. package/npm/scripts/postinstall.mjs +12 -0
  7. package/package.json +33 -0
  8. package/pyproject.toml +64 -0
  9. package/qingflow-app-builder-mcp +15 -0
  10. package/src/qingflow_mcp/__init__.py +5 -0
  11. package/src/qingflow_mcp/__main__.py +5 -0
  12. package/src/qingflow_mcp/backend_client.py +336 -0
  13. package/src/qingflow_mcp/config.py +182 -0
  14. package/src/qingflow_mcp/errors.py +66 -0
  15. package/src/qingflow_mcp/json_types.py +18 -0
  16. package/src/qingflow_mcp/list_type_labels.py +52 -0
  17. package/src/qingflow_mcp/server.py +70 -0
  18. package/src/qingflow_mcp/server_app_builder.py +352 -0
  19. package/src/qingflow_mcp/server_app_user.py +334 -0
  20. package/src/qingflow_mcp/session_store.py +249 -0
  21. package/src/qingflow_mcp/solution/__init__.py +6 -0
  22. package/src/qingflow_mcp/solution/build_assembly_store.py +137 -0
  23. package/src/qingflow_mcp/solution/compiler/__init__.py +265 -0
  24. package/src/qingflow_mcp/solution/compiler/chart_compiler.py +96 -0
  25. package/src/qingflow_mcp/solution/compiler/form_compiler.py +456 -0
  26. package/src/qingflow_mcp/solution/compiler/icon_utils.py +113 -0
  27. package/src/qingflow_mcp/solution/compiler/navigation_compiler.py +57 -0
  28. package/src/qingflow_mcp/solution/compiler/package_compiler.py +19 -0
  29. package/src/qingflow_mcp/solution/compiler/portal_compiler.py +60 -0
  30. package/src/qingflow_mcp/solution/compiler/view_compiler.py +51 -0
  31. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +134 -0
  32. package/src/qingflow_mcp/solution/design_session.py +222 -0
  33. package/src/qingflow_mcp/solution/design_store.py +100 -0
  34. package/src/qingflow_mcp/solution/executor.py +2065 -0
  35. package/src/qingflow_mcp/solution/normalizer.py +23 -0
  36. package/src/qingflow_mcp/solution/run_store.py +221 -0
  37. package/src/qingflow_mcp/solution/spec_models.py +853 -0
  38. package/src/qingflow_mcp/tools/__init__.py +1 -0
  39. package/src/qingflow_mcp/tools/app_tools.py +406 -0
  40. package/src/qingflow_mcp/tools/approval_tools.py +498 -0
  41. package/src/qingflow_mcp/tools/auth_tools.py +514 -0
  42. package/src/qingflow_mcp/tools/base.py +81 -0
  43. package/src/qingflow_mcp/tools/directory_tools.py +476 -0
  44. package/src/qingflow_mcp/tools/file_tools.py +375 -0
  45. package/src/qingflow_mcp/tools/navigation_tools.py +177 -0
  46. package/src/qingflow_mcp/tools/package_tools.py +142 -0
  47. package/src/qingflow_mcp/tools/portal_tools.py +100 -0
  48. package/src/qingflow_mcp/tools/qingbi_report_tools.py +235 -0
  49. package/src/qingflow_mcp/tools/record_tools.py +4307 -0
  50. package/src/qingflow_mcp/tools/role_tools.py +94 -0
  51. package/src/qingflow_mcp/tools/solution_tools.py +2680 -0
  52. package/src/qingflow_mcp/tools/task_tools.py +692 -0
  53. package/src/qingflow_mcp/tools/view_tools.py +280 -0
  54. package/src/qingflow_mcp/tools/workflow_tools.py +238 -0
  55. package/src/qingflow_mcp/tools/workspace_tools.py +170 -0
@@ -0,0 +1,70 @@
1
+ from __future__ import annotations
2
+
3
+ from mcp.server.fastmcp import FastMCP
4
+
5
+ from .backend_client import BackendClient
6
+ from .session_store import SessionStore
7
+ from .tools.app_tools import AppTools
8
+ from .tools.auth_tools import AuthTools
9
+ from .tools.file_tools import FileTools
10
+ from .tools.package_tools import PackageTools
11
+ from .tools.navigation_tools import NavigationTools
12
+ from .tools.approval_tools import ApprovalTools
13
+ from .tools.directory_tools import DirectoryTools
14
+ from .tools.portal_tools import PortalTools
15
+ from .tools.qingbi_report_tools import QingbiReportTools
16
+ from .tools.record_tools import RecordTools
17
+ from .tools.role_tools import RoleTools
18
+ from .tools.solution_tools import SolutionTools
19
+ from .tools.task_tools import TaskTools
20
+ from .tools.view_tools import ViewTools
21
+ from .tools.workflow_tools import WorkflowTools
22
+ from .tools.workspace_tools import WorkspaceTools
23
+
24
+
25
+ def build_server() -> FastMCP:
26
+ server = FastMCP(
27
+ "Qingflow MCP",
28
+ instructions=(
29
+ "Use auth_login first, then workspace_list and workspace_select. "
30
+ "All resource tools operate with the logged-in user's Qingflow permissions.\n\n"
31
+ "Task Center (待办/已办) handling:\n"
32
+ "- Use task_statistics to get counts of pending tasks (todo_count), timeouts, urged, etc.\n"
33
+ "- Use task_list to query tasks. Type values: 1=todo (待办), 2=initiated (我发起的), 3=cc (抄送), 5=done (已办).\n"
34
+ "- Use task_list_grouped to get tasks grouped by form/worksheet.\n"
35
+ "- Use task_mark_read to mark a specific task as read.\n"
36
+ "- Use task_urge to send an urgent reminder for a pending task.\n"
37
+ "- Process status values: 1=all, 2=processing, 3=passed, 4=refused, 5=need_supply, 6=urged, 7=timeout, 8=pre_timeout, 9=unread.\n"
38
+ "- After identifying the exact task node and record, use record_approve, record_reject, record_rollback, record_transfer, record_reassign, or record_countersign as needed."
39
+ ),
40
+ )
41
+ sessions = SessionStore()
42
+ backend = BackendClient()
43
+ AuthTools(sessions, backend).register(server)
44
+ WorkspaceTools(sessions, backend).register(server)
45
+ FileTools(sessions, backend).register(server)
46
+ RecordTools(sessions, backend).register(server)
47
+ RoleTools(sessions, backend).register(server)
48
+ AppTools(sessions, backend).register(server)
49
+ QingbiReportTools(sessions, backend).register(server)
50
+ PackageTools(sessions, backend).register(server)
51
+ NavigationTools(sessions, backend).register(server)
52
+ ApprovalTools(sessions, backend).register(server)
53
+ PortalTools(sessions, backend).register(server)
54
+ DirectoryTools(sessions, backend).register(server)
55
+ WorkflowTools(sessions, backend).register(server)
56
+ ViewTools(sessions, backend).register(server)
57
+ SolutionTools(sessions, backend).register(server)
58
+ TaskTools(sessions, backend).register(server)
59
+ return server
60
+
61
+
62
+ mcp = build_server()
63
+
64
+
65
+ def main() -> None:
66
+ mcp.run()
67
+
68
+
69
+ if __name__ == "__main__":
70
+ main()
@@ -0,0 +1,352 @@
1
+ from __future__ import annotations
2
+
3
+ from mcp.server.fastmcp import FastMCP
4
+
5
+ from .backend_client import BackendClient
6
+ from .config import DEFAULT_PROFILE
7
+ from .session_store import SessionStore
8
+ from .tools.app_tools import AppTools
9
+ from .tools.auth_tools import AuthTools
10
+ from .tools.file_tools import FileTools
11
+ from .tools.navigation_tools import NavigationTools
12
+ from .tools.package_tools import PackageTools
13
+ from .tools.portal_tools import PortalTools
14
+ from .tools.solution_tools import SolutionTools
15
+ from .tools.workspace_tools import WorkspaceTools
16
+
17
+
18
+ def build_builder_server() -> FastMCP:
19
+ server = FastMCP(
20
+ "Qingflow App Builder MCP",
21
+ instructions=(
22
+ "Use this server for high-level Qingflow builder workflows. "
23
+ "Call solution_schema_example before generating non-trivial builder payloads. "
24
+ "Prefer solution_build_app for package/entity/field/layout design, then solution_build_flow for workflow, "
25
+ "then solution_build_views, solution_build_analytics_portal, and solution_build_navigation. "
26
+ "Avoid handcrafting low-level workflow/view/report payloads."
27
+ ),
28
+ )
29
+ sessions = SessionStore()
30
+ backend = BackendClient()
31
+ auth = AuthTools(sessions, backend)
32
+ workspace = WorkspaceTools(sessions, backend)
33
+ files = FileTools(sessions, backend)
34
+ apps = AppTools(sessions, backend)
35
+ packages = PackageTools(sessions, backend)
36
+ portals = PortalTools(sessions, backend)
37
+ navigation = NavigationTools(sessions, backend)
38
+ solution = SolutionTools(sessions, backend)
39
+
40
+ @server.tool()
41
+ def auth_login(
42
+ profile: str = DEFAULT_PROFILE,
43
+ base_url: str | None = None,
44
+ qf_version: str | None = None,
45
+ email: str = "",
46
+ password: str = "",
47
+ persist: bool = True,
48
+ ) -> dict:
49
+ return auth.auth_login(
50
+ profile=profile,
51
+ base_url=base_url,
52
+ qf_version=qf_version,
53
+ email=email,
54
+ password=password,
55
+ persist=persist,
56
+ )
57
+
58
+ @server.tool()
59
+ def auth_use_token(
60
+ profile: str = DEFAULT_PROFILE,
61
+ base_url: str | None = None,
62
+ qf_version: str | None = None,
63
+ token: str = "",
64
+ ws_id: int | None = None,
65
+ persist: bool = False,
66
+ ) -> dict:
67
+ return auth.auth_use_token(
68
+ profile=profile,
69
+ base_url=base_url,
70
+ qf_version=qf_version,
71
+ token=token,
72
+ ws_id=ws_id,
73
+ persist=persist,
74
+ )
75
+
76
+ @server.tool()
77
+ def auth_whoami(profile: str = DEFAULT_PROFILE) -> dict:
78
+ return auth.auth_whoami(profile=profile)
79
+
80
+ @server.tool()
81
+ def auth_logout(profile: str = DEFAULT_PROFILE, forget_persisted: bool = False) -> dict:
82
+ return auth.auth_logout(profile=profile, forget_persisted=forget_persisted)
83
+
84
+ @server.tool()
85
+ def workspace_list(
86
+ profile: str = DEFAULT_PROFILE,
87
+ page_num: int = 1,
88
+ page_size: int = 20,
89
+ include_external: bool = False,
90
+ ) -> dict:
91
+ return workspace.workspace_list(
92
+ profile=profile,
93
+ page_num=page_num,
94
+ page_size=page_size,
95
+ include_external=include_external,
96
+ )
97
+
98
+ @server.tool()
99
+ def workspace_select(profile: str = DEFAULT_PROFILE, ws_id: int = 0) -> dict:
100
+ return workspace.workspace_select(profile=profile, ws_id=ws_id)
101
+
102
+ @server.tool()
103
+ def file_upload_local(
104
+ profile: str = DEFAULT_PROFILE,
105
+ upload_kind: str = "attachment",
106
+ file_path: str = "",
107
+ upload_mark: str | None = None,
108
+ content_type: str | None = None,
109
+ bucket_type: str | None = None,
110
+ path_id: int | None = None,
111
+ file_related_url: str | None = None,
112
+ ) -> dict:
113
+ return files.file_upload_local(
114
+ profile=profile,
115
+ upload_kind=upload_kind,
116
+ file_path=file_path,
117
+ upload_mark=upload_mark,
118
+ content_type=content_type,
119
+ bucket_type=bucket_type,
120
+ path_id=path_id,
121
+ file_related_url=file_related_url,
122
+ )
123
+
124
+ @server.tool()
125
+ def app_list(profile: str = DEFAULT_PROFILE, ship_auth: bool = False) -> dict:
126
+ return apps.app_list(profile=profile, ship_auth=ship_auth)
127
+
128
+ @server.tool()
129
+ def app_search(profile: str = DEFAULT_PROFILE, keyword: str = "", page_num: int = 1, page_size: int = 50) -> dict:
130
+ return apps.app_search(profile=profile, keyword=keyword, page_num=page_num, page_size=page_size)
131
+
132
+ @server.tool()
133
+ def app_get_base(profile: str = DEFAULT_PROFILE, app_key: str = "", include_raw: bool = False) -> dict:
134
+ return apps.app_get_base(profile=profile, app_key=app_key, include_raw=include_raw)
135
+
136
+ @server.tool()
137
+ def app_get_form_schema(
138
+ profile: str = DEFAULT_PROFILE,
139
+ app_key: str = "",
140
+ form_type: int = 1,
141
+ being_draft: bool | None = None,
142
+ being_apply: bool | None = None,
143
+ audit_node_id: int | None = None,
144
+ include_raw: bool = False,
145
+ ) -> dict:
146
+ return apps.app_get_form_schema(
147
+ profile=profile,
148
+ app_key=app_key,
149
+ form_type=form_type,
150
+ being_draft=being_draft,
151
+ being_apply=being_apply,
152
+ audit_node_id=audit_node_id,
153
+ include_raw=include_raw,
154
+ )
155
+
156
+ @server.tool()
157
+ def app_create(profile: str = DEFAULT_PROFILE, payload: dict | None = None) -> dict:
158
+ return apps.app_create(profile=profile, payload=payload or {})
159
+
160
+ @server.tool()
161
+ def app_publish(profile: str = DEFAULT_PROFILE, app_key: str = "", payload: dict | None = None) -> dict:
162
+ return apps.app_publish(profile=profile, app_key=app_key, payload=payload or {})
163
+
164
+ @server.tool()
165
+ def package_list(profile: str = DEFAULT_PROFILE, trial_status: str = "all") -> dict:
166
+ return packages.package_list(profile=profile, trial_status=trial_status)
167
+
168
+ @server.tool()
169
+ def package_get(profile: str = DEFAULT_PROFILE, tag_id: int = 0) -> dict:
170
+ return packages.package_get(profile=profile, tag_id=tag_id)
171
+
172
+ @server.tool()
173
+ def portal_list(profile: str = DEFAULT_PROFILE) -> dict:
174
+ return portals.portal_list(profile=profile)
175
+
176
+ @server.tool()
177
+ def portal_get(profile: str = DEFAULT_PROFILE, dash_key: str = "", being_draft: bool = False) -> dict:
178
+ return portals.portal_get(profile=profile, dash_key=dash_key, being_draft=being_draft)
179
+
180
+ @server.tool()
181
+ def navigation_list_published(profile: str = DEFAULT_PROFILE, page_num: int = 1, page_size: int = 50) -> dict:
182
+ return navigation.navigation_list_published(profile=profile, page_num=page_num, page_size=page_size)
183
+
184
+ @server.tool()
185
+ def navigation_list_draft_all(profile: str = DEFAULT_PROFILE, query_key: str | None = None) -> dict:
186
+ return navigation.navigation_list_draft_all(profile=profile, query_key=query_key)
187
+
188
+ @server.tool()
189
+ def navigation_get_status(profile: str = DEFAULT_PROFILE) -> dict:
190
+ return navigation.navigation_get_status(profile=profile)
191
+
192
+ @server.tool()
193
+ def solution_design_session(
194
+ action: str = "get",
195
+ session_id: str = "",
196
+ stage: str | None = None,
197
+ design_patch: dict | None = None,
198
+ metadata: dict | None = None,
199
+ ) -> dict:
200
+ return solution.solution_design_session(
201
+ action=action,
202
+ session_id=session_id,
203
+ stage=stage,
204
+ design_patch=design_patch or {},
205
+ metadata=metadata or {},
206
+ )
207
+
208
+ @server.tool()
209
+ def solution_schema_example(stage: str = "app", intent: str = "minimal") -> dict:
210
+ return solution.solution_schema_example(stage=stage, intent=intent)
211
+
212
+ @server.tool()
213
+ def solution_build_all(
214
+ profile: str = DEFAULT_PROFILE,
215
+ mode: str = "plan",
216
+ build_id: str = "",
217
+ solution_spec: dict | None = None,
218
+ publish: bool = True,
219
+ run_label: str | None = None,
220
+ target: dict | None = None,
221
+ repair_patch: dict | None = None,
222
+ verify: bool = False,
223
+ ) -> dict:
224
+ return solution.solution_build_all(
225
+ profile=profile,
226
+ mode=mode,
227
+ build_id=build_id,
228
+ solution_spec=solution_spec or {},
229
+ publish=publish,
230
+ run_label=run_label,
231
+ target=target or {},
232
+ repair_patch=repair_patch or {},
233
+ verify=verify,
234
+ )
235
+
236
+ @server.tool()
237
+ def solution_build_app(
238
+ profile: str = DEFAULT_PROFILE,
239
+ mode: str = "plan",
240
+ build_id: str = "",
241
+ app_spec: dict | None = None,
242
+ publish: bool = True,
243
+ run_label: str | None = None,
244
+ target: dict | None = None,
245
+ repair_patch: dict | None = None,
246
+ ) -> dict:
247
+ return solution.solution_build_app(
248
+ profile=profile,
249
+ mode=mode,
250
+ build_id=build_id,
251
+ app_spec=app_spec or {},
252
+ publish=publish,
253
+ run_label=run_label,
254
+ target=target or {},
255
+ repair_patch=repair_patch or {},
256
+ )
257
+
258
+ @server.tool()
259
+ def solution_build_flow(
260
+ profile: str = DEFAULT_PROFILE,
261
+ mode: str = "plan",
262
+ build_id: str = "",
263
+ flow_spec: dict | None = None,
264
+ publish: bool = True,
265
+ run_label: str | None = None,
266
+ repair_patch: dict | None = None,
267
+ ) -> dict:
268
+ return solution.solution_build_flow(
269
+ profile=profile,
270
+ mode=mode,
271
+ build_id=build_id,
272
+ flow_spec=flow_spec or {},
273
+ publish=publish,
274
+ run_label=run_label,
275
+ repair_patch=repair_patch or {},
276
+ )
277
+
278
+ @server.tool()
279
+ def solution_build_views(
280
+ profile: str = DEFAULT_PROFILE,
281
+ mode: str = "plan",
282
+ build_id: str = "",
283
+ views_spec: dict | None = None,
284
+ publish: bool = True,
285
+ run_label: str | None = None,
286
+ repair_patch: dict | None = None,
287
+ ) -> dict:
288
+ return solution.solution_build_views(
289
+ profile=profile,
290
+ mode=mode,
291
+ build_id=build_id,
292
+ views_spec=views_spec or {},
293
+ publish=publish,
294
+ run_label=run_label,
295
+ repair_patch=repair_patch or {},
296
+ )
297
+
298
+ @server.tool()
299
+ def solution_build_analytics_portal(
300
+ profile: str = DEFAULT_PROFILE,
301
+ mode: str = "plan",
302
+ build_id: str = "",
303
+ analytics_portal_spec: dict | None = None,
304
+ publish: bool = True,
305
+ run_label: str | None = None,
306
+ repair_patch: dict | None = None,
307
+ ) -> dict:
308
+ return solution.solution_build_analytics_portal(
309
+ profile=profile,
310
+ mode=mode,
311
+ build_id=build_id,
312
+ analytics_portal_spec=analytics_portal_spec or {},
313
+ publish=publish,
314
+ run_label=run_label,
315
+ repair_patch=repair_patch or {},
316
+ )
317
+
318
+ @server.tool()
319
+ def solution_build_navigation(
320
+ profile: str = DEFAULT_PROFILE,
321
+ mode: str = "plan",
322
+ build_id: str = "",
323
+ navigation_spec: dict | None = None,
324
+ publish: bool = True,
325
+ run_label: str | None = None,
326
+ repair_patch: dict | None = None,
327
+ ) -> dict:
328
+ return solution.solution_build_navigation(
329
+ profile=profile,
330
+ mode=mode,
331
+ build_id=build_id,
332
+ navigation_spec=navigation_spec or {},
333
+ publish=publish,
334
+ run_label=run_label,
335
+ repair_patch=repair_patch or {},
336
+ )
337
+
338
+ @server.tool()
339
+ def solution_build_status(build_id: str = "") -> dict:
340
+ return solution.solution_build_status(build_id=build_id)
341
+ return server
342
+
343
+
344
+ mcp = build_builder_server()
345
+
346
+
347
+ def main() -> None:
348
+ mcp.run()
349
+
350
+
351
+ if __name__ == "__main__":
352
+ main()