feat: ticket ingestion MCP server (#1)
* basic ticket uploading * take project_id in request params instead of env * add an endpoint to list all available projects * add mcp server bin to npx * add missing scripts to package and publish to npm * fix rmcp version * Use utils::asset_dir * Don't run migrations or create DB from MCP * a fix for the first dev run when no frontend/dist/index.html exists * Add more MCP endpoints (#8) * add new endpoints for project and task management * add simpler more focused endpoints to improve agent understanding on this MCP * improve test script * combine npm binaries and allow passing --mcp as an arg * cargo fmt * fixes after rebase * clippy fixes * Script tweaks --------- Co-authored-by: couscous <couscous@runner.com> Co-authored-by: anastasiya1155 <anastasiya1155@gmail.com> Co-authored-by: Louis Knight-Webb <louis@bloop.ai> Co-authored-by: Anastasiia Solop <35258279+anastasiya1155@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
58f621c816
commit
0514d437a2
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { execSync } = require("child_process");
|
||||
const { execSync, spawn } = require("child_process");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
@@ -19,9 +19,7 @@ function getPlatformDir() {
|
||||
} else if (platform === "darwin" && arch === "arm64") {
|
||||
return "macos-arm64";
|
||||
} else {
|
||||
console.error(
|
||||
`❌ Unsupported platform: ${platform}-${arch}`
|
||||
);
|
||||
console.error(`❌ Unsupported platform: ${platform}-${arch}`);
|
||||
console.error("Supported platforms:");
|
||||
console.error(" - Linux x64");
|
||||
console.error(" - Windows x64");
|
||||
@@ -31,64 +29,128 @@ function getPlatformDir() {
|
||||
}
|
||||
}
|
||||
|
||||
function getBinaryName() {
|
||||
return platform === "win32" ? "vibe-kanban.exe" : "vibe-kanban";
|
||||
function getBinaryName(base_name) {
|
||||
return platform === "win32" ? `${base_name}.exe` : base_name;
|
||||
}
|
||||
|
||||
try {
|
||||
const platformDir = getPlatformDir();
|
||||
const extractDir = path.join(__dirname, "..", "dist", platformDir);
|
||||
const zipName = "vibe-kanban.zip";
|
||||
const platformDir = getPlatformDir();
|
||||
const extractDir = path.join(__dirname, "..", "dist", platformDir);
|
||||
|
||||
const isMcpMode = process.argv.includes("--mcp");
|
||||
|
||||
if (!fs.existsSync(extractDir)) {
|
||||
fs.mkdirSync(extractDir, { recursive: true });
|
||||
}
|
||||
|
||||
if (isMcpMode) {
|
||||
const baseName = "vibe-kanban-mcp";
|
||||
const binaryName = getBinaryName(baseName);
|
||||
const binaryPath = path.join(extractDir, binaryName);
|
||||
const zipName = `${baseName}.zip`;
|
||||
const zipPath = path.join(extractDir, zipName);
|
||||
|
||||
// Check if binary exists, delete if it does
|
||||
if (fs.existsSync(binaryPath)) {
|
||||
fs.unlinkSync(binaryPath);
|
||||
}
|
||||
|
||||
// Check if zip file exists
|
||||
if (!fs.existsSync(zipPath)) {
|
||||
console.error(`❌ vibe-kanban.zip not found at: ${zipPath}`);
|
||||
// console.error(`❌ ${zipName} not found at: ${zipPath}`);
|
||||
// console.error(`Current platform: ${platform}-${arch} (${platformDir})`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Unzip the file
|
||||
// console.log(`📦 Extracting ${baseName}...`);
|
||||
if (platform === "win32") {
|
||||
// Use PowerShell on Windows
|
||||
execSync(
|
||||
`powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${extractDir}' -Force"`,
|
||||
{ stdio: "inherit" }
|
||||
);
|
||||
} else {
|
||||
// Use unzip on Unix-like systems
|
||||
execSync(`unzip -qq -o "${zipPath}" -d "${extractDir}"`, {
|
||||
stdio: "inherit",
|
||||
});
|
||||
}
|
||||
|
||||
// Make sure it's executable
|
||||
try {
|
||||
fs.chmodSync(binaryPath, 0o755);
|
||||
} catch (error) {
|
||||
// console.error(
|
||||
// "⚠️ Warning: Could not set executable permissions:",
|
||||
// error.message
|
||||
// );
|
||||
}
|
||||
|
||||
// Launch MCP server
|
||||
// console.error(`🚀 Starting ${baseName}...`);
|
||||
|
||||
const mcpProcess = spawn(binaryPath, [], {
|
||||
stdio: ["pipe", "pipe", "inherit"], // stdin/stdout for MCP, stderr for logs
|
||||
});
|
||||
|
||||
// Forward stdin to MCP server
|
||||
process.stdin.pipe(mcpProcess.stdin);
|
||||
|
||||
// Forward MCP server stdout to our stdout
|
||||
mcpProcess.stdout.pipe(process.stdout);
|
||||
|
||||
// Handle process termination
|
||||
mcpProcess.on("exit", (code) => {
|
||||
process.exit(code || 0);
|
||||
});
|
||||
|
||||
mcpProcess.on("error", (error) => {
|
||||
console.error("❌ MCP server error:", error.message);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Handle Ctrl+C
|
||||
process.on("SIGINT", () => {
|
||||
console.error("\n🛑 Shutting down MCP server...");
|
||||
mcpProcess.kill("SIGINT");
|
||||
});
|
||||
|
||||
process.on("SIGTERM", () => {
|
||||
mcpProcess.kill("SIGTERM");
|
||||
});
|
||||
} else {
|
||||
const baseName = "vibe-kanban";
|
||||
const binaryName = getBinaryName(baseName);
|
||||
const binaryPath = path.join(extractDir, binaryName);
|
||||
const zipName = `${baseName}.zip`;
|
||||
const zipPath = path.join(extractDir, zipName);
|
||||
|
||||
// Check if binary exists, delete if it does
|
||||
if (fs.existsSync(binaryPath)) {
|
||||
fs.unlinkSync(binaryPath);
|
||||
}
|
||||
|
||||
// Check if zip file exists
|
||||
if (!fs.existsSync(zipPath)) {
|
||||
console.error(`❌ ${zipName} not found at: ${zipPath}`);
|
||||
console.error(`Current platform: ${platform}-${arch} (${platformDir})`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Clean out any previous extraction (but keep the zip)
|
||||
console.log("🧹 Cleaning up old files…");
|
||||
if (fs.existsSync(extractDir)) {
|
||||
fs.readdirSync(extractDir).forEach((name) => {
|
||||
if (name !== zipName) {
|
||||
fs.rmSync(path.join(extractDir, name), { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Unzip the file
|
||||
console.log("📦 Extracting vibe-kanban...");
|
||||
console.log(`📦 Extracting ${baseName}...`);
|
||||
if (platform === "win32") {
|
||||
// Use PowerShell on Windows
|
||||
execSync(`powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${extractDir}' -Force"`, { stdio: "inherit" });
|
||||
execSync(
|
||||
`powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${extractDir}' -Force"`,
|
||||
{ stdio: "inherit" }
|
||||
);
|
||||
} else {
|
||||
// Use unzip on Unix-like systems
|
||||
execSync(`unzip -o "${zipPath}" -d "${extractDir}"`, { stdio: "inherit" });
|
||||
}
|
||||
|
||||
// Find the extracted directory (should match the zip structure)
|
||||
const extractedDirs = fs.readdirSync(extractDir).filter(name =>
|
||||
name !== zipName && fs.statSync(path.join(extractDir, name)).isDirectory()
|
||||
);
|
||||
|
||||
if (extractedDirs.length === 0) {
|
||||
console.error("❌ No extracted directory found");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Execute the binary
|
||||
const binaryName = getBinaryName();
|
||||
const binaryPath = path.join(extractDir, extractedDirs[0], binaryName);
|
||||
|
||||
if (!fs.existsSync(binaryPath)) {
|
||||
console.error(`❌ Binary not found at: ${binaryPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`🚀 Launching vibe-kanban (${platformDir})...`);
|
||||
|
||||
console.log(`🚀 Launching ${baseName}...`);
|
||||
if (platform === "win32") {
|
||||
execSync(`"${binaryPath}"`, { stdio: "inherit" });
|
||||
} else {
|
||||
@@ -96,7 +158,4 @@ try {
|
||||
execSync(`chmod +x "${binaryPath}"`);
|
||||
execSync(`"${binaryPath}"`, { stdio: "inherit" });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Error running vibe-kanban:", error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
{
|
||||
"name": "vibe-kanban",
|
||||
"private": false,
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.24",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
"my-npx-cli": "bin/cli.js"
|
||||
"vibe-kanban": "bin/cli.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "bloop",
|
||||
"license": "",
|
||||
"description": "NPX wrapper around vibe-kanban",
|
||||
"description": "NPX wrapper around vibe-kanban and vibe-kanban-mcp",
|
||||
"files": [
|
||||
"dist"
|
||||
"dist",
|
||||
"bin"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user