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
103 lines
3.0 KiB
JavaScript
103 lines
3.0 KiB
JavaScript
#!/usr/bin/env node
|
|
// Minimal MCP stdio server fixture for the lean-ctx gateway E2E test (#1077).
|
|
//
|
|
// Speaks newline-delimited JSON-RPC 2.0 over stdin/stdout — the MCP stdio wire
|
|
// protocol — implementing just enough to exercise the gateway's real spawn path:
|
|
// initialize -> handshake (echoes the client's protocolVersion)
|
|
// tools/list -> advertises `echo` and `boom`
|
|
// tools/call -> `echo` returns `echo:<text>`; `boom` exits without replying
|
|
// (simulates a child dying mid-call, for the pool self-heal test)
|
|
// No dependencies, no network; only Node.js is required.
|
|
|
|
import { createInterface } from 'node:readline';
|
|
|
|
const rl = createInterface({ input: process.stdin });
|
|
|
|
function send(msg) {
|
|
process.stdout.write(JSON.stringify(msg) + '\n');
|
|
}
|
|
|
|
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-echo', version: '0.1.0' },
|
|
},
|
|
});
|
|
} else if (method === 'notifications/initialized') {
|
|
// Notification: no response.
|
|
} else if (method === 'tools/list') {
|
|
send({
|
|
jsonrpc: '2.0',
|
|
id,
|
|
result: {
|
|
tools: [
|
|
{
|
|
name: 'echo',
|
|
description: 'Echo back the provided text',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: { text: { type: 'string' } },
|
|
required: ['text'],
|
|
},
|
|
},
|
|
{
|
|
name: 'boom',
|
|
description: 'Exit the process without replying (test fault injection)',
|
|
inputSchema: { type: 'object', properties: {} },
|
|
},
|
|
],
|
|
},
|
|
});
|
|
} else if (method === 'tools/call') {
|
|
const name = params && params.name;
|
|
const args = (params && params.arguments) || {};
|
|
if (name === 'boom') {
|
|
// Die mid-request, never sending a response: the client's transport sees
|
|
// EOF on a pending call. Exercises pool eviction + reopen (no blind retry).
|
|
process.exit(1);
|
|
} else if (name === 'echo') {
|
|
send({
|
|
jsonrpc: '2.0',
|
|
id,
|
|
result: {
|
|
content: [{ type: 'text', text: 'echo:' + (args.text ?? '') }],
|
|
isError: false,
|
|
},
|
|
});
|
|
} else {
|
|
send({
|
|
jsonrpc: '2.0',
|
|
id,
|
|
error: { code: -32602, message: 'unknown tool: ' + name },
|
|
});
|
|
}
|
|
} else if (id !== undefined && id !== null) {
|
|
// Any other request gets a method-not-found so the client never hangs.
|
|
send({
|
|
jsonrpc: '2.0',
|
|
id,
|
|
error: { code: -32601, message: 'method not found: ' + method },
|
|
});
|
|
}
|
|
});
|
|
|
|
// When the client closes stdin (session dropped from the pool), exit cleanly.
|
|
rl.on('close', () => process.exit(0));
|