#!/usr/bin/env node
import { Command } from "commander";
import open from "open";
import { createServer } from "http";
import { getApiKey, getBaseUrl, isAuthenticated, saveConfig } from "./config.js";
import { verifyApiKey } from "./api.js";
const program = new Command();
program
.name("nexus-mcp")
.description("MCP server for Nexus - AI-assisted Project Delivery Platform")
.version("0.1.0");
program
.command("login")
.description("Authenticate with Nexus")
.option("--no-browser", "Don't open browser automatically")
.action(async (options) => {
console.log("Starting authentication flow...\n");
// Create a temporary server to receive the callback
const server = createServer((req, res) => {
const url = new URL(req.url || "", `http://localhost`);
if (url.pathname === "/callback") {
const apiKey = url.searchParams.get("key");
const error = url.searchParams.get("error");
if (error) {
res.writeHead(400, { "Content-Type": "text/html" });
res.end(`
<html>
<body style="font-family: system-ui; padding: 40px; text-align: center;">
<h1 style="color: #dc2626;">Authentication Failed</h1>
<p>${error}</p>
<p>You can close this window.</p>
</body>
</html>
`);
console.error(`\nAuthentication failed: ${error}`);
server.close();
process.exit(1);
}
if (apiKey) {
saveConfig({ apiKey });
res.writeHead(200, { "Content-Type": "text/html" });
res.end(`
<html>
<body style="font-family: system-ui; padding: 40px; text-align: center;">
<h1 style="color: #16a34a;">Authentication Successful!</h1>
<p>You can close this window and return to your terminal.</p>
</body>
</html>
`);
console.log("\n✓ Authentication successful!");
console.log(" API key saved to ~/.nexus-mcp/config.json\n");
console.log("You can now use the MCP server with Claude Code:");
console.log(" claude mcp add nexus -- npx @nexus/mcp-server\n");
server.close();
process.exit(0);
}
}
res.writeHead(404);
res.end("Not found");
});
// Find an available port
server.listen(0, "127.0.0.1", () => {
const address = server.address();
if (!address || typeof address === "string") {
console.error("Failed to start callback server");
process.exit(1);
}
const callbackUrl = `http://127.0.0.1:${address.port}/callback`;
const baseUrl = getBaseUrl();
const authUrl = `${baseUrl}/auth/mcp?callback=${encodeURIComponent(callbackUrl)}`;
console.log("Opening browser for authentication...");
console.log(`\nIf the browser doesn't open, visit:\n${authUrl}\n`);
if (options.browser !== false) {
open(authUrl).catch(() => {
console.log("Failed to open browser automatically.");
});
}
console.log("Waiting for authentication...");
});
// Timeout after 5 minutes
setTimeout(() => {
console.error("\nAuthentication timed out.");
server.close();
process.exit(1);
}, 5 * 60 * 1000);
});
program
.command("auth")
.description("Set API key manually")
.requiredOption("--key <key>", "Nexus API key")
.action(async (options) => {
saveConfig({ apiKey: options.key });
console.log("API key saved to ~/.nexus-mcp/config.json");
try {
const result = await verifyApiKey();
if (result.valid) {
console.log(`\n✓ Authenticated as ${result.user?.email}`);
console.log(` Organization: ${result.org?.name}\n`);
} else {
console.error("\n✗ Invalid API key");
process.exit(1);
}
} catch (error) {
console.error(`\n✗ Failed to verify API key: ${error}`);
process.exit(1);
}
});
program
.command("status")
.description("Check authentication status")
.action(async () => {
if (!isAuthenticated()) {
console.log("Not authenticated. Run: nexus-mcp login");
process.exit(1);
}
try {
const result = await verifyApiKey();
if (result.valid) {
console.log("✓ Authenticated");
console.log(` User: ${result.user?.name} (${result.user?.email})`);
console.log(` Organization: ${result.org?.name}`);
console.log(` API URL: ${getBaseUrl()}`);
} else {
console.log("✗ API key is invalid or expired");
console.log(" Run: nexus-mcp login");
process.exit(1);
}
} catch (error) {
console.error(`✗ Failed to verify: ${error}`);
process.exit(1);
}
});
program
.command("logout")
.description("Remove stored credentials")
.action(() => {
saveConfig({ apiKey: undefined });
console.log("Logged out. API key removed.");
});
program
.command("config")
.description("Configure Nexus MCP")
.option("--url <url>", "Set Nexus API URL")
.option("--org <orgId>", "Set default organization ID")
.action((options) => {
if (options.url) {
saveConfig({ baseUrl: options.url });
console.log(`API URL set to: ${options.url}`);
}
if (options.org) {
saveConfig({ orgId: options.org });
console.log(`Default org set to: ${options.org}`);
}
if (!options.url && !options.org) {
const apiKey = getApiKey();
console.log("Current configuration:");
console.log(` API URL: ${getBaseUrl()}`);
console.log(` API Key: ${apiKey ? apiKey.slice(0, 12) + "..." : "(not set)"}`);
}
});
program
.command("serve", { isDefault: true })
.description("Start the MCP server (default command)")
.action(async () => {
// Import and run the server
const { startServer } = await import("./index.js");
await startServer();
});
program.parse();