fix(mcp): bound panel requests, request bodies and cancelled calls

Panels on one MCP server share six requests in flight; further requests
wait their turn (up to 256 waiting) instead of failing, and a waiting
request leaves the queue when its caller cancels. Held responses belong to
their own MCP server rather than to the process. /mcp request bodies must
arrive within 30 seconds. Over stdio, a client's notifications/cancelled
aborts the named request, which then gets no response.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Sameh KhamisandClaude Opus 5.5 committed 2026-10-02 19:47:37 -07:00
1 parent fec93aa817
commit 857767842f
6 files changed
+310 -32

No files matched your search

+41
View File
@@ -195,3 +195,44 @@ test('a client that disconnects cancels its tool call', async (t) => {
await aborted.promise;
assert.equal(seen.aborted, true);
});
test('a client that stops sending its request body is answered with a timeout', async (t) => {
let middleware;
const plugin = localMcpPlugin({
bodyTimeoutMs: 20,
createServer: () => ({ handle: async () => null }),
});
plugin.configureServer({
middlewares: { use: (path, handler) => (middleware = handler) },
});
const http = createServer((req, res) => middleware(req, res));
await new Promise((resolve) => http.listen(0, '127.0.0.1', resolve));
t.after(() => http.close());
const { port } = http.address();
const status = await new Promise((resolve, reject) => {
const request = httpRequest(
{
host: '127.0.0.1',
port,
path: '/mcp',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': '100',
Host: `localhost:${port}`,
},
},
(response) => {
response.resume();
resolve(response.statusCode);
request.destroy();
},
);
request.on('error', (error) => {
if (error.code !== 'ECONNRESET') reject(error);
});
// Part of the body, then nothing more.
request.write('{"jsonrpc"');
});
assert.equal(status, 408);
});