UNPKG

@sigyl-dev/cli

Version:

Official Sigyl CLI for installing and managing MCP packages. Zero-config installation for public packages, secure API-based authentication.

269 lines • 12.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createInstallCommand = createInstallCommand; const commander_1 = require("commander"); const chalk_1 = __importDefault(require("chalk")); const claude_config_1 = require("../lib/claude-config"); const config_1 = require("../lib/config"); // Fetch package information from the registry API async function resolveRemoteMCPServer(pkgName, apiKey, profile) { const config = (0, config_1.getRegistryConfig)(); try { console.log(chalk_1.default.gray(`šŸ” Looking up package: ${pkgName}`)); // Construct the API URL const apiUrl = `${config.registryUrl}/api/v1/packages/${pkgName}`; // Prepare headers const headers = { 'Content-Type': 'application/json' }; // Add API key if available if (config.apiKey) { headers['Authorization'] = `Bearer ${config.apiKey}`; } // Make the API request const response = await fetch(apiUrl, { headers }); if (!response.ok) { if (response.status === 404) { console.error(chalk_1.default.red(`āŒ Package '${pkgName}' not found in the registry.`)); console.log(chalk_1.default.yellow(`šŸ’” Check available packages at https://sigyl.dev/registry`)); } else if (response.status === 401) { console.error(chalk_1.default.red(`āŒ Authentication failed. Invalid API key.`)); console.log(chalk_1.default.yellow(`šŸ’” Get your API key from https://sigyl.dev/dashboard`)); console.log(chalk_1.default.yellow(`šŸ’” Or run 'sigyl config' to set it up`)); } else { console.error(chalk_1.default.red(`āŒ Failed to fetch package information (${response.status})`)); } process.exit(1); } const data = await response.json(); if (!data.success || !data.data) { console.error(chalk_1.default.red(`āŒ Invalid response from registry API`)); process.exit(1); } const packageInfo = data.data; if (!packageInfo.source_api_url) { console.error(chalk_1.default.red(`āŒ Package '${pkgName}' does not have a valid source API URL`)); process.exit(1); } console.log(chalk_1.default.green(`āœ… Found package: ${packageInfo.name}`)); if (packageInfo.description) { console.log(chalk_1.default.gray(` ${packageInfo.description}`)); } return { url: packageInfo.source_api_url, api_key: apiKey || 'demo-key', profile: profile || 'default', name: packageInfo.name, slug: pkgName, packageInfo }; } catch (error) { console.error(chalk_1.default.red(`āŒ Failed to connect to registry API:`), error instanceof Error ? error.message : String(error)); console.log(chalk_1.default.yellow(`šŸ’” Check your internet connection and try again`)); console.log(chalk_1.default.yellow(`šŸ’” Registry URL: ${config.registryUrl}`)); process.exit(1); } } const SUPPORTED_CLIENTS = ["claude", "cursor", "vscode"]; function createInstallCommand() { return new commander_1.Command("install") .description("Install a remote MCP server in a desktop client (claude, cursor, vscode). Default: claude. Accepts a package name or a full MCP endpoint URL.") .argument("<target>", "MCP server package name (e.g. kazumah1/mcp-test) or full MCP endpoint URL") .option("-n, --name <name>", "Custom name for the server in the client") .option("-l, --list", "List currently installed MCP servers") .option("-r, --remove <name>", "Remove an MCP server from the client") .option("--client <client>", "Target client: claude, cursor, vscode (default: claude)") .option("--env <key=value>", "Environment variables (can be used multiple times)", []) .option("--cwd <path>", "Working directory for the server") .option("--key <key>", "API key for the MCP server") .action(async (target, options) => { try { const client = (options.client || "claude").toLowerCase(); if (!SUPPORTED_CLIENTS.includes(client)) { console.error(chalk_1.default.red(`\u274c Unsupported client: ${client}. Supported clients: ${SUPPORTED_CLIENTS.join(", ")}`)); process.exit(1); } if (options.list) { // For now, only list for claude if (client === "claude") { (0, claude_config_1.listMCPServers)(); } else { console.log(chalk_1.default.yellow(`Listing is not yet implemented for client: ${client}`)); } return; } if (options.remove) { // For now, only remove for claude if (client === "claude") { const success = (0, claude_config_1.removeMCPServer)(options.remove); process.exit(success ? 0 : 1); } else { console.log(chalk_1.default.yellow(`Remove is not yet implemented for client: ${client}`)); process.exit(1); } } let remote; if (target.startsWith('http://') || target.startsWith('https://')) { // Use the URL directly // Extract repo name from URL (last part after '/') let repoName = target; try { const urlObj = new URL(target); const pathParts = urlObj.pathname.split('/').filter(Boolean); // If path is /@user/repo/mcp, get the second-to-last part (repo) if (pathParts.length >= 2) { repoName = pathParts[pathParts.length - 2]; } else { repoName = pathParts[pathParts.length - 1] || target; } } catch { repoName = target; } remote = { url: target, api_key: options.key || 'demo-key', profile: options.profile || 'default', name: options.name || repoName, slug: target, packageInfo: { source_api_url: target } }; } else { // Resolve remote MCP server details from the registry API remote = await resolveRemoteMCPServer(target, options.key /*, options.profile*/); } await installRemoteServer(remote, options, client); } catch (error) { console.error(chalk_1.default.red("\u274c Install command failed:"), error); process.exit(1); } }); } // Install remote MCP server for client async function installRemoteServer(remote, options, client) { const serverName = options.name || remote.name; const fs = require("fs"); const os = require("os"); const path = require("path"); const { getRegistryConfig } = require("../lib/config"); console.log(chalk_1.default.blue(`šŸ“¦ Installing ${remote.name} for ${client}...`)); if (client === "claude") { // Use API key from config unless overridden const configApiKey = getRegistryConfig().apiKey; const apiKeyToUse = options.key || configApiKey; if (!apiKeyToUse) { console.error(chalk_1.default.red("\u274c No API key found. Please run 'sigyl config' or provide --key.")); process.exit(1); } const configPath = path.join(os.homedir(), "Library/Application Support/Claude/claude_desktop_config.json"); let config = { mcpServers: {} }; if (fs.existsSync(configPath)) { try { config = JSON.parse(fs.readFileSync(configPath, "utf8")); } catch { } } let mcpServers = config.mcpServers || {}; // Use package name or URL as key const runTarget = remote.url || remote.slug; mcpServers[serverName] = { command: "npx", args: [ "-y", "@sigyl-dev/cli@latest", "run", runTarget, "--key", apiKeyToUse ] }; config.mcpServers = mcpServers; fs.mkdirSync(path.dirname(configPath), { recursive: true }); fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); console.log(chalk_1.default.green(`\u2705 Installed '${serverName}' for Claude Desktop.`)); console.log(chalk_1.default.yellow("\nšŸ”„ Please restart Claude Desktop to load the new server.")); return; } if (client === "vscode") { // Use API key from config unless overridden const configApiKey = getRegistryConfig().apiKey; const apiKeyToUse = options.key || configApiKey; if (!apiKeyToUse) { console.error(chalk_1.default.red("āŒ No API key found. Please run 'sigyl config' or provide --key.")); process.exit(1); } // Write config file for VS Code (mcpServers field) using 'command' and 'args' const configPath = path.join(os.homedir(), ".vscode", "mcp_servers.json"); let config = { mcpServers: {} }; if (fs.existsSync(configPath)) { try { config = JSON.parse(fs.readFileSync(configPath, "utf8")); } catch { } } let mcpServers = config.mcpServers || {}; // Use package name as key, but slug for run command mcpServers[serverName] = { command: "npx", args: [ "-y", "@sigyl-dev/cli@latest", "run", remote.slug, "--key", apiKeyToUse ] }; config.mcpServers = mcpServers; fs.mkdirSync(path.dirname(configPath), { recursive: true }); fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); console.log(chalk_1.default.green(`\nšŸŽ‰ Installed remote MCP server '${serverName}' for VS Code!`)); console.log(chalk_1.default.yellow("\nšŸ”„ Please restart VS Code to load the new server.")); return; } if (client === "cursor") { // Use API key from config unless overridden const configApiKey = getRegistryConfig().apiKey; const apiKeyToUse = options.key || configApiKey; if (!apiKeyToUse) { console.error(chalk_1.default.red("āŒ No API key found. Please run 'sigyl config' or provide --key.")); process.exit(1); } // Write config file for Cursor (mcp.json, flat object) const configPath = path.join(os.homedir(), ".cursor", "mcp.json"); let config = {}; if (fs.existsSync(configPath)) { try { config = JSON.parse(fs.readFileSync(configPath, "utf8")); } catch { } } // Use package name as key, value is the MCP endpoint URL with apiKey param let mcpUrl = remote.url || remote.slug; // Add apiKey as query param try { const urlObj = new URL(mcpUrl); urlObj.searchParams.set("apiKey", apiKeyToUse); mcpUrl = urlObj.toString(); } catch { } config[serverName] = mcpUrl; fs.mkdirSync(path.dirname(configPath), { recursive: true }); fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); console.log(chalk_1.default.green(`\nšŸŽ‰ Installed remote MCP server '${serverName}' for Cursor (mcp.json)!`)); console.log(chalk_1.default.yellow("\nšŸ”„ Please restart Cursor to load the new server.")); return; } } //# sourceMappingURL=install.js.map