fix(memory): constrain search_nodes query length (#4662)

The search_nodes tool accepted an unbounded query string (per
modelcontextprotocol/servers#3537, official servers should constrain
string parameters). An oversized query costs an O(graph) scan per
call with no value. Cap it at 2048 chars via an exported
SearchNodesQuerySchema, and add vitest coverage for the boundary
(at-limit accepted, over-limit rejected, non-string still rejected).

Signed-off-by: fei <204683769+feiiiiii5@users.noreply.github.com>
This commit is contained in:
fei 2026-09-03 09:35:03 +08:00 • committed by GitHub
commit f41b666edf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 33 additions and 1 deletions

View file

@ -0,0 +1,25 @@
import { describe, it, expect } from 'vitest';
import { SearchNodesQuerySchema, SEARCH_QUERY_MAX_LENGTH } from '../index.js';
describe('search_nodes input schema', () => {
it('should accept a normal query', () => {
expect(SearchNodesQuerySchema.safeParse('Alice').success).toBe(true);
expect(SearchNodesQuerySchema.safeParse('works at Acme Corp').success).toBe(true);
});
it('should accept a query at exactly the max length', () => {
const atLimit = 'a'.repeat(SEARCH_QUERY_MAX_LENGTH);
expect(SearchNodesQuerySchema.safeParse(atLimit).success).toBe(true);
});
it('should reject a query longer than the max length', () => {
const oversized = 'a'.repeat(SEARCH_QUERY_MAX_LENGTH + 1);
const result = SearchNodesQuerySchema.safeParse(oversized);
expect(result.success).toBe(false);
});
it('should still reject non-string input', () => {
expect(SearchNodesQuerySchema.safeParse(42).success).toBe(false);
expect(SearchNodesQuerySchema.safeParse(null).success).toBe(false);
});
});

View file

@ -582,6 +582,13 @@ server.registerTool(
}
);
export const SEARCH_QUERY_MAX_LENGTH = 2048;
export const SearchNodesQuerySchema = z
.string()
.max(SEARCH_QUERY_MAX_LENGTH)
.describe("The search query to match against entity names, types, and observation content");
// Register search_nodes tool
server.registerTool(
"search_nodes",
@ -589,7 +596,7 @@ server.registerTool(
title: "Search Nodes",
description: "Search for nodes in the knowledge graph based on a query",
inputSchema: {
query: z.string().describe("The search query to match against entity names, types, and observation content")
query: SearchNodesQuerySchema
},
outputSchema: {
entities: z.array(EntitySchema),