fix(runner): use python -m for Python MCPs instead of CLI binary
Some checks failed
CI / Typecheck (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-08 15:38:31 +01:00
parent 4ae6a86bf6
commit 4c385a16cc

View File

@@ -117,17 +117,15 @@ async function initMcp(svc) {
// --- Spawn --- // --- Spawn ---
function spawnService(svc) { function spawnService(svc) {
// npx/uvx packages have a pre-resolved binary // npx/uvx packages have pre-resolved entry points
let cmd, args; let cmd, args;
if (svc._npxBin) { if (svc._pythonModule) {
// Python venv binaries are scripts with shebangs — run directly // Python MCPs: run via venv python -m <module>
if (svc.runtime === "python") { cmd = svc._venvPython;
cmd = svc._npxBin; args = ["-m", svc._pythonModule];
args = []; } else if (svc._npxBin) {
} else {
cmd = "node"; cmd = "node";
args = [svc._npxBin]; args = [svc._npxBin];
}
} else { } else {
({ cmd, args } = detectEntry(svc.sourcePath, svc.runtime)); ({ cmd, args } = detectEntry(svc.sourcePath, svc.runtime));
} }
@@ -264,18 +262,13 @@ const server = createServer(async (req, res) => {
} catch (e) { } catch (e) {
return json(res, 500, { error: `uvx install failed: ${e.message}` }); return json(res, 500, { error: `uvx install failed: ${e.message}` });
} }
// Find the MCP binary in the venv // For Python MCPs: run via `python -m <module>` using the venv python.
let uvxBinPath = null; // The module name is derived from the package name: mcp-server-time → mcp_server_time
const venvBin = join(svcSourcePath, ".venv/bin"); const venvPython = join(svcSourcePath, ".venv/bin/python");
if (existsSync(venvBin)) { const moduleName = body.uvxPackage.replace(/-/g, "_");
const bins = readdirSync(venvBin).filter(b => !["python", "python3", "pip", "pip3", "activate", "Activate.ps1", "activate.csh", "activate.fish", "deactivate"].includes(b) && !b.startsWith("python3."));
const pkgShort = body.uvxPackage.split("/").pop().replace(/^@/, "");
const match = bins.find(b => b.includes(pkgShort.replace(/-/g, ""))) || bins.find(b => b.includes("mcp")) || bins[0];
if (match) uvxBinPath = join(venvBin, match);
}
svcRuntime = "python"; svcRuntime = "python";
// Skip normal installDeps — already installed via uv // _pythonModule signals spawnService to use `python -m <module>` instead of binary
const svc2 = { name, sourcePath: svcSourcePath, runtime: svcRuntime, env: svcEnv || {}, process: null, pid: null, tools: [], status: "running", pending: new Map(), logs: [], restarts: 0, healthFailures: 0, _npxBin: uvxBinPath }; const svc2 = { name, sourcePath: svcSourcePath, runtime: svcRuntime, env: svcEnv || {}, process: null, pid: null, tools: [], status: "running", pending: new Map(), logs: [], restarts: 0, healthFailures: 0, _venvPython: venvPython, _pythonModule: moduleName };
services.set(name, svc2); services.set(name, svc2);
spawnService(svc2); spawnService(svc2);
// Python MCPs take longer to start — retry init with backoff // Python MCPs take longer to start — retry init with backoff