@qingflow-tech/qingflow-app-builder-mcp 1.0.0

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 (105) hide show
  1. package/README.md +32 -0
  2. package/docs/local-agent-install.md +332 -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 +339 -0
  6. package/npm/scripts/postinstall.mjs +16 -0
  7. package/package.json +34 -0
  8. package/pyproject.toml +67 -0
  9. package/qingflow-app-builder-mcp +15 -0
  10. package/skills/qingflow-app-builder/SKILL.md +251 -0
  11. package/skills/qingflow-app-builder/agents/openai.yaml +4 -0
  12. package/skills/qingflow-app-builder/references/create-app.md +128 -0
  13. package/skills/qingflow-app-builder/references/environments.md +63 -0
  14. package/skills/qingflow-app-builder/references/flow-actors-and-permissions.md +123 -0
  15. package/skills/qingflow-app-builder/references/gotchas.md +64 -0
  16. package/skills/qingflow-app-builder/references/solution-playbooks.md +53 -0
  17. package/skills/qingflow-app-builder/references/tool-selection.md +93 -0
  18. package/skills/qingflow-app-builder/references/update-flow.md +158 -0
  19. package/skills/qingflow-app-builder/references/update-layout.md +68 -0
  20. package/skills/qingflow-app-builder/references/update-schema.md +68 -0
  21. package/skills/qingflow-app-builder/references/update-views.md +162 -0
  22. package/skills/qingflow-app-builder-code-integrations/SKILL.md +137 -0
  23. package/skills/qingflow-app-builder-code-integrations/agents/openai.yaml +4 -0
  24. package/skills/qingflow-app-builder-code-integrations/references/code-block.md +66 -0
  25. package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +77 -0
  26. package/src/qingflow_mcp/__init__.py +5 -0
  27. package/src/qingflow_mcp/__main__.py +5 -0
  28. package/src/qingflow_mcp/backend_client.py +649 -0
  29. package/src/qingflow_mcp/builder_facade/__init__.py +3 -0
  30. package/src/qingflow_mcp/builder_facade/models.py +1836 -0
  31. package/src/qingflow_mcp/builder_facade/service.py +15044 -0
  32. package/src/qingflow_mcp/cli/__init__.py +1 -0
  33. package/src/qingflow_mcp/cli/commands/__init__.py +18 -0
  34. package/src/qingflow_mcp/cli/commands/app.py +40 -0
  35. package/src/qingflow_mcp/cli/commands/auth.py +44 -0
  36. package/src/qingflow_mcp/cli/commands/builder.py +538 -0
  37. package/src/qingflow_mcp/cli/commands/chart.py +18 -0
  38. package/src/qingflow_mcp/cli/commands/common.py +62 -0
  39. package/src/qingflow_mcp/cli/commands/imports.py +96 -0
  40. package/src/qingflow_mcp/cli/commands/portal.py +25 -0
  41. package/src/qingflow_mcp/cli/commands/record.py +331 -0
  42. package/src/qingflow_mcp/cli/commands/repo.py +80 -0
  43. package/src/qingflow_mcp/cli/commands/task.py +89 -0
  44. package/src/qingflow_mcp/cli/commands/view.py +18 -0
  45. package/src/qingflow_mcp/cli/commands/workspace.py +25 -0
  46. package/src/qingflow_mcp/cli/context.py +60 -0
  47. package/src/qingflow_mcp/cli/formatters.py +334 -0
  48. package/src/qingflow_mcp/cli/json_io.py +50 -0
  49. package/src/qingflow_mcp/cli/main.py +178 -0
  50. package/src/qingflow_mcp/config.py +513 -0
  51. package/src/qingflow_mcp/errors.py +66 -0
  52. package/src/qingflow_mcp/import_store.py +121 -0
  53. package/src/qingflow_mcp/json_types.py +18 -0
  54. package/src/qingflow_mcp/list_type_labels.py +76 -0
  55. package/src/qingflow_mcp/public_surface.py +233 -0
  56. package/src/qingflow_mcp/repository_store.py +71 -0
  57. package/src/qingflow_mcp/response_trim.py +470 -0
  58. package/src/qingflow_mcp/server.py +212 -0
  59. package/src/qingflow_mcp/server_app_builder.py +533 -0
  60. package/src/qingflow_mcp/server_app_user.py +362 -0
  61. package/src/qingflow_mcp/session_store.py +302 -0
  62. package/src/qingflow_mcp/solution/__init__.py +6 -0
  63. package/src/qingflow_mcp/solution/build_assembly_store.py +181 -0
  64. package/src/qingflow_mcp/solution/compiler/__init__.py +282 -0
  65. package/src/qingflow_mcp/solution/compiler/chart_compiler.py +96 -0
  66. package/src/qingflow_mcp/solution/compiler/form_compiler.py +495 -0
  67. package/src/qingflow_mcp/solution/compiler/icon_utils.py +187 -0
  68. package/src/qingflow_mcp/solution/compiler/navigation_compiler.py +57 -0
  69. package/src/qingflow_mcp/solution/compiler/package_compiler.py +19 -0
  70. package/src/qingflow_mcp/solution/compiler/portal_compiler.py +60 -0
  71. package/src/qingflow_mcp/solution/compiler/view_compiler.py +51 -0
  72. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +173 -0
  73. package/src/qingflow_mcp/solution/design_session.py +222 -0
  74. package/src/qingflow_mcp/solution/design_store.py +100 -0
  75. package/src/qingflow_mcp/solution/executor.py +2398 -0
  76. package/src/qingflow_mcp/solution/normalizer.py +23 -0
  77. package/src/qingflow_mcp/solution/requirements_builder.py +536 -0
  78. package/src/qingflow_mcp/solution/run_store.py +244 -0
  79. package/src/qingflow_mcp/solution/spec_models.py +855 -0
  80. package/src/qingflow_mcp/tools/__init__.py +1 -0
  81. package/src/qingflow_mcp/tools/ai_builder_tools.py +3419 -0
  82. package/src/qingflow_mcp/tools/app_tools.py +925 -0
  83. package/src/qingflow_mcp/tools/approval_tools.py +1062 -0
  84. package/src/qingflow_mcp/tools/auth_tools.py +875 -0
  85. package/src/qingflow_mcp/tools/base.py +388 -0
  86. package/src/qingflow_mcp/tools/code_block_tools.py +777 -0
  87. package/src/qingflow_mcp/tools/custom_button_tools.py +202 -0
  88. package/src/qingflow_mcp/tools/directory_tools.py +675 -0
  89. package/src/qingflow_mcp/tools/feedback_tools.py +238 -0
  90. package/src/qingflow_mcp/tools/file_tools.py +409 -0
  91. package/src/qingflow_mcp/tools/import_tools.py +2189 -0
  92. package/src/qingflow_mcp/tools/navigation_tools.py +210 -0
  93. package/src/qingflow_mcp/tools/package_tools.py +326 -0
  94. package/src/qingflow_mcp/tools/portal_tools.py +158 -0
  95. package/src/qingflow_mcp/tools/qingbi_report_tools.py +374 -0
  96. package/src/qingflow_mcp/tools/record_tools.py +14037 -0
  97. package/src/qingflow_mcp/tools/repository_dev_tools.py +552 -0
  98. package/src/qingflow_mcp/tools/resource_read_tools.py +421 -0
  99. package/src/qingflow_mcp/tools/role_tools.py +112 -0
  100. package/src/qingflow_mcp/tools/solution_tools.py +4054 -0
  101. package/src/qingflow_mcp/tools/task_context_tools.py +2228 -0
  102. package/src/qingflow_mcp/tools/task_tools.py +890 -0
  103. package/src/qingflow_mcp/tools/view_tools.py +335 -0
  104. package/src/qingflow_mcp/tools/workflow_tools.py +376 -0
  105. package/src/qingflow_mcp/tools/workspace_tools.py +125 -0
