@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
JavaScript
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
;