Relay upstream MCP elicitation
All checks were successful
build / test (push) Successful in 31s

This commit is contained in:
2026-05-23 13:32:25 -04:00
parent 2fd97d8d09
commit 4ff51157c0
3 changed files with 190 additions and 15 deletions

View File

@@ -320,3 +320,97 @@ test("tools/call reports upstream SSE idle timeout", async () => {
await upstream.close();
}
});
test("tools/call relays upstream elicitation requests", async () => {
let resolveResponse: (() => void) | undefined;
const responseSeen = new Promise<void>((resolve) => { resolveResponse = resolve; });
let upstreamResponse: any = null;
const requests: any[] = [];
const server = http.createServer((req, res) => {
if (req.method !== "POST" || req.url !== "/mcp") {
res.statusCode = 404;
res.end();
return;
}
const chunks: Buffer[] = [];
req.on("data", (c) => chunks.push(c));
req.on("end", () => {
const body = JSON.parse(Buffer.concat(chunks).toString("utf8"));
requests.push({ headers: req.headers, body });
if (body.method === "tools/call") {
res.writeHead(200, {
"content-type": "text/event-stream",
"Mcp-Session-Id": "sess-1",
});
res.write(
`data: ${JSON.stringify({
jsonrpc: "2.0",
id: "ask-1",
method: "elicitation/create",
params: { message: "Name?", requestedSchema: { type: "object" } },
})}\n\n`,
);
void responseSeen.then(() => {
res.end(
`data: ${JSON.stringify({
jsonrpc: "2.0",
id: body.id,
result: {
content: [{ type: "text", text: upstreamResponse.result.content.answer }],
isError: false,
},
})}\n\n`,
);
});
return;
}
if (body.id === "ask-1" && req.headers["mcp-session-id"] === "sess-1") {
upstreamResponse = body;
res.statusCode = 202;
res.end();
resolveResponse?.();
return;
}
res.setHeader("content-type", "application/json");
res.end(JSON.stringify({ jsonrpc: "2.0", id: body.id, result: {} }));
});
});
const upstream = await new Promise<{ url: string; close: () => Promise<void> }>((resolve) => {
server.listen(0, "127.0.0.1", () => {
const { port } = server.address() as AddressInfo;
resolve({
url: `http://127.0.0.1:${port}`,
close: () => new Promise((r) => server.close(() => r())),
});
});
});
const clientRequests: any[] = [];
try {
const result = await callGateway(
[{ name: "human", url: upstream.url, addedAt: "now" }],
null,
"tools/call",
{ name: `human${SEP}run_demo`, arguments: {} },
{
extra: {
requestId: "req-elicitation",
sendNotification: async () => {},
sendRequest: async (request: any) => {
clientRequests.push(request);
return { action: "accept", content: { answer: "Alice" } };
},
},
},
);
assert.equal(result.isError, false);
assert.equal(result.content[0].text, "Alice");
assert.equal(clientRequests[0].method, "elicitation/create");
assert.deepEqual(upstreamResponse.result, {
action: "accept",
content: { answer: "Alice" },
});
assert.ok(requests.some((r) => r.body.id === "ask-1"));
} finally {
await upstream.close();
}
});