feat(runner): add uvxPackage source type for Python MCPs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -246,6 +246,41 @@ const server = createServer(async (req, res) => {
|
||||
const pkg = { name: `mcp-${name}`, private: true, dependencies: { [npxPackage]: "*" } };
|
||||
writeFileSync(join(svcSourcePath, "package.json"), JSON.stringify(pkg, null, 2));
|
||||
svcRuntime = svcRuntime || "node";
|
||||
} else if (body.uvxPackage) {
|
||||
// uvx-based Python MCP: install via uv and find the entry point
|
||||
svcSourcePath = join("/var/claudemesh/services", name);
|
||||
mkdirSync(svcSourcePath, { recursive: true });
|
||||
const { execSync } = await import("node:child_process");
|
||||
try {
|
||||
execSync(`uv venv ${join(svcSourcePath, ".venv")}`, { timeout: 30_000, stdio: "pipe" });
|
||||
execSync(`${join(svcSourcePath, ".venv/bin/pip")} install ${body.uvxPackage}`, { timeout: 120_000, stdio: "pipe" });
|
||||
console.log(`[runner] uvx package installed: ${body.uvxPackage}`);
|
||||
} catch (e) {
|
||||
return json(res, 500, { error: `uvx install failed: ${e.message}` });
|
||||
}
|
||||
// Find the MCP binary in the venv
|
||||
const venvBin = join(svcSourcePath, ".venv/bin");
|
||||
if (existsSync(venvBin)) {
|
||||
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) svc._npxBin = join(venvBin, match);
|
||||
}
|
||||
svcRuntime = "python";
|
||||
// Skip normal installDeps — already installed via uv
|
||||
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: svc?._npxBin };
|
||||
Object.assign(svc, svc2);
|
||||
services.set(name, svc);
|
||||
spawnService(svc);
|
||||
await new Promise(r => setTimeout(r, 1500));
|
||||
try {
|
||||
svc.tools = await initMcp(svc);
|
||||
console.log(`[runner] ${name} ready (uvx), ${svc.tools.length} tools`);
|
||||
return json(res, 200, { status: "running", tools: svc.tools });
|
||||
} catch (e) {
|
||||
svc.status = "failed"; svc.logs.push(`MCP init failed: ${e.message}`);
|
||||
return json(res, 500, { error: e.message, logs: svc.logs.slice(-10) });
|
||||
}
|
||||
} else if (!svcSourcePath) {
|
||||
return json(res, 400, { error: "one of sourcePath, gitUrl, or npxPackage required" });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user