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 <noreply@anthropic.com>
This commit is contained in:
@@ -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',
|
description: 'Step 1: Search memory. Returns index with IDs. Params: query, limit, project, type, obs_type, dateStart, dateEnd, offset, orderBy',
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: 'object',
|
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
|
additionalProperties: true
|
||||||
},
|
},
|
||||||
handler: async (args: any) => {
|
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',
|
description: 'Step 2: Get context around results. Params: anchor (observation ID) OR query (finds anchor automatically), depth_before, depth_after, project',
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: 'object',
|
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
|
additionalProperties: true
|
||||||
},
|
},
|
||||||
handler: async (args: any) => {
|
handler: async (args: any) => {
|
||||||
|
|||||||
@@ -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:");
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user