This commit is contained in:
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user