From 8cdabe6315cf8fe7fb7b5d06df95599c48226f22 Mon Sep 17 00:00:00 2001 From: Ousama Ben Younes Date: Wed, 1 Apr 2026 06:28:29 +0000 Subject: [PATCH] fix: declare inputSchema properties for search and timeline MCP tools (#1384 #1413) Both tools had properties:{} which prevents MCP clients from exposing params to the LLM, causing every call to send {} and get a 500 error ("Either query or filters required for search"). - search: declare query, limit, project, type, obs_type, dateStart, dateEnd, offset, orderBy - timeline: declare anchor, query, depth_before, depth_after, project - Add 3 schema regression tests (static source validation) Closes #1384 Closes #1413 Co-Authored-By: Claude --- src/servers/mcp-server.ts | 20 ++++++++- tests/servers/mcp-tool-schemas.test.ts | 56 ++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 tests/servers/mcp-tool-schemas.test.ts diff --git a/src/servers/mcp-server.ts b/src/servers/mcp-server.ts index 70fbf336..4a0a5bfe 100644 --- a/src/servers/mcp-server.ts +++ b/src/servers/mcp-server.ts @@ -188,7 +188,17 @@ NEVER fetch full details without filtering first. 10x token savings.`, description: 'Step 1: Search memory. Returns index with IDs. Params: query, limit, project, type, obs_type, dateStart, dateEnd, offset, orderBy', inputSchema: { type: 'object', - properties: {}, + properties: { + query: { type: 'string', description: 'Search query' }, + limit: { type: 'number', description: 'Max results (default 20)' }, + project: { type: 'string', description: 'Filter by project name' }, + type: { type: 'string', description: 'Filter by observation type' }, + obs_type: { type: 'string', description: 'Filter by obs_type field' }, + dateStart: { type: 'string', description: 'Start date filter (ISO)' }, + dateEnd: { type: 'string', description: 'End date filter (ISO)' }, + offset: { type: 'number', description: 'Pagination offset' }, + orderBy: { type: 'string', description: 'Sort order: date_desc or date_asc' } + }, additionalProperties: true }, handler: async (args: any) => { @@ -201,7 +211,13 @@ NEVER fetch full details without filtering first. 10x token savings.`, description: 'Step 2: Get context around results. Params: anchor (observation ID) OR query (finds anchor automatically), depth_before, depth_after, project', inputSchema: { type: 'object', - properties: {}, + properties: { + anchor: { type: 'number', description: 'Observation ID to center the timeline around' }, + query: { type: 'string', description: 'Query to find anchor automatically' }, + depth_before: { type: 'number', description: 'Items before anchor (default 3)' }, + depth_after: { type: 'number', description: 'Items after anchor (default 3)' }, + project: { type: 'string', description: 'Filter by project name' } + }, additionalProperties: true }, handler: async (args: any) => { diff --git a/tests/servers/mcp-tool-schemas.test.ts b/tests/servers/mcp-tool-schemas.test.ts new file mode 100644 index 00000000..ca7a1a68 --- /dev/null +++ b/tests/servers/mcp-tool-schemas.test.ts @@ -0,0 +1,56 @@ +/** + * Tests for MCP tool inputSchema declarations (fix for #1384 / #1413) + * + * Validates that search and timeline tools declare their parameters explicitly + * so MCP clients (Claude Code) can expose them to the LLM. + */ +import { describe, it, expect } from 'bun:test'; + +// Import the tools array directly — static schema validation, no server needed +import '../../src/servers/mcp-server.js'; + +// Re-import via dynamic require to access the tools array +const mcpServerPath = new URL('../../src/servers/mcp-server.ts', import.meta.url).pathname; + +describe('MCP tool inputSchema declarations', () => { + let tools: any[]; + + // Load tools by reading the source and extracting the exported structure + // We test the schema shape directly from the source constants + it('search tool declares query parameter', async () => { + const src = await Bun.file(mcpServerPath).text(); + + // Verify search properties are declared (not empty) + expect(src).toContain("name: 'search'"); + // Check query is declared in properties after the search tool definition + const searchSection = src.slice(src.indexOf("name: 'search'"), src.indexOf("name: 'timeline'")); + expect(searchSection).toContain("query:"); + expect(searchSection).toContain("limit:"); + expect(searchSection).toContain("project:"); + expect(searchSection).toContain("orderBy:"); + expect(searchSection).not.toContain("properties: {}"); + }); + + it('timeline tool declares anchor and query parameters', async () => { + const src = await Bun.file(mcpServerPath).text(); + + const timelineSection = src.slice( + src.indexOf("name: 'timeline'"), + src.indexOf("name: 'get_observations'") + ); + expect(timelineSection).toContain("anchor:"); + expect(timelineSection).toContain("query:"); + expect(timelineSection).toContain("depth_before:"); + expect(timelineSection).toContain("depth_after:"); + expect(timelineSection).toContain("project:"); + expect(timelineSection).not.toContain("properties: {}"); + }); + + it('get_observations still declares ids (regression check)', async () => { + const src = await Bun.file(mcpServerPath).text(); + + const getObsSection = src.slice(src.indexOf("name: 'get_observations'")); + expect(getObsSection).toContain("ids:"); + expect(getObsSection).toContain("required:"); + }); +});