mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-18 15:50:25 +02:00
fix(memory-mcp): accept the reserved _meta param on ping
tools/list and tools/call on main already admit `_meta` — MCP reserves it for request metadata and a client may attach it to any request. ping still refused every parameter, so a client that sends `_meta` on everything (Codex does) got -32602 on its keepalive. Rebased onto main and narrowed: when this branch was first written the same gap existed on tools/list, which has since been fixed upstream. Only the ping handler is left, so only the ping handler is touched. Refs #2810
This commit is contained in:
+12
-2
@@ -426,8 +426,18 @@ function createMemoryMcpService(options = {}) {
|
||||
return jsonRpcError(message.id, -32002, 'Server is not initialized.');
|
||||
}
|
||||
if (message.method === 'ping') {
|
||||
if (message.params && Object.keys(message.params).length > 0) {
|
||||
return jsonRpcError(message.id, -32602, 'ping does not accept parameters.');
|
||||
const params = message.params ?? {};
|
||||
// `_meta` is reserved by MCP for request metadata (e.g. progressToken) and
|
||||
// may ride on any request, which is why `tools/list` and `tools/call` below
|
||||
// both admit it. `ping` rejected every parameter, so a client that attaches
|
||||
// `_meta` to everything — Codex does — got -32602 on its keepalive. Present
|
||||
// means it must be a metadata object; nothing else is accepted. (#2810)
|
||||
if (
|
||||
!isRecord(params)
|
||||
|| (Object.prototype.hasOwnProperty.call(params, '_meta') && !isRecord(params._meta))
|
||||
|| Object.keys(params).some(key => key !== '_meta')
|
||||
) {
|
||||
return jsonRpcError(message.id, -32602, 'ping accepts no parameters other than _meta.');
|
||||
}
|
||||
return jsonRpcResult(message.id, {});
|
||||
}
|
||||
|
||||
@@ -260,6 +260,7 @@ async function withClient(fn, options = {}) {
|
||||
{ name, arguments: toolArguments }
|
||||
),
|
||||
callToolRaw: params => request('tools/call', params),
|
||||
ping: params => request('ping', params),
|
||||
};
|
||||
phase = 'callback';
|
||||
await Promise.race([Promise.resolve().then(() => fn(client, fixture)), transportFailure]);
|
||||
@@ -393,6 +394,25 @@ async function main() {
|
||||
});
|
||||
});
|
||||
|
||||
await test('accepts the reserved _meta param on ping and rejects malformed values (#2810)', async () => {
|
||||
await withClient(async client => {
|
||||
assert.deepStrictEqual(await client.ping({ _meta: { progressToken: 'progress-1' } }), {});
|
||||
assert.deepStrictEqual(await client.ping(), {});
|
||||
assert.deepStrictEqual(await client.ping({}), {});
|
||||
|
||||
for (const badMeta of [null, ['not', 'an', 'object'], 'string', 42, true]) {
|
||||
await assert.rejects(
|
||||
client.ping({ _meta: badMeta }),
|
||||
/-32602/,
|
||||
`expected ping _meta=${JSON.stringify(badMeta)} to be rejected`
|
||||
);
|
||||
}
|
||||
|
||||
await assert.rejects(client.ping({ unexpected: true }), /-32602/);
|
||||
await assert.rejects(client.ping({ _meta: {}, unexpected: true }), /-32602/);
|
||||
});
|
||||
});
|
||||
|
||||
await test('accepts the reserved _meta param on tools/call and rejects malformed values', async () => {
|
||||
await withClient(async client => {
|
||||
// A valid `_meta` object (e.g. progressToken) must not block the tool call.
|
||||
|
||||
Reference in New Issue
Block a user