// A temporary MCP client; does not modify any saved MCP client or REA configuration. import { pathToFileURL } from 'node:url'; import { resolve, join } from 'node:path'; import { mkdir, writeFile } from 'node:fs/promises'; import { createInterface } from 'node:readline'; const [packageRoot, hopperLauncher] = process.argv.slice(2); if (!packageRoot || !hopperLauncher) { throw new Error('Usage: node tools/rea-session.mjs REA_PACKAGE_ROOT HOPPER_LAUNCHER (the vendor hopper helper)'); } const dependencies = resolve(packageRoot, '..', '@modelcontextprotocol'); const { Client } = await import(pathToFileURL(join(dependencies, 'client/dist/index.mjs'))); const { StdioClientTransport } = await import(pathToFileURL(join(dependencies, 'client/dist/stdio.mjs'))); const transport = new StdioClientTransport({ command: join(packageRoot, '..', '.bin', 'rea'), args: ['--mcp'], env: { ...process.env, HOPPER_LAUNCHER_PATH: hopperLauncher, REA_HOPPER_STARTUP_TIMEOUT_MS: '600000' }, stderr: 'pipe', }); // Consume diagnostics without printing inherited environment or bridge credentials. transport.stderr?.on('data', () => {}); const client = new Client({ name: 'factorio-port-investigation', version: '0.1.0' }); const allowed = new Set(['open_binary', 'binary_session', 'list_documents', 'search_procedures', 'procedure_assembly', 'procedure_info', 'read_bytes', 'search_strings', 'read_function_instructions', 'procedure_callers', 'procedure_callees', 'procedure_pseudo_code', 'analyze_function', 'export_evidence_bundle', 'close_binary']); await mkdir('evidence', { recursive: true }); await client.connect(transport); console.log('REA MCP connected. Enter one JSON tool request per line; quit closes the target.'); const input = createInterface({ input: process.stdin, terminal: false }); let sequence = 0; try { for await (const line of input) { if (line.trim() === 'quit') break; try { const request = JSON.parse(line); if (!allowed.has(request.name)) throw new Error('Tool outside investigation allowlist'); console.log(`Calling ${request.name}`); const result = await client.callTool(request, { timeout: 660000 }); const file = `evidence/${Date.now()}-${++sequence}-${request.name}.json`; await writeFile(file, JSON.stringify(result, null, 2), { mode: 0o600 }); const data = result.structuredContent ?? result.content?.filter(x => x.type === 'text').map(x => { try { return JSON.parse(x.text); } catch { return x.text; } }); console.log(JSON.stringify({ file, isError: result.isError ?? false, data }).slice(0, 14000)); } catch (error) { console.log(JSON.stringify({ error: error.message })); } } } finally { try { await client.callTool({ name: 'close_binary', arguments: {} }); } finally { await client.close(); } }