#!/usr/bin/env node /** * Memvid MCP Search Helper * * Usage: node memvid-search.js [top_k] [snippet_chars] * * This script calls the Memvid MCP server to perform semantic search. */ const { spawn } = require('child_process'); const path = require('path'); const fs = require('fs'); // Parse arguments const args = process.argv.slice(2); if (args.length < 2) { console.error('Usage: node memvid-search.js [top_k] [snippet_chars]'); process.exit(1); } const memoryFilePath = args[0]; const query = args[1]; const topK = parseInt(args[2]) || 3; const snippetChars = parseInt(args[3]) || 500; // Validate memory file exists if (!fs.existsSync(memoryFilePath)) { console.error(JSON.stringify({ error: `Memory file not found: ${memoryFilePath}` })); process.exit(1); } // MCP configuration (from .claude/mcp.json) const mcpCommand = 'uv'; const mcpArgs = [ 'run', '--directory', 'D:\\works\\projects-windows\\memvid-agent-mcp', 'memvid_mcp_server.py' ]; const mcpEnv = { ...process.env, PYTHONUNBUFFERED: '1', MEMVID_LOG_LEVEL: 'ERROR' // Reduce noise }; // JSON-RPC communication let messageId = 1; function createRequest(method, params = {}) { return { jsonrpc: '2.0', id: messageId++, method, params }; } function sendRequest(proc, request) { const message = JSON.stringify(request) + '\n'; proc.stdin.write(message); } // Main execution async function main() { return new Promise((resolve, reject) => { const proc = spawn(mcpCommand, mcpArgs, { env: mcpEnv, stdio: ['pipe', 'pipe', 'pipe'] }); let outputBuffer = ''; let errorBuffer = ''; let initialized = false; let searchRequestSent = false; let timeoutId = null; function cleanup() { if (timeoutId) clearTimeout(timeoutId); proc.kill(); } proc.stdout.on('data', (data) => { outputBuffer += data.toString(); // Process complete JSON-RPC messages const lines = outputBuffer.split('\n'); outputBuffer = lines.pop() || ''; // Keep incomplete line for (const line of lines) { if (!line.trim()) continue; try { const response = JSON.parse(line); // Handle initialization if (!initialized && response.id === 1) { initialized = true; // Send search request const searchRequest = createRequest('tools/call', { name: 'memvid_search', arguments: { file_path: memoryFilePath, query: query, top_k: topK, snippet_chars: snippetChars } }); sendRequest(proc, searchRequest); searchRequestSent = true; } // Handle search result else if (searchRequestSent && response.result) { console.log(JSON.stringify({ success: true, result: response.result })); cleanup(); setTimeout(() => process.exit(0), 100); resolve(); } // Handle errors else if (response.error) { console.error(JSON.stringify({ error: response.error.message || 'MCP error' })); cleanup(); setTimeout(() => process.exit(1), 100); reject(new Error(response.error.message)); } } catch (e) { // Ignore non-JSON lines (logs, etc.) } } }); proc.stderr.on('data', (data) => { errorBuffer += data.toString(); }); proc.on('error', (error) => { console.error(JSON.stringify({ error: error.message })); cleanup(); reject(error); }); proc.on('exit', (code) => { if (code !== 0 && code !== null) { console.error(JSON.stringify({ error: `MCP server exited with code ${code}`, stderr: errorBuffer })); reject(new Error(`Process exited with code ${code}`)); } }); // Initialize MCP connection const initRequest = createRequest('initialize', { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'claudian-memvid-helper', version: '1.0.0' } }); sendRequest(proc, initRequest); // Timeout after 10 seconds timeoutId = setTimeout(() => { proc.kill(); console.error(JSON.stringify({ error: 'Search timeout' })); reject(new Error('Timeout')); }, 10000); }); } main().catch((error) => { process.exit(1); });