mcp-memory-ts
Version:
Cloud-based vector MCP memory service for Claude.ai - TypeScript implementation
336 lines (335 loc) • 12.7 kB
JavaScript
/**
* Claude Desktop Integration CLI
* Manages MCP server installation and configuration for Claude Desktop
*/
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
import { homedir } from 'os';
import { join, resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
import { execSync } from 'child_process';
import { colors, icons, success, error, warning, info, section, keyValue } from './colors.js';
// ESM-compatible __dirname replacement
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const CONFIG_DIR = join(homedir(), '.mcp-memory');
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
const CLAUDE_DESKTOP_CONFIG = join(homedir(), 'Library/Application Support/Claude/claude_desktop_config.json');
const BACKUP_SUFFIX = '.backup';
/**
* Load user configuration from ~/.mcp-memory/config.json
*/
export function loadUserConfig() {
if (!existsSync(CONFIG_FILE)) {
return null;
}
try {
const data = readFileSync(CONFIG_FILE, 'utf8');
return JSON.parse(data);
}
catch (err) {
console.error(error(`Error reading config file: ${err}`));
return null;
}
}
/**
* Load user configuration and set environment variables
* This should be called early in CLI startup to make config available to all commands
*/
export function loadConfigToEnv() {
const config = loadUserConfig();
if (!config) {
// No config file found - this is OK, user might be using .env or system env vars
return;
}
// Set environment variables from config if not already set
// Existing env vars take precedence over config file
if (!process.env.TURSO_URL && config.tursoUrl) {
process.env.TURSO_URL = config.tursoUrl;
}
if (!process.env.TURSO_AUTH_TOKEN && config.tursoAuthToken) {
process.env.TURSO_AUTH_TOKEN = config.tursoAuthToken;
}
if (!process.env.OPENAI_API_KEY && config.openaiApiKey) {
process.env.OPENAI_API_KEY = config.openaiApiKey;
}
if (!process.env.DEFAULT_USER_EMAIL && config.userEmail) {
process.env.DEFAULT_USER_EMAIL = config.userEmail;
}
}
/**
* Save user configuration to ~/.mcp-memory/config.json
*/
export function saveUserConfig(config) {
try {
if (!existsSync(CONFIG_DIR)) {
mkdirSync(CONFIG_DIR, { recursive: true });
}
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf8');
console.log(success(`Configuration saved to ${CONFIG_FILE}`));
return true;
}
catch (err) {
console.error(error(`Error saving config: ${err}`));
return false;
}
}
/**
* Load Claude Desktop configuration
*/
function loadClaudeDesktopConfig() {
if (!existsSync(CLAUDE_DESKTOP_CONFIG)) {
console.log(warning('Claude Desktop config not found'));
return null;
}
try {
const data = readFileSync(CLAUDE_DESKTOP_CONFIG, 'utf8');
return JSON.parse(data);
}
catch (err) {
console.error(error(`Error reading Claude Desktop config: ${err}`));
return null;
}
}
/**
* Save Claude Desktop configuration with backup
*/
function saveClaudeDesktopConfig(config) {
try {
// Create backup of existing config
if (existsSync(CLAUDE_DESKTOP_CONFIG)) {
const backupPath = CLAUDE_DESKTOP_CONFIG + BACKUP_SUFFIX;
const existing = readFileSync(CLAUDE_DESKTOP_CONFIG, 'utf8');
writeFileSync(backupPath, existing, 'utf8');
console.log(info(`Backed up existing config to ${backupPath}`));
}
// Write new config
writeFileSync(CLAUDE_DESKTOP_CONFIG, JSON.stringify(config, null, 2), 'utf8');
console.log(success('Claude Desktop config updated'));
return true;
}
catch (err) {
console.error(error(`Error saving Claude Desktop config: ${err}`));
return false;
}
}
/**
* Get the path to the CLI binary
* Returns the path to the mcp-memory executable
*/
function getCliPath() {
// Try global npm installation first
try {
const npmBin = execSync('npm bin -g', { encoding: 'utf-8' }).trim();
const globalPath = join(npmBin, 'mcp-memory');
if (existsSync(globalPath)) {
return globalPath;
}
}
catch {
// Fall back to local development path if npm command fails
}
// For local development - check if we can use npx or local node_modules/.bin
const projectRoot = resolve(__dirname, '../..');
const localBinPath = join(projectRoot, 'node_modules', '.bin', 'mcp-memory');
if (existsSync(localBinPath)) {
return localBinPath;
}
// Last resort: try to use mcp-memory from PATH
try {
const whichResult = execSync('which mcp-memory', { encoding: 'utf-8' }).trim();
if (whichResult && existsSync(whichResult)) {
return whichResult;
}
}
catch {
// which command failed, continue to error
}
throw new Error(`mcp-memory CLI not found. Tried:
- Global: $(npm bin -g)/mcp-memory
- Local: ${localBinPath}
- PATH: which mcp-memory
Run 'npm run build-full && npm link' first, or install globally with 'npm install -g mcp-memory-ts'`);
}
/**
* Install MCP memory server in Claude Desktop
*/
export async function installClaudeDesktop() {
console.log(colors.title(`\n${icons.gear} Installing MCP Memory Server for Claude Desktop...\n`));
// Load user config
const userConfig = loadUserConfig();
if (!userConfig) {
console.error(error('User configuration not found. Run "mcp-memory init" first.'));
process.exit(1);
}
// Get CLI path
let cliPath;
try {
cliPath = getCliPath();
}
catch (err) {
console.error(error(`${err}`));
process.exit(1);
}
// Load or create Claude Desktop config
let claudeConfig = loadClaudeDesktopConfig();
if (!claudeConfig) {
claudeConfig = { mcpServers: {} };
}
// Add/update mcp-memory-ts entry using "mcp-memory server" command
claudeConfig.mcpServers['mcp-memory-ts'] = {
command: cliPath,
args: ['server'],
env: {
TURSO_URL: userConfig.tursoUrl,
TURSO_AUTH_TOKEN: userConfig.tursoAuthToken,
OPENAI_API_KEY: userConfig.openaiApiKey,
DEFAULT_USER_EMAIL: userConfig.userEmail,
LOG_LEVEL: 'INFO',
},
};
// Save config
if (saveClaudeDesktopConfig(claudeConfig)) {
console.log(success('\nMCP Memory Server installed successfully!'));
console.log(section(`${icons.pencil} Configuration:`));
console.log(` ${keyValue('User', userConfig.userEmail)}`);
console.log(` ${keyValue('Command', `${cliPath} server`)}`);
console.log(` ${keyValue('Database', userConfig.tursoUrl)}`);
console.log(warning('\nPlease restart Claude Desktop for changes to take effect.'));
console.log('');
}
else {
process.exit(1);
}
}
/**
* Update MCP memory server configuration
*/
export async function updateClaudeDesktop() {
console.log(colors.title(`\n${icons.cycle} Updating MCP Memory Server configuration...\n`));
// Load user config
const userConfig = loadUserConfig();
if (!userConfig) {
console.error(error('User configuration not found. Run "mcp-memory init" first.'));
process.exit(1);
}
// Load Claude Desktop config
const claudeConfig = loadClaudeDesktopConfig();
if (!claudeConfig || !claudeConfig.mcpServers['mcp-memory-ts']) {
console.error(error('MCP Memory Server not installed. Run "mcp-memory install" first.'));
process.exit(1);
}
// Update CLI path and ensure using "server" command
try {
const cliPath = getCliPath();
claudeConfig.mcpServers['mcp-memory-ts'].command = cliPath;
claudeConfig.mcpServers['mcp-memory-ts'].args = ['server'];
}
catch (err) {
console.error(error(`${err}`));
process.exit(1);
}
// Update environment variables
claudeConfig.mcpServers['mcp-memory-ts'].env = {
TURSO_URL: userConfig.tursoUrl,
TURSO_AUTH_TOKEN: userConfig.tursoAuthToken,
OPENAI_API_KEY: userConfig.openaiApiKey,
DEFAULT_USER_EMAIL: userConfig.userEmail,
LOG_LEVEL: 'INFO',
};
// Save config
if (saveClaudeDesktopConfig(claudeConfig)) {
console.log(success('\nConfiguration updated successfully!'));
console.log(warning('\nPlease restart Claude Desktop for changes to take effect.'));
console.log('');
}
else {
process.exit(1);
}
}
/**
* Check MCP memory server status
*/
export async function statusClaudeDesktop() {
console.log(section(`${icons.database} MCP Memory Server Status\n`));
// Check user config
const userConfig = loadUserConfig();
if (!userConfig) {
console.log(error('User configuration: Not found'));
console.log(` ${colors.dim('Run')} ${colors.command('mcp-memory init')} ${colors.dim('to set up configuration')}`);
console.log('');
return;
}
console.log(success('User configuration: Found'));
console.log(` ${keyValue('User', userConfig.userEmail)}`);
// Check Claude Desktop config
const claudeConfig = loadClaudeDesktopConfig();
if (!claudeConfig) {
console.log(error('Claude Desktop config: Not found'));
console.log(` ${colors.dim('Run')} ${colors.command('mcp-memory install')} ${colors.dim('to set up')}`);
console.log('');
return;
}
// Check if mcp-memory-ts is installed
const mcpConfig = claudeConfig.mcpServers['mcp-memory-ts'];
if (!mcpConfig) {
console.log(error('MCP Memory Server: Not installed in Claude Desktop'));
console.log(` ${colors.dim('Run')} ${colors.command('mcp-memory install')} ${colors.dim('to set up')}`);
console.log('');
return;
}
console.log(success('MCP Memory Server: Installed in Claude Desktop'));
console.log(` ${keyValue('Command', mcpConfig.command)}`);
console.log(` ${keyValue('Server', mcpConfig.args[0])}`);
// Check if server file exists
const serverPath = mcpConfig.args[0];
if (existsSync(serverPath)) {
console.log(success('Server file: Found'));
}
else {
console.log(error('Server file: Not found'));
console.log(` ${keyValue('Expected at', serverPath)}`);
console.log(` ${colors.dim('Run')} ${colors.command('npm run build')} ${colors.dim('to compile the server')}`);
}
// Check environment variables
if (mcpConfig.env) {
console.log(section(`${icons.pencil} Environment variables:`));
console.log(` ${keyValue('TURSO_URL', mcpConfig.env.TURSO_URL ? colors.success('Set') : colors.error('Not set'))}`);
console.log(` ${keyValue('TURSO_AUTH_TOKEN', mcpConfig.env.TURSO_AUTH_TOKEN ? colors.success('Set') : colors.error('Not set'))}`);
console.log(` ${keyValue('OPENAI_API_KEY', mcpConfig.env.OPENAI_API_KEY ? colors.success('Set') : colors.error('Not set'))}`);
console.log(` ${keyValue('DEFAULT_USER_EMAIL', mcpConfig.env.DEFAULT_USER_EMAIL || colors.dim('Not set'))}`);
}
console.log(`\n${icons.bulb} ${colors.dim('To apply changes, restart Claude Desktop')}`);
console.log('');
}
/**
* Uninstall MCP memory server from Claude Desktop
*/
export async function uninstallClaudeDesktop() {
console.log(colors.title(`\n${icons.trash} Uninstalling MCP Memory Server from Claude Desktop...\n`));
// Load Claude Desktop config
const claudeConfig = loadClaudeDesktopConfig();
if (!claudeConfig) {
console.log(warning('Claude Desktop config not found. Nothing to uninstall.'));
console.log('');
return;
}
// Check if mcp-memory-ts is installed
if (!claudeConfig.mcpServers['mcp-memory-ts']) {
console.log(warning('MCP Memory Server not installed. Nothing to uninstall.'));
console.log('');
return;
}
// Remove mcp-memory-ts entry
delete claudeConfig.mcpServers['mcp-memory-ts'];
// Save config
if (saveClaudeDesktopConfig(claudeConfig)) {
console.log(success('\nMCP Memory Server uninstalled successfully!'));
console.log(warning('\nPlease restart Claude Desktop for changes to take effect.'));
console.log(`\n${icons.bulb} ${colors.dim('User configuration in ~/.mcp-memory/ has been preserved.')}`);
console.log('');
}
else {
process.exit(1);
}
}
//# sourceMappingURL=claude-desktop.js.map