Skip to content

Commit f41b666

Browse files
authored
fix(memory): constrain search_nodes query length (#4662)
The search_nodes tool accepted an unbounded query string (per #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 <[email protected]>
1 parent c3d8e43 commit f41b666

2 files changed

Lines changed: 33 additions & 1 deletion

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { SearchNodesQuerySchema, SEARCH_QUERY_MAX_LENGTH } from '../index.js';
3+
4+
describe('search_nodes input schema', () => {
5+
it('should accept a normal query', () => {
6+
expect(SearchNodesQuerySchema.safeParse('Alice').success).toBe(true);
7+
expect(SearchNodesQuerySchema.safeParse('works at Acme Corp').success).toBe(true);
8+
});
9+
10+
it('should accept a query at exactly the max length', () => {
11+
const atLimit = 'a'.repeat(SEARCH_QUERY_MAX_LENGTH);
12+
expect(SearchNodesQuerySchema.safeParse(atLimit).success).toBe(true);
13+
});
14+
15+
it('should reject a query longer than the max length', () => {
16+
const oversized = 'a'.repeat(SEARCH_QUERY_MAX_LENGTH + 1);
17+
const result = SearchNodesQuerySchema.safeParse(oversized);
18+
expect(result.success).toBe(false);
19+
});
20+
21+
it('should still reject non-string input', () => {
22+
expect(SearchNodesQuerySchema.safeParse(42).success).toBe(false);
23+
expect(SearchNodesQuerySchema.safeParse(null).success).toBe(false);
24+
});
25+
});

‎src/memory/index.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,14 +582,21 @@ server.registerTool(
582582
}
583583
);
584584

585+
export const SEARCH_QUERY_MAX_LENGTH = 2048;
586+
587+
export const SearchNodesQuerySchema = z
588+
.string()
589+
.max(SEARCH_QUERY_MAX_LENGTH)
590+
.describe("The search query to match against entity names, types, and observation content");
591+
585592
// Register search_nodes tool
586593
server.registerTool(
587594
"search_nodes",
588595
{
589596
title: "Search Nodes",
590597
description: "Search for nodes in the knowledge graph based on a query",
591598
inputSchema: {
592-
query: z.string().describe("The search query to match against entity names, types, and observation content")
599+
query: SearchNodesQuerySchema
593600
},
594601
outputSchema: {
595602
entities: z.array(EntitySchema),

0 commit comments

Comments
 (0)