Skip to content

Commit b9d376b

Browse files
Copilotdata-douser
andcommitted
Remove grep from cache tools; fix annotation_search API to FTS semantics; always apply SARIF path for SARIF format
Agent-Logs-Url: https://github.com/advanced-security/codeql-development-mcp-server/sessions/219712ee-4c28-4b51-9da5-961020112e6e Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com>
1 parent 0e52b34 commit b9d376b

File tree

6 files changed

+31
-56
lines changed

6 files changed

+31
-56
lines changed

server/dist/codeql-development-mcp-server.js

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -181038,10 +181038,6 @@ var Protocol = class {
181038181038
this._progressHandlers.clear();
181039181039
this._taskProgressTokens.clear();
181040181040
this._pendingDebouncedNotifications.clear();
181041-
for (const info of this._timeoutInfo.values()) {
181042-
clearTimeout(info.timeoutId);
181043-
}
181044-
this._timeoutInfo.clear();
181045181041
for (const controller of this._requestHandlerAbortControllers.values()) {
181046181042
controller.abort();
181047181043
}
@@ -181172,9 +181168,7 @@ var Protocol = class {
181172181168
await capturedTransport?.send(errorResponse);
181173181169
}
181174181170
}).catch((error2) => this._onerror(new Error(`Failed to send response: ${error2}`))).finally(() => {
181175-
if (this._requestHandlerAbortControllers.get(request.id) === abortController) {
181176-
this._requestHandlerAbortControllers.delete(request.id);
181177-
}
181171+
this._requestHandlerAbortControllers.delete(request.id);
181178181172
});
181179181173
}
181180181174
_onprogress(notification) {
@@ -183187,9 +183181,6 @@ var McpServer = class {
183187183181
annotations = rest.shift();
183188183182
}
183189183183
} else if (typeof firstArg === "object" && firstArg !== null) {
183190-
if (Object.values(firstArg).some((v) => typeof v === "object" && v !== null)) {
183191-
throw new Error(`Tool ${name} expected a Zod schema or ToolAnnotations, but received an unrecognized object`);
183192-
}
183193183184
annotations = rest.shift();
183194183185
}
183195183186
}
@@ -183308,9 +183299,6 @@ function getZodSchemaObject(schema2) {
183308183299
if (isZodRawShapeCompat(schema2)) {
183309183300
return objectFromShape(schema2);
183310183301
}
183311-
if (!isZodSchemaInstance(schema2)) {
183312-
throw new Error("inputSchema must be a Zod schema or raw shape, received an unrecognized object");
183313-
}
183314183302
return schema2;
183315183303
}
183316183304
function promptArgumentsFromSchema(schema2) {
@@ -185651,7 +185639,7 @@ var SqliteStore = class _SqliteStore {
185651185639
return null;
185652185640
}
185653185641
/**
185654-
* Retrieve a subset of cached result content via line range, grep, or maxLines.
185642+
* Retrieve a subset of cached result content via line range or maxLines.
185655185643
*/
185656185644
getCacheContentSubset(cacheKey2, options) {
185657185645
const fullContent = this.getCacheContent(cacheKey2);
@@ -185666,10 +185654,6 @@ var SqliteStore = class _SqliteStore {
185666185654
const endIdx = Math.min(totalLines, end);
185667185655
selectedLines = selectedLines.slice(startIdx, endIdx);
185668185656
}
185669-
if (options.grep) {
185670-
const term = options.grep.toLowerCase();
185671-
selectedLines = selectedLines.filter((line) => line.toLowerCase().includes(term));
185672-
}
185673185657
const truncated = selectedLines.length > maxLines;
185674185658
if (truncated) {
185675185659
selectedLines = selectedLines.slice(0, maxLines);
@@ -195412,16 +195396,16 @@ function registerAnnotationDeleteTool(server) {
195412195396
function registerAnnotationSearchTool(server) {
195413195397
server.tool(
195414195398
"annotation_search",
195415-
"Substring search across annotation content, metadata, and labels (case-insensitive SQL LIKE matching).",
195399+
'Full-text search across annotation content, metadata, and labels using SQLite FTS (token-based MATCH; use * suffix for prefix matching, e.g. "vulnerab*").',
195416195400
{
195417-
query: external_exports.string().describe("Search term \u2014 matched as a substring against content, metadata, and label."),
195401+
search: external_exports.string().describe("Full-text search query matched against annotation content, metadata, and label (SQLite FTS MATCH syntax; use * for prefix matching)."),
195418195402
category: external_exports.string().optional().describe("Restrict search to a specific category."),
195419195403
limit: external_exports.number().optional().describe("Maximum number of results (default: 50).")
195420195404
},
195421-
async ({ query, category, limit }) => {
195405+
async ({ search, category, limit }) => {
195422195406
const store = sessionDataManager.getStore();
195423195407
const results = store.listAnnotations({
195424-
search: query,
195408+
search,
195425195409
category,
195426195410
limit: limit ?? 50
195427195411
});
@@ -195637,24 +195621,23 @@ function registerQueryResultsCacheLookupTool(server) {
195637195621
function registerQueryResultsCacheRetrieveTool(server) {
195638195622
server.tool(
195639195623
"query_results_cache_retrieve",
195640-
"Retrieve cached query results with optional subset selection. Supports line ranges (for graphtext/CSV), SARIF result indices, file filtering, and text search to return only the relevant portion.",
195624+
"Retrieve cached query results with optional subset selection. Supports line ranges (for graphtext/CSV) and SARIF result indices and file filtering to return only the relevant portion.",
195641195625
{
195642195626
cacheKey: external_exports.string().describe("The cache key of the result to retrieve."),
195643-
lineRange: external_exports.tuple([external_exports.number(), external_exports.number()]).optional().describe("Line range [start, end] (1-indexed). For graphtext/CSV output."),
195644-
resultIndices: external_exports.tuple([external_exports.number(), external_exports.number()]).optional().describe("SARIF result index range [start, end] (0-indexed, inclusive)."),
195627+
lineRange: external_exports.tuple([external_exports.number(), external_exports.number()]).optional().describe("Line range [start, end] (1-indexed, inclusive). For graphtext/CSV output only."),
195628+
resultIndices: external_exports.tuple([external_exports.number(), external_exports.number()]).optional().describe("SARIF result index range [start, end] (0-indexed, inclusive). For SARIF output only."),
195645195629
fileFilter: external_exports.string().optional().describe("For SARIF: only include results whose file path contains this string."),
195646-
grep: external_exports.string().optional().describe("Text search filter: only include lines/results containing this term."),
195647195630
maxLines: external_exports.number().optional().describe("Maximum number of lines to return for line-based formats (default: 500)."),
195648195631
maxResults: external_exports.number().optional().describe("Maximum number of SARIF results to return (default: 100).")
195649195632
},
195650-
async ({ cacheKey: cacheKey2, lineRange, resultIndices, fileFilter, grep, maxLines, maxResults }) => {
195633+
async ({ cacheKey: cacheKey2, lineRange, resultIndices, fileFilter, maxLines, maxResults }) => {
195651195634
const store = sessionDataManager.getStore();
195652195635
const meta = store.getCacheEntryMeta(cacheKey2);
195653195636
if (!meta) {
195654195637
return { content: [{ type: "text", text: `No cached result found for key: ${cacheKey2}` }] };
195655195638
}
195656195639
const isSarif = meta.outputFormat.includes("sarif");
195657-
if (isSarif && (resultIndices || fileFilter)) {
195640+
if (isSarif) {
195658195641
const subset2 = store.getCacheSarifSubset(cacheKey2, {
195659195642
resultIndices,
195660195643
fileFilter,
@@ -195688,7 +195671,6 @@ function registerQueryResultsCacheRetrieveTool(server) {
195688195671
}
195689195672
const subset = store.getCacheContentSubset(cacheKey2, {
195690195673
lineRange,
195691-
grep,
195692195674
maxLines: maxLines ?? 500
195693195675
});
195694195676
if (!subset) {

server/dist/codeql-development-mcp-server.js.map

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/src/lib/sqlite-store.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -662,13 +662,12 @@ export class SqliteStore {
662662
}
663663

664664
/**
665-
* Retrieve a subset of cached result content via line range, grep, or maxLines.
665+
* Retrieve a subset of cached result content via line range or maxLines.
666666
*/
667667
getCacheContentSubset(
668668
cacheKey: string,
669669
options: {
670670
lineRange?: [number, number];
671-
grep?: string;
672671
maxLines?: number;
673672
},
674673
): { content: string; totalLines: number; returnedLines: number; truncated: boolean } | null {
@@ -687,11 +686,6 @@ export class SqliteStore {
687686
selectedLines = selectedLines.slice(startIdx, endIdx);
688687
}
689688

690-
if (options.grep) {
691-
const term = options.grep.toLowerCase();
692-
selectedLines = selectedLines.filter(line => line.toLowerCase().includes(term));
693-
}
694-
695689
const truncated = selectedLines.length > maxLines;
696690
if (truncated) {
697691
selectedLines = selectedLines.slice(0, maxLines);

server/src/tools/annotation-tools.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,16 +166,16 @@ function registerAnnotationDeleteTool(server: McpServer): void {
166166
function registerAnnotationSearchTool(server: McpServer): void {
167167
server.tool(
168168
'annotation_search',
169-
'Substring search across annotation content, metadata, and labels (case-insensitive SQL LIKE matching).',
169+
'Full-text search across annotation content, metadata, and labels using SQLite FTS (token-based MATCH; use * suffix for prefix matching, e.g. "vulnerab*").',
170170
{
171-
query: z.string().describe('Search term — matched as a substring against content, metadata, and label.'),
171+
search: z.string().describe('Full-text search query matched against annotation content, metadata, and label (SQLite FTS MATCH syntax; use * for prefix matching).'),
172172
category: z.string().optional().describe('Restrict search to a specific category.'),
173173
limit: z.number().optional().describe('Maximum number of results (default: 50).'),
174174
},
175-
async ({ query, category, limit }) => {
175+
async ({ search, category, limit }) => {
176176
const store = sessionDataManager.getStore();
177177
const results = store.listAnnotations({
178-
search: query,
178+
search,
179179
category,
180180
limit: limit ?? 50,
181181
});

server/src/tools/cache-tools.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,27 +80,27 @@ function registerQueryResultsCacheLookupTool(server: McpServer): void {
8080
function registerQueryResultsCacheRetrieveTool(server: McpServer): void {
8181
server.tool(
8282
'query_results_cache_retrieve',
83-
'Retrieve cached query results with optional subset selection. Supports line ranges (for graphtext/CSV), SARIF result indices, file filtering, and text search to return only the relevant portion.',
83+
'Retrieve cached query results with optional subset selection. Supports line ranges (for graphtext/CSV) and SARIF result indices and file filtering to return only the relevant portion.',
8484
{
8585
cacheKey: z.string().describe('The cache key of the result to retrieve.'),
86-
lineRange: z.tuple([z.number(), z.number()]).optional().describe('Line range [start, end] (1-indexed). For graphtext/CSV output.'),
87-
resultIndices: z.tuple([z.number(), z.number()]).optional().describe('SARIF result index range [start, end] (0-indexed, inclusive).'),
86+
lineRange: z.tuple([z.number(), z.number()]).optional().describe('Line range [start, end] (1-indexed, inclusive). For graphtext/CSV output only.'),
87+
resultIndices: z.tuple([z.number(), z.number()]).optional().describe('SARIF result index range [start, end] (0-indexed, inclusive). For SARIF output only.'),
8888
fileFilter: z.string().optional().describe('For SARIF: only include results whose file path contains this string.'),
89-
grep: z.string().optional().describe('Text search filter: only include lines/results containing this term.'),
9089
maxLines: z.number().optional().describe('Maximum number of lines to return for line-based formats (default: 500).'),
9190
maxResults: z.number().optional().describe('Maximum number of SARIF results to return (default: 100).'),
9291
},
93-
async ({ cacheKey, lineRange, resultIndices, fileFilter, grep, maxLines, maxResults }) => {
92+
async ({ cacheKey, lineRange, resultIndices, fileFilter, maxLines, maxResults }) => {
9493
const store = sessionDataManager.getStore();
9594
const meta = store.getCacheEntryMeta(cacheKey);
9695

9796
if (!meta) {
9897
return { content: [{ type: 'text' as const, text: `No cached result found for key: ${cacheKey}` }] };
9998
}
10099

101-
// For SARIF format with SARIF-specific filters, use SARIF subset retrieval
100+
// SARIF format: always use the SARIF-aware subset retrieval so that
101+
// maxResults is applied and result-level filters (indices, file path) work correctly.
102102
const isSarif = meta.outputFormat.includes('sarif');
103-
if (isSarif && (resultIndices || fileFilter)) {
103+
if (isSarif) {
104104
const subset = store.getCacheSarifSubset(cacheKey, {
105105
resultIndices,
106106
fileFilter,
@@ -134,10 +134,9 @@ function registerQueryResultsCacheRetrieveTool(server: McpServer): void {
134134
};
135135
}
136136

137-
// Line-based subset for graphtext, CSV, or any text format
137+
// Line-based subset for graphtext, CSV, or any other text format.
138138
const subset = store.getCacheContentSubset(cacheKey, {
139139
lineRange,
140-
grep,
141140
maxLines: maxLines ?? 500,
142141
});
143142
if (!subset) {

server/test/src/lib/sqlite-store.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,9 @@ describe('SqliteStore', () => {
291291
expect(subset!.totalLines).toBe(6); // 5 lines + trailing newline
292292
});
293293

294-
it('should retrieve subset by grep', () => {
294+
it('should retrieve subset by maxLines without lineRange', () => {
295295
store.putCacheEntry({
296-
cacheKey: 'grep',
296+
cacheKey: 'nolimit',
297297
queryName: 'PrintAST',
298298
queryPath: '/p.ql',
299299
databasePath: '/db',
@@ -303,11 +303,11 @@ describe('SqliteStore', () => {
303303
resultContent: 'Function foo\n Param x\nFunction bar\n Param y\n',
304304
});
305305

306-
const subset = store.getCacheContentSubset('grep', { grep: 'Function' });
306+
const subset = store.getCacheContentSubset('nolimit', { maxLines: 2 });
307307
expect(subset).not.toBeNull();
308308
expect(subset!.returnedLines).toBe(2);
309-
expect(subset!.content).toContain('Function foo');
310-
expect(subset!.content).toContain('Function bar');
309+
expect(subset!.truncated).toBe(true);
310+
expect(subset!.content).toBe('Function foo\n Param x');
311311
});
312312

313313
it('should enforce maxLines cap', () => {

0 commit comments

Comments
 (0)