UNPKG

@openmemory/install

Version:

OpenMemory MCP server installation tool

207 lines (200 loc) 7.23 kB
#!/usr/bin/env node // src/index.ts import yargs from "yargs"; import { hideBin } from "yargs/helpers"; // src/logger.ts import { createConsola } from "consola"; var logger = createConsola({}); var verbose = (msg) => logger.verbose(msg); // src/commands/install.ts import pc from "picocolors"; // src/client-config.ts import fs from "fs"; import os from "os"; import path from "path"; import process2 from "process"; var homeDir = os.homedir(); var platformPaths = { win32: { baseDir: process2.env.APPDATA || path.join(homeDir, "AppData", "Roaming"), vscodePath: path.join("Code", "User", "globalStorage") }, darwin: { baseDir: path.join(homeDir, "Library", "Application Support"), vscodePath: path.join("Code", "User", "globalStorage") }, linux: { baseDir: process2.env.XDG_CONFIG_HOME || path.join(homeDir, ".config"), vscodePath: path.join("Code/User/globalStorage") } }; var platform = process2.platform; var { baseDir, vscodePath } = platformPaths[platform]; var defaultClaudePath = path.join(baseDir, "Claude", "claude_desktop_config.json"); var clientPaths = { claude: { type: "file", path: defaultClaudePath }, cline: { type: "file", path: path.join(baseDir, vscodePath, "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json") }, roocline: { type: "file", path: path.join(baseDir, vscodePath, "rooveterinaryinc.roocline", "settings", "mcp_settings.json") }, windsurf: { type: "file", path: path.join(homeDir, ".codeium", "windsurf", "mcp_config.json") }, witsy: { type: "file", path: path.join(baseDir, "Witsy", "settings.json") }, enconvo: { type: "file", path: path.join(homeDir, ".config", "enconvo", "mcp_config.json") }, cursor: { type: "file", path: path.join(homeDir, ".cursor", "mcp.json"), localPath: path.join(process2.cwd(), ".cursor", "mcp.json") } }; var clientNames = Object.keys(clientPaths); function getConfigPath(client, local) { const normalizedClient = client?.toLowerCase() || "claude"; verbose(`Getting config path for client: ${normalizedClient}${local ? " (local)" : ""}`); const configTarget = clientPaths[normalizedClient]; if (!configTarget) { return { type: "file", path: path.join(path.dirname(defaultClaudePath), "..", client || "claude", `${normalizedClient}_config.json`) }; } if (local && configTarget.localPath) { verbose(`Using local config path for ${normalizedClient}: ${configTarget.localPath}`); return { ...configTarget, path: configTarget.localPath }; } verbose(`Using default config path for ${normalizedClient}: ${configTarget.path}`); return configTarget; } function readConfig(client, local) { verbose(`Reading config for client: ${client}${local ? " (local)" : ""}`); try { const configPath = getConfigPath(client, local); verbose(`Checking if config file exists at: ${configPath.path}`); if (!fs.existsSync(configPath.path)) { verbose("Config file not found, returning default empty config"); return { mcpServers: {} }; } verbose("Reading config file content"); const rawConfig = JSON.parse(fs.readFileSync(configPath.path, "utf8")); verbose(`Config loaded successfully: ${JSON.stringify(rawConfig, null, 2)}`); return { ...rawConfig, mcpServers: rawConfig.mcpServers || {} }; } catch (error) { verbose(`Error reading config: ${error instanceof Error ? error.stack : JSON.stringify(error)}`); return { mcpServers: {} }; } } function writeConfig(config, client, local) { verbose(`Writing config for client: ${client || "default"}${local ? " (local)" : ""}`); verbose(`Config data: ${JSON.stringify(config, null, 2)}`); if (!config.mcpServers || typeof config.mcpServers !== "object") { verbose("Invalid mcpServers structure in config"); throw new Error("Invalid mcpServers structure"); } const configPath = getConfigPath(client, local); writeConfigFile(config, configPath); } function writeConfigFile(config, target) { const configDir = path.dirname(target.path); verbose(`Ensuring config directory exists: ${configDir}`); if (!fs.existsSync(configDir)) { verbose(`Creating directory: ${configDir}`); fs.mkdirSync(configDir, { recursive: true }); } let existingConfig = { mcpServers: {} }; try { if (fs.existsSync(target.path)) { verbose("Reading existing config file for merging"); existingConfig = JSON.parse(fs.readFileSync(target.path, "utf8")); verbose(`Existing config loaded: ${JSON.stringify(existingConfig, null, 2)}`); } } catch (error) { verbose(`Error reading existing config for merge: ${error instanceof Error ? error.message : String(error)}`); } verbose("Merging configs"); const mergedConfig = { ...existingConfig, ...config }; verbose(`Merged config: ${JSON.stringify(mergedConfig, null, 2)}`); verbose(`Writing config to file: ${target.path}`); fs.writeFileSync(target.path, JSON.stringify(mergedConfig, null, 2)); verbose("Config successfully written"); } // src/commands/install.ts var { green, red } = pc; function builder(yargs2) { return yargs2.option("name", { type: "string", description: 'Name of the server (defaults to "openmemory")', default: "openmemory" }).option("client", { type: "string", description: "Client to use for installation", demandOption: true, choices: clientNames }).option("env", { type: "string", description: "Environment variables as key=value pairs (can be used multiple times). OPENMEMORY_API_KEY is required.", array: true }); } async function handler(argv2) { if (!argv2.client || !clientNames.includes(argv2.client)) { logger.error(`Invalid client: ${argv2.client}. Available clients: ${clientNames.join(", ")}`); return; } const envVars = {}; if (argv2.env && argv2.env.length > 0) { for (const envVar of argv2.env) { const [key, ...valueParts] = envVar.split("="); if (key && valueParts.length > 0) { envVars[key] = valueParts.join("="); } else { logger.warn(`Invalid environment variable format: ${envVar}. Expected KEY=VALUE format.`); } } } if (!envVars.OPENMEMORY_API_KEY) { logger.error("OPENMEMORY_API_KEY environment variable is required. Use --env OPENMEMORY_API_KEY=your_key"); return; } envVars.CLIENT_NAME = argv2.client; const name = argv2.name || "openmemory"; const ready = await logger.prompt( green(`Are you ready to install OpenMemory MCP server in ${argv2.client}?`), { type: "confirm" } ); if (ready) { try { const config = readConfig(argv2.client); config.mcpServers[name] = { command: "npx", args: ["-y", "openmemory"], env: envVars }; writeConfig(config, argv2.client); logger.box( green(`Successfully installed OpenMemory MCP server in ${argv2.client}.`) ); } catch (e) { logger.error(red(e.message)); } } } // src/index.ts var argv = builder(yargs(hideBin(process.argv))).scriptName("@openmemory/install").usage("$0 [options]").help().alias("h", "help").version().alias("v", "version").parseSync(); handler(argv);