@@ -0,0 +1,339 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { spawn, spawnSync } from "node:child_process";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const WINDOWS = process.platform === "win32";
7
+
8
+ function runChecked(command, args, options = {}) {
9
+ const result = spawnSync(command, args, {
10
+ encoding: "utf8",
11
+ stdio: ["ignore", "pipe", "pipe"],
12
+ ...options,
13
+ });
14
+ if (result.error) {
15
+ throw result.error;
16
+ }
17
+ if (result.status !== 0) {
18
+ const details = [result.stderr, result.stdout].filter((value) => typeof value === "string" && value.trim()).join("\n");
19
+ throw new Error(details ? `Command failed: ${command} ${args.join(" ")}\n${details}` : `Command failed: ${command} ${args.join(" ")}`);
20
+ }
21
+ }
22
+
23
+ function commandWorks(command, args) {
24
+ const result = spawnSync(command, args, {
25
+ stdio: "ignore",
26
+ });
27
+ return result.status === 0;
28
+ }
29
+
30
+ export function getPackageRoot(metaUrl) {
31
+ return path.resolve(path.dirname(fileURLToPath(metaUrl)), "..", "..");
32
+ }
33
+
34
+ export function getCodexHome() {
35
+ const configured = process.env.CODEX_HOME?.trim();
36
+ if (configured) {
37
+ return path.resolve(configured);
38
+ }
39
+ const home = process.env.HOME || process.env.USERPROFILE;
40
+ if (!home) {
41
+ throw new Error("Cannot resolve CODEX_HOME because HOME is not set.");
42
+ }
43
+ return path.join(home, ".codex");
44
+ }
45
+
46
+ export function installBundledSkills(packageRoot) {
47
+ const skillsSrc = path.join(packageRoot, "skills");
48
+ if (!fs.existsSync(skillsSrc)) {
49
+ return { installed: [], skipped: true, destination: null };
50
+ }
51
+
52
+ const codexHome = getCodexHome();
53
+ const skillsDestRoot = path.join(codexHome, "skills");
54
+ fs.mkdirSync(skillsDestRoot, { recursive: true });
55
+
56
+ const installed = [];
57
+ for (const entry of fs.readdirSync(skillsSrc, { withFileTypes: true })) {
58
+ if (!entry.isDirectory()) {
59
+ continue;
60
+ }
61
+ const src = path.join(skillsSrc, entry.name);
62
+ const dest = path.join(skillsDestRoot, entry.name);
63
+ fs.rmSync(dest, { recursive: true, force: true });
64
+ fs.cpSync(src, dest, { recursive: true });
65
+ installed.push(entry.name);
66
+ }
67
+
68
+ return { installed, skipped: false, destination: skillsDestRoot };
69
+ }
70
+
71
+ export function getVenvDir(packageRoot) {
72
+ return path.join(packageRoot, ".npm-python");
73
+ }
74
+
75
+ export function getVenvPython(packageRoot) {
76
+ return WINDOWS
77
+ ? path.join(getVenvDir(packageRoot), "Scripts", "python.exe")
78
+ : path.join(getVenvDir(packageRoot), "bin", "python");
79
+ }
80
+
81
+ export function getVenvServerCommand(packageRoot, commandName = "qingflow-mcp") {
82
+ return WINDOWS
83
+ ? path.join(getVenvDir(packageRoot), "Scripts", `${commandName}.exe`)
84
+ : path.join(getVenvDir(packageRoot), "bin", commandName);
85
+ }
86
+
87
+ function readPackageVersion(packageRoot) {
88
+ const packageJsonPath = path.join(packageRoot, "package.json");
89
+ try {
90
+ const payload = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
91
+ return typeof payload.version === "string" && payload.version.trim() ? payload.version.trim() : null;
92
+ } catch {
93
+ return null;
94
+ }
95
+ }
96
+
97
+ function readBootstrapStamp(stampPath) {
98
+ if (!fs.existsSync(stampPath)) {
99
+ return { exists: false, version: null };
100
+ }
101
+ try {
102
+ const payload = JSON.parse(fs.readFileSync(stampPath, "utf8"));
103
+ const version = typeof payload.package_version === "string" && payload.package_version.trim() ? payload.package_version.trim() : null;
104
+ return { exists: true, version };
105
+ } catch {
106
+ return { exists: true, version: null };
107
+ }
108
+ }
109
+
110
+ export function inspectPythonEnv(packageRoot, commandName = "qingflow-mcp") {
111
+ const venvDir = getVenvDir(packageRoot);
112
+ const serverCommand = getVenvServerCommand(packageRoot, commandName);
113
+ const stampPath = path.join(venvDir, ".bootstrap.json");
114
+ const packageVersion = readPackageVersion(packageRoot);
115
+ const stamp = readBootstrapStamp(stampPath);
116
+ const problems = [];
117
+
118
+ if (!packageVersion) {
119
+ problems.push("package-version-missing");
120
+ }
121
+ if (!fs.existsSync(serverCommand)) {
122
+ problems.push("server-command-missing");
123
+ }
124
+ if (!stamp.exists) {
125
+ problems.push("bootstrap-stamp-missing");
126
+ } else if (!stamp.version) {
127
+ problems.push("bootstrap-stamp-invalid");
128
+ } else if (packageVersion && stamp.version !== packageVersion) {
129
+ problems.push("bootstrap-version-mismatch");
130
+ }
131
+
132
+ return {
133
+ ready: problems.length === 0,
134
+ packageVersion,
135
+ stampVersion: stamp.version,
136
+ stampExists: stamp.exists,
137
+ stampPath,
138
+ serverCommand,
139
+ venvDir,
140
+ problems,
141
+ };
142
+ }
143
+
144
+ function formatRuntimeProblem(runtime, commandName, { allowRuntimeBootstrap = false } = {}) {
145
+ const problemLines = [];
146
+ for (const problem of runtime.problems) {
147
+ switch (problem) {
148
+ case "package-version-missing":
149
+ problemLines.push("- package.json is missing a valid version field");
150
+ break;
151
+ case "server-command-missing":
152
+ problemLines.push(`- missing Python entrypoint: ${runtime.serverCommand}`);
153
+ break;
154
+ case "bootstrap-stamp-missing":
155
+ problemLines.push(`- missing bootstrap stamp: ${runtime.stampPath}`);
156
+ break;
157
+ case "bootstrap-stamp-invalid":
158
+ problemLines.push(`- bootstrap stamp is unreadable or invalid: ${runtime.stampPath}`);
159
+ break;
160
+ case "bootstrap-version-mismatch":
161
+ problemLines.push(
162
+ `- bootstrap version mismatch: package=${runtime.packageVersion ?? "unknown"}, runtime=${runtime.stampVersion ?? "unknown"}`
163
+ );
164
+ break;
165
+ default:
166
+ problemLines.push(`- ${problem}`);
167
+ break;
168
+ }
169
+ }
170
+
171
+ const action = allowRuntimeBootstrap
172
+ ? "Delete .npm-python and retry, or rerun npm install to rebuild the embedded Python runtime."
173
+ : "Delete .npm-python and rerun npm install, or reinstall the npm package before starting the MCP server.";
174
+
175
+ const bootstrapNote = allowRuntimeBootstrap
176
+ ? ""
177
+ : "\nRuntime bootstrap is disabled for stdio MCP entrypoints so install logs can never corrupt the MCP stdout transport.";
178
+
179
+ return [
180
+ `[qingflow-mcp] Python runtime for ${commandName} is not ready.`,
181
+ ...problemLines,
182
+ action + bootstrapNote,
183
+ ].join("\n");
184
+ }
185
+
186
+ function getVenvPip(packageRoot) {
187
+ return WINDOWS
188
+ ? path.join(getVenvDir(packageRoot), "Scripts", "pip.exe")
189
+ : path.join(getVenvDir(packageRoot), "bin", "pip");
190
+ }
191
+
192
+ export function findPython() {
193
+ const preferred = process.env.QINGFLOW_MCP_PYTHON?.trim();
194
+ const candidates = preferred
195
+ ? [{ command: preferred, args: [], label: preferred }]
196
+ : WINDOWS
197
+ ? [
198
+ { command: "py", args: ["-3", "-V"], label: "py -3" },
199
+ { command: "python", args: ["-V"], label: "python" },
200
+ { command: "python3", args: ["-V"], label: "python3" },
201
+ ]
202
+ : [
203
+ { command: "python3", args: ["-V"], label: "python3" },
204
+ { command: "python", args: ["-V"], label: "python" },
205
+ ];
206
+
207
+ for (const candidate of candidates) {
208
+ if (commandWorks(candidate.command, candidate.args)) {
209
+ if (candidate.command === "py") {
210
+ return { command: "py", args: ["-3"], label: candidate.label };
211
+ }
212
+ return { command: candidate.command, args: [], label: candidate.label };
213
+ }
214
+ }
215
+
216
+ throw new Error(
217
+ "Python 3.11+ was not found. Set QINGFLOW_MCP_PYTHON to a Python 3 executable before running npm install."
218
+ );
219
+ }
220
+
221
+ export function ensurePythonEnv(packageRoot, { force = false, commandName = "qingflow-mcp" } = {}) {
222
+ const python = findPython();
223
+ const runtime = inspectPythonEnv(packageRoot, commandName);
224
+ const venvPython = getVenvPython(packageRoot);
225
+ const { packageVersion, serverCommand, stampPath, venvDir, stampVersion } = runtime;
226
+
227
+ if (!force && fs.existsSync(serverCommand) && fs.existsSync(stampPath) && stampVersion && stampVersion === packageVersion) {
228
+ return serverCommand;
229
+ }
230
+
231
+ if (force && fs.existsSync(venvDir)) {
232
+ fs.rmSync(venvDir, { recursive: true, force: true });
233
+ }
234
+
235
+ if (!fs.existsSync(venvPython)) {
236
+ runChecked(python.command, [...python.args, "-m", "venv", venvDir], { cwd: packageRoot });
237
+ }
238
+
239
+ const pip = getVenvPip(packageRoot);
240
+ runChecked(pip, ["install", "--disable-pip-version-check", "."], { cwd: packageRoot });
241
+
242
+ fs.writeFileSync(
243
+ stampPath,
244
+ JSON.stringify(
245
+ {
246
+ installed_at: new Date().toISOString(),
247
+ installer: "npm",
248
+ package_version: packageVersion,
249
+ },
250
+ null,
251
+ 2
252
+ )
253
+ );
254
+
255
+ if (!fs.existsSync(serverCommand)) {
256
+ throw new Error(`Bootstrap finished but ${serverCommand} was not created.`);
257
+ }
258
+
259
+ return serverCommand;
260
+ }
261
+
262
+ function proxyStreams(child) {
263
+ if (process.stdin.readable && child.stdin) {
264
+ process.stdin.pipe(child.stdin);
265
+ child.stdin.on("error", (error) => {
266
+ if (error.code !== "EPIPE") {
267
+ console.error(`[qingflow-mcp] Failed to forward stdin: ${error.message}`);
268
+ }
269
+ });
270
+ } else if (child.stdin) {
271
+ child.stdin.end();
272
+ }
273
+
274
+ if (child.stdout) {
275
+ child.stdout.pipe(process.stdout);
276
+ }
277
+ if (child.stderr) {
278
+ child.stderr.pipe(process.stderr);
279
+ }
280
+ }
281
+
282
+ function forwardSignal(child, signal) {
283
+ process.on(signal, () => {
284
+ if (!child.killed) {
285
+ child.kill(signal);
286
+ }
287
+ });
288
+ }
289
+
290
+ export function spawnServer(packageRoot, args, commandName = "qingflow-mcp", { allowRuntimeBootstrap = false } = {}) {
291
+ let runtime = inspectPythonEnv(packageRoot, commandName);
292
+ let serverCommand = runtime.serverCommand;
293
+
294
+ if (!runtime.ready) {
295
+ if (!allowRuntimeBootstrap) {
296
+ console.error(formatRuntimeProblem(runtime, commandName, { allowRuntimeBootstrap }));
297
+ process.exit(1);
298
+ return;
299
+ }
300
+
301
+ try {
302
+ serverCommand = ensurePythonEnv(packageRoot, { commandName });
303
+ runtime = inspectPythonEnv(packageRoot, commandName);
304
+ } catch (error) {
305
+ console.error(`[qingflow-mcp] Failed to prepare Python runtime for ${commandName}: ${error.message}`);
306
+ process.exit(1);
307
+ return;
308
+ }
309
+
310
+ if (!runtime.ready) {
311
+ console.error(formatRuntimeProblem(runtime, commandName, { allowRuntimeBootstrap }));
312
+ process.exit(1);
313
+ return;
314
+ }
315
+ }
316
+
317
+ const child = spawn(serverCommand, args, {
318
+ stdio: ["pipe", "pipe", "pipe"],
319
+ env: process.env,
320
+ windowsHide: true,
321
+ });
322
+
323
+ proxyStreams(child);
324
+ forwardSignal(child, "SIGINT");
325
+ forwardSignal(child, "SIGTERM");
326
+
327
+ child.on("close", (code, signal) => {
328
+ if (signal) {
329
+ process.kill(process.pid, signal);
330
+ return;
331
+ }
332
+ process.exit(code ?? 0);
333
+ });
334
+
335
+ child.on("error", (error) => {
336
+ console.error(`[qingflow-mcp] Failed to start server: ${error.message}`);
337
+ process.exit(1);
338
+ });
339
+ }
@@ -0,0 +1,16 @@
1
+ import { ensurePythonEnv, getPackageRoot, installBundledSkills } from "../lib/runtime.mjs";
2
+
3
+ const packageRoot = getPackageRoot(import.meta.url);
4
+
5
+ try {
6
+ console.log("[qingflow-mcp] Bootstrapping Python runtime...");
7
+ ensurePythonEnv(packageRoot, { commandName: "qingflow-app-builder-mcp" });
8
+ console.log("[qingflow-mcp] Python runtime is ready.");
9
+ const skills = installBundledSkills(packageRoot);
10
+ if (!skills.skipped) {
11
+ console.log(`[qingflow-mcp] Installed skills to ${skills.destination}: ${skills.installed.join(", ")}`);
12
+ }
13
+ } catch (error) {
14
+ console.error(`[qingflow-mcp] postinstall failed: ${error.message}`);
15
+ process.exit(1);
16
+ }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@qingflow-tech/qingflow-app-builder-mcp",
3
+ "version": "1.0.0",
4
+ "description": "Builder MCP for Qingflow app/package/system design and staged solution workflows.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "qingflow-app-builder-mcp": "./npm/bin/qingflow-app-builder-mcp.mjs"
9
+ },
10
+ "scripts": {
11
+ "postinstall": "node ./npm/scripts/postinstall.mjs"
12
+ },
13
+ "files": [
14
+ "README.md",
15
+ "pyproject.toml",
16
+ "entry_point.py",
17
+ "src/qingflow_mcp/**/*.py",
18
+ "src/qingflow_mcp/py.typed",
19
+ "qingflow-app-builder-mcp",
20
+ "npm/",
21
+ "docs/local-agent-install.md",
22
+ "skills/"
23
+ ],
24
+ "engines": {
25
+ "node": ">=16.16.0"
26
+ },
27
+ "keywords": [
28
+ "mcp",
29
+ "qingflow",
30
+ "agent",
31
+ "stdio",
32
+ "app-builder"
33
+ ]
34
+ }
package/pyproject.toml ADDED
@@ -0,0 +1,67 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "qingflow-mcp"
7
+ version = "0.2.0b87"
8
+ description = "User-authenticated MCP server for Qingflow"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.11"
12
+ authors = [
13
+ { name = "Qingflow", email = "support@qingflow.com" }
14
+ ]
15
+ keywords = ["mcp", "qingflow", "automation", "workflow"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Topic :: Software Development :: Libraries :: Python Modules",
24
+ ]
25
+ dependencies = [
26
+ "mcp>=1.9.4,<2.0.0",
27
+ "httpx>=0.27,<1.0",
28
+ "keyring>=25.5,<26.0",
29
+ "openpyxl>=3.1,<4.0",
30
+ "pydantic>=2.8,<3.0",
31
+ "pycryptodome>=3.20,<4.0",
32
+ "python-socketio[client]>=5.11,<6.0",
33
+ ]
34
+
35
+ [project.optional-dependencies]
36
+ dev = [
37
+ "pytest>=8.3,<9.0",
38
+ "respx>=0.22,<1.0",
39
+ ]
40
+ build = [
41
+ "pyinstaller>=6.0,<7.0",
42
+ "build>=1.0,<2.0",
43
+ "twine>=5.0,<6.0",
44
+ ]
45
+
46
+ [project.scripts]
47
+ qingflow-app-user-mcp = "qingflow_mcp.server_app_user:main"
48
+ qingflow-app-builder-mcp = "qingflow_mcp.server_app_builder:main"
49
+ qingflow = "qingflow_mcp.cli.main:main"
50
+
51
+ [project.urls]
52
+ Homepage = "https://github.com/qingflow/qingflow-mcp"
53
+ Documentation = "https://github.com/qingflow/qingflow-mcp#readme"
54
+ Repository = "https://github.com/qingflow/qingflow-mcp"
55
+ Issues = "https://github.com/qingflow/qingflow-mcp/issues"
56
+
57
+ [tool.setuptools.package-dir]
58
+ "" = "src"
59
+
60
+ [tool.setuptools.packages.find]
61
+ where = ["src"]
62
+
63
+ [tool.setuptools.package-data]
64
+ qingflow_mcp = ["py.typed"]
65
+
66
+ [tool.pytest.ini_options]
67
+ testpaths = ["tests"]
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ ROOT_DIR="$(cd "$(dirname "$0")" && pwd)"
4
+ if [[ -n "${PYTHON_BIN:-}" ]]; then
5
+ PYTHON="${PYTHON_BIN}"
6
+ elif [[ -x "$ROOT_DIR/.venv/bin/python" ]]; then
7
+ PYTHON="$ROOT_DIR/.venv/bin/python"
8
+ elif command -v python3 >/dev/null 2>&1; then
9
+ PYTHON="$(command -v python3)"
10
+ else
11
+ echo "qingflow-app-builder-mcp: python interpreter not found. Set PYTHON_BIN or create $ROOT_DIR/.venv" >&2
12
+ exit 1
13
+ fi
14
+ export PYTHONPATH="$ROOT_DIR/src${PYTHONPATH:+:$PYTHONPATH}"
15
+ exec "$PYTHON" -m qingflow_mcp.server_app_builder "$@"