@openmemory/install
Version:
OpenMemory MCP server installation tool
207 lines (200 loc) • 7.23 kB
JavaScript
// 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);