fix request metadata HTTP method handling (#409)

This commit is contained in:
thomas 2026-07-22 23:04:40 -07:00 • committed by GitHub
commit a865118206
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 74 additions and 2 deletions

View file

@ -6,7 +6,10 @@ import {
} from './auth/test_helpers/testClient';
import { getHandler } from '../../../examples/clients/typescript/everything-client';
import { getScenario } from '../index';
import { DECLARED_CHECK_IDS } from './request-metadata';
import {
DECLARED_CHECK_IDS,
RequestMetadataScenario
} from './request-metadata';
// A bad client that does not send _meta
async function badClient(serverUrl: string) {
@ -246,6 +249,48 @@ describe('request-metadata client scenario — client never connects', () => {
});
});
describe('request-metadata client scenario — HTTP handling', () => {
test.each(['GET', 'DELETE'])(
'rejects an empty-body %s request without parsing it as JSON',
async (method) => {
const scenario = new RequestMetadataScenario();
const { serverUrl } = await scenario.start(testScenarioContext());
try {
const response = await fetch(serverUrl, { method });
expect(response.status).toBe(405);
expect(response.headers.get('allow')).toBe('POST');
expect(await response.text()).toBe('Method Not Allowed');
} finally {
await scenario.stop();
}
}
);
test('returns a JSON-RPC parse error for malformed POST bodies', async () => {
const scenario = new RequestMetadataScenario();
const { serverUrl } = await scenario.start(testScenarioContext());
try {
const response = await fetch(serverUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: '{'
});
expect(response.status).toBe(400);
expect(await response.json()).toMatchObject({
jsonrpc: '2.0',
id: null,
error: { code: -32700 }
});
} finally {
await scenario.stop();
}
});
});
describe('request-metadata client scenario — negative tests', () => {
test('client fails when omitting _meta', async () => {
const runner = new InlineClientRunner(badClient);

View file

@ -129,12 +129,39 @@ export class RequestMetadataScenario implements Scenario {
req: http.IncomingMessage,
res: http.ServerResponse
): void {
// This scenario only evaluates the metadata attached to POST requests.
// Clients may probe a Streamable HTTP endpoint with an empty-body GET
// before sending MCP requests, so reject unsupported methods before
// attempting to parse a JSON-RPC body.
if (req.method !== 'POST') {
res.writeHead(405, { Allow: 'POST' });
res.end('Method Not Allowed');
return;
}
let body = '';
req.on('data', (chunk) => {
body += chunk.toString();
});
req.on('end', () => {
const request = JSON.parse(body);
let request;
try {
request = JSON.parse(body);
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(
JSON.stringify({
jsonrpc: '2.0',
id: null,
error: {
code: -32700,
message: `Parse error: ${String(error)}`
}
})
);
return;
}
this.requestsObserved++;
// Extract version and headers