fix: accept standard tools list metadata

This commit is contained in:
cadenli
2026-08-24 22:27:31 -03:00
committed by Alex Schmitt
parent 06ac5b8a49
commit c5ea82f6bf
2 changed files with 56 additions and 2 deletions
+10 -2
View File
@@ -423,8 +423,16 @@ function createMemoryMcpService(options = {}) {
return jsonRpcResult(message.id, {});
}
if (message.method === 'tools/list') {
if (message.params && Object.keys(message.params).length > 0) {
return jsonRpcError(message.id, -32602, 'tools/list does not accept parameters.');
const params = message.params || {};
if (
(Object.prototype.hasOwnProperty.call(params, '_meta') && !isRecord(params._meta))
|| (
Object.prototype.hasOwnProperty.call(params, 'cursor')
&& typeof params.cursor !== 'string'
)
|| Object.keys(params).some(key => !['cursor', '_meta'].includes(key))
) {
return jsonRpcError(message.id, -32602, 'Invalid tools/list parameters.');
}
return jsonRpcResult(message.id, {
tools: TOOL_DEFINITIONS.map(tool => ({ ...tool })),
+46
View File
@@ -132,6 +132,7 @@ async function withClient(fn, options = {}) {
const client = {
listTools: () => request('tools/list'),
listToolsRaw: params => request('tools/list', params),
callTool: ({ name, arguments: toolArguments }) => request(
'tools/call',
{ name, arguments: toolArguments }
@@ -181,6 +182,51 @@ async function main() {
});
});
await test('accepts reserved tools/list params and rejects malformed values', async () => {
await withClient(async client => {
const withMeta = await client.listToolsRaw({
_meta: { progressToken: 'progress-123' },
});
assert.strictEqual(withMeta.tools.length, 4);
const withCursor = await client.listToolsRaw({ cursor: 'next-page' });
assert.strictEqual(withCursor.tools.length, 4);
const withCursorAndMeta = await client.listToolsRaw({
cursor: 'next-page',
_meta: { progressToken: 'progress-456' },
});
assert.strictEqual(withCursorAndMeta.tools.length, 4);
const withoutMeta = await client.listTools();
assert.deepStrictEqual(
withoutMeta.tools.map(tool => tool.name).sort(),
['memory_doctor', 'memory_read', 'memory_save', 'memory_search']
);
for (const badMeta of [null, ['not', 'an', 'object'], 'string', 42, true]) {
await assert.rejects(
client.listToolsRaw({ _meta: badMeta }),
/-32602/,
`expected _meta=${JSON.stringify(badMeta)} to be rejected`
);
}
for (const badCursor of [null, {}, [], 42, true]) {
await assert.rejects(
client.listToolsRaw({ cursor: badCursor }),
/-32602/,
`expected cursor=${JSON.stringify(badCursor)} to be rejected`
);
}
await assert.rejects(
client.listToolsRaw({ 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.