Files
my-vault/.claude/scripts/memvid-search.cjs
T

186 lines
4.5 KiB
JavaScript

#!/usr/bin/env node
/**
* Memvid MCP Search Helper
*
* Usage: node memvid-search.js <memory_file_path> <query> [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 <memory_file_path> <query> [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);
});