26382a7ac6
CI / Clippy (push) Failing after 15m13s
CI / Test (ubuntu-latest) (push) Failing after 16m1s
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Build (no embeddings / no ORT) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Cookbook (Node) (push) Has been cancelled
CI / Pi Extension (Node) (push) Has been cancelled
CI / Rust SDK (lean-ctx-client) (push) Has been cancelled
CI / Embed SDK (lean-ctx-sdk) (push) Has been cancelled
CI / Python SDK (leanctx) (push) Has been cancelled
CI / Hermes Plugin (Python) (push) Has been cancelled
CI / SDK Conformance Matrix (push) Has been cancelled
CI / Coverage (push) Has been cancelled
CI / cargo-deny (push) Has been cancelled
CI / Adversarial Safety (push) Has been cancelled
CI / Benchmarks (push) Has been cancelled
CI / Output-Quality Gate (eval A/B) (push) Has been cancelled
CI / Documentation (push) Has been cancelled
CI / CI Green (push) Has been cancelled
JetBrains Plugin / Actionlint (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (rust) (push) Has been cancelled
JetBrains Plugin / Validation (push) Has been cancelled
JetBrains Plugin / Build (push) Has been cancelled
JetBrains Plugin / Test (push) Has been cancelled
Security Check / Security Scan (push) Has been cancelled
91 lines
3.7 KiB
JavaScript
91 lines
3.7 KiB
JavaScript
#!/usr/bin/env node
|
||
// MCP stdio fixture for the deeper-addon-integration E2E (#1102).
|
||
//
|
||
// A *real* MCP stdio server (newline-delimited JSON-RPC 2.0) whose tools return
|
||
// the output shapes the L1–L4 pipeline consumes, so the gateway's production
|
||
// proxy → postprocess → adapters path is exercised end to end against a spawned
|
||
// child process (no protocol mocks). Tools:
|
||
// echo -> `echo:<text>` (drives L1/L2/L3 + secret redaction)
|
||
// pack_codebase -> repomix-shaped JSON {outputId, directoryStructure, …}
|
||
// query_graph -> code-graph JSON {edges:[{from,to,type}]}
|
||
// search_memories -> memory JSON {results:[{memory,id,score}]}
|
||
// compress_text -> `compressed:<text>` (drives the compression adapter)
|
||
// Only Node.js is required.
|
||
|
||
import { createInterface } from 'node:readline';
|
||
|
||
const rl = createInterface({ input: process.stdin });
|
||
const send = (msg) => process.stdout.write(JSON.stringify(msg) + '\n');
|
||
const textResult = (id, text) =>
|
||
send({ jsonrpc: '2.0', id, result: { content: [{ type: 'text', text }], isError: false } });
|
||
|
||
const TOOLS = [
|
||
{ name: 'echo', description: 'Echo back the provided text',
|
||
inputSchema: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] } },
|
||
{ name: 'pack_codebase', description: 'Pack a repository (repomix-shaped)',
|
||
inputSchema: { type: 'object', properties: { directory: { type: 'string' } } } },
|
||
{ name: 'query_graph', description: 'Return code-graph edges',
|
||
inputSchema: { type: 'object', properties: { q: { type: 'string' } } } },
|
||
{ name: 'search_memories', description: 'Search stored memories',
|
||
inputSchema: { type: 'object', properties: { query: { type: 'string' } } } },
|
||
{ name: 'compress_text', description: 'Compress text',
|
||
inputSchema: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] } },
|
||
];
|
||
|
||
function callTool(id, name, args) {
|
||
switch (name) {
|
||
case 'echo':
|
||
return textResult(id, 'echo:' + (args.text ?? ''));
|
||
case 'compress_text':
|
||
return textResult(id, 'compressed:' + (args.text ?? ''));
|
||
case 'pack_codebase':
|
||
return textResult(id, JSON.stringify({
|
||
outputId: 'rmx_e2e_001',
|
||
directoryStructure: 'src/\n auth.rs\n db.rs\n',
|
||
totalFiles: 2,
|
||
totalTokens: 4321,
|
||
}));
|
||
case 'query_graph':
|
||
return textResult(id, JSON.stringify({
|
||
edges: [
|
||
{ from: 'src/auth.rs', to: 'src/db.rs', type: 'calls' },
|
||
{ from: 'src/api.rs', to: 'src/auth.rs', type: 'imports' },
|
||
],
|
||
}));
|
||
case 'search_memories':
|
||
return textResult(id, JSON.stringify({
|
||
results: [
|
||
{ memory: 'the user prefers structured logging', id: 'mem-e2e-1', score: 0.88 },
|
||
],
|
||
}));
|
||
default:
|
||
return send({ jsonrpc: '2.0', id, error: { code: -32602, message: 'unknown tool: ' + name } });
|
||
}
|
||
}
|
||
|
||
rl.on('line', (line) => {
|
||
const trimmed = line.trim();
|
||
if (!trimmed) return;
|
||
let req;
|
||
try { req = JSON.parse(trimmed); } catch { return; }
|
||
const { id, method, params } = req;
|
||
|
||
if (method === 'initialize') {
|
||
send({ jsonrpc: '2.0', id, result: {
|
||
protocolVersion: (params && params.protocolVersion) || '2025-06-18',
|
||
capabilities: { tools: {} },
|
||
serverInfo: { name: 'mcp-stdio-addon', version: '0.1.0' },
|
||
} });
|
||
} else if (method === 'notifications/initialized') {
|
||
// no response
|
||
} else if (method === 'tools/list') {
|
||
send({ jsonrpc: '2.0', id, result: { tools: TOOLS } });
|
||
} else if (method === 'tools/call') {
|
||
callTool(id, params && params.name, (params && params.arguments) || {});
|
||
} else if (id !== undefined && id !== null) {
|
||
send({ jsonrpc: '2.0', id, error: { code: -32601, message: 'method not found: ' + method } });
|
||
}
|
||
});
|
||
|
||
rl.on('close', () => process.exit(0));
|