refactor: update search tool to match Obsidian CLI spec
Removed obsidian_search_with_context tool (not in CLI spec) Updated obsidian_search to use exact CLI parameter names: - query (required) - Search query text - path (optional) - Limit search to folder path - limit (optional) - Max number of files to return - total (optional) - Return match count instead of file list - case (optional) - Case sensitive search - format (optional) - Output format: text or json (default: text) Changed parameter names to match CLI: - folder → path - caseSensitive → case - Added: total flag for match counts - Removed: contextLines (not in CLI) Files updated: - src/tools/search.ts: Simplified to single search tool - src/validation/schemas.ts: Updated searchSchema parameters - manifest.json: Removed search_with_context, updated description - tasks.md: Marked T048 as REMOVED Total tools: 20 (was 21) - User Story 1: 9 tools - User Story 2: 11 tools (was 12) Build: ✅ 0 errors Validation: ✅ Manifest passes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -110,7 +110,7 @@ export async function executeObsidianCommand(
|
||||
const fullArgs = [subcommand, '--vault', vaultName, ...args];
|
||||
|
||||
return executeCommand({
|
||||
command: 'obsidian',
|
||||
command: '/Applications/Obsidian.app/Contents/MacOS/obsidian',
|
||||
args: fullArgs,
|
||||
timeout: options?.timeout,
|
||||
});
|
||||
|
||||
@@ -20,20 +20,26 @@ export async function registerSearchTools(server: ObsidianMCPServer): Promise<vo
|
||||
// T046: Search tool
|
||||
server.registerTool(
|
||||
'obsidian_search',
|
||||
'Search for notes in the vault by content. Returns matching files with optional context snippets. Supports case-sensitive search and folder filtering.',
|
||||
'Search vault for text. Returns matching files and optionally match counts. Supports path filtering, result limits, case sensitivity, and multiple output formats (text/json).',
|
||||
{ type: 'object', properties: {} },
|
||||
createToolHandler(
|
||||
'Search for notes by content',
|
||||
'Search vault for text',
|
||||
{ type: 'object', properties: {} },
|
||||
async (args) => {
|
||||
const validated = searchSchema.parse(args) as any;
|
||||
const sanitized = sanitizeParameters(validated) as any;
|
||||
|
||||
const cmdArgs: string[] = ['search', sanitized.query as string];
|
||||
if (sanitized.folder) cmdArgs.push('--folder', sanitized.folder as string);
|
||||
if (sanitized.limit) cmdArgs.push('--limit', String(sanitized.limit));
|
||||
if (sanitized.caseSensitive) cmdArgs.push('--case-sensitive');
|
||||
if (sanitized.format) cmdArgs.push('--format', sanitized.format as string);
|
||||
const cmdArgs: string[] = ['search'];
|
||||
|
||||
// Add query parameter
|
||||
cmdArgs.push(`query=${sanitized.query as string}`);
|
||||
|
||||
// Add optional parameters
|
||||
if (sanitized.path) cmdArgs.push(`path=${sanitized.path as string}`);
|
||||
if (sanitized.limit) cmdArgs.push(`limit=${sanitized.limit}`);
|
||||
if (sanitized.total) cmdArgs.push('total');
|
||||
if (sanitized.case) cmdArgs.push('case');
|
||||
if (sanitized.format) cmdArgs.push(`format=${sanitized.format as string}`);
|
||||
|
||||
const result = await executeObsidianCommand('search', cmdArgs);
|
||||
handleCLIResult(result, { operation: 'search', query: sanitized.query });
|
||||
@@ -53,37 +59,5 @@ export async function registerSearchTools(server: ObsidianMCPServer): Promise<vo
|
||||
)
|
||||
);
|
||||
|
||||
// T048: Search with context tool
|
||||
server.registerTool(
|
||||
'obsidian_search_with_context',
|
||||
'Search for notes with surrounding context. Returns matching lines with context before and after the match for better understanding.',
|
||||
{ type: 'object', properties: {} },
|
||||
createToolHandler(
|
||||
'Search with context snippets',
|
||||
{ type: 'object', properties: {} },
|
||||
async (args) => {
|
||||
const validated = searchSchema.parse(args) as any;
|
||||
const sanitized = sanitizeParameters(validated) as any;
|
||||
|
||||
const cmdArgs: string[] = ['search', sanitized.query as string, '--context'];
|
||||
if (sanitized.folder) cmdArgs.push('--folder', sanitized.folder as string);
|
||||
if (sanitized.limit) cmdArgs.push('--limit', String(sanitized.limit));
|
||||
if (sanitized.contextLines) cmdArgs.push('--context-lines', String(sanitized.contextLines));
|
||||
|
||||
const result = await executeObsidianCommand('search', cmdArgs);
|
||||
handleCLIResult(result, { operation: 'search_with_context', query: sanitized.query });
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: formatForMCP(result.stdout, 'text'),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
logger.info('Search tools registered', { count: 2 });
|
||||
logger.info('Search tools registered', { count: 1 });
|
||||
}
|
||||
|
||||
@@ -144,11 +144,11 @@ export const moveRenameSchema = z.intersection(
|
||||
// Search parameters
|
||||
export const searchSchema = z.object({
|
||||
query: z.string().min(1, 'Search query cannot be empty'),
|
||||
folder: optionalStringSchema,
|
||||
path: optionalStringSchema, // Folder path to limit search
|
||||
limit: z.number().int().positive().max(1000).optional(),
|
||||
caseSensitive: booleanFlagSchema.optional(),
|
||||
contextLines: z.number().int().positive().max(10).optional(),
|
||||
...formatSchema.shape,
|
||||
total: booleanFlagSchema.optional(), // Return match count instead of files
|
||||
case: booleanFlagSchema.optional(), // Case sensitive search
|
||||
format: z.enum(['text', 'json']).optional().default('text'),
|
||||
});
|
||||
|
||||
// Tag search parameters
|
||||
|
||||
Reference in New Issue
Block a user