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:
@@ -72,11 +72,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "obsidian_search",
|
"name": "obsidian_search",
|
||||||
"description": "Search for notes in the vault by content. Returns matching files with optional context snippets. Supports case-sensitive search and folder filtering."
|
"description": "Search vault for text. Returns matching files and optionally match counts. Supports path filtering, result limits, case sensitivity, and multiple output formats (text/json)."
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "obsidian_search_with_context",
|
|
||||||
"description": "Search for notes with surrounding context. Returns matching lines with context before and after the match for better understanding."
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "obsidian_get_backlinks",
|
"name": "obsidian_get_backlinks",
|
||||||
|
|||||||
@@ -108,8 +108,8 @@
|
|||||||
### Implementation for User Story 2
|
### Implementation for User Story 2
|
||||||
|
|
||||||
- [X] T046 [P] [US2] Create obsidian_search tool in src/tools/search.ts
|
- [X] T046 [P] [US2] Create obsidian_search tool in src/tools/search.ts
|
||||||
- [X] T047 [P] [US2] Define Zod schema for search parameters (query, folder, limit, caseSensitive)
|
- [X] T047 [P] [US2] Define Zod schema for search parameters (query, path, limit, case, total)
|
||||||
- [X] T048 [P] [US2] Create obsidian_search_with_context tool in src/tools/search.ts
|
- [X] T048 [P] [US2] REMOVED - Search with context (not in Obsidian CLI spec)
|
||||||
- [X] T049 [P] [US2] Create obsidian_get_backlinks tool in src/tools/links.ts
|
- [X] T049 [P] [US2] Create obsidian_get_backlinks tool in src/tools/links.ts
|
||||||
- [X] T050 [P] [US2] Define Zod schema for backlinks parameters (file/path, counts)
|
- [X] T050 [P] [US2] Define Zod schema for backlinks parameters (file/path, counts)
|
||||||
- [X] T051 [P] [US2] Create obsidian_get_outgoing_links tool in src/tools/links.ts
|
- [X] T051 [P] [US2] Create obsidian_get_outgoing_links tool in src/tools/links.ts
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export async function executeObsidianCommand(
|
|||||||
const fullArgs = [subcommand, '--vault', vaultName, ...args];
|
const fullArgs = [subcommand, '--vault', vaultName, ...args];
|
||||||
|
|
||||||
return executeCommand({
|
return executeCommand({
|
||||||
command: 'obsidian',
|
command: '/Applications/Obsidian.app/Contents/MacOS/obsidian',
|
||||||
args: fullArgs,
|
args: fullArgs,
|
||||||
timeout: options?.timeout,
|
timeout: options?.timeout,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,20 +20,26 @@ export async function registerSearchTools(server: ObsidianMCPServer): Promise<vo
|
|||||||
// T046: Search tool
|
// T046: Search tool
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'obsidian_search',
|
'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: {} },
|
{ type: 'object', properties: {} },
|
||||||
createToolHandler(
|
createToolHandler(
|
||||||
'Search for notes by content',
|
'Search vault for text',
|
||||||
{ type: 'object', properties: {} },
|
{ type: 'object', properties: {} },
|
||||||
async (args) => {
|
async (args) => {
|
||||||
const validated = searchSchema.parse(args) as any;
|
const validated = searchSchema.parse(args) as any;
|
||||||
const sanitized = sanitizeParameters(validated) as any;
|
const sanitized = sanitizeParameters(validated) as any;
|
||||||
|
|
||||||
const cmdArgs: string[] = ['search', sanitized.query as string];
|
const cmdArgs: string[] = ['search'];
|
||||||
if (sanitized.folder) cmdArgs.push('--folder', sanitized.folder as string);
|
|
||||||
if (sanitized.limit) cmdArgs.push('--limit', String(sanitized.limit));
|
// Add query parameter
|
||||||
if (sanitized.caseSensitive) cmdArgs.push('--case-sensitive');
|
cmdArgs.push(`query=${sanitized.query as string}`);
|
||||||
if (sanitized.format) cmdArgs.push('--format', sanitized.format 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);
|
const result = await executeObsidianCommand('search', cmdArgs);
|
||||||
handleCLIResult(result, { operation: 'search', query: sanitized.query });
|
handleCLIResult(result, { operation: 'search', query: sanitized.query });
|
||||||
@@ -53,37 +59,5 @@ export async function registerSearchTools(server: ObsidianMCPServer): Promise<vo
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// T048: Search with context tool
|
logger.info('Search tools registered', { count: 1 });
|
||||||
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 });
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,11 +144,11 @@ export const moveRenameSchema = z.intersection(
|
|||||||
// Search parameters
|
// Search parameters
|
||||||
export const searchSchema = z.object({
|
export const searchSchema = z.object({
|
||||||
query: z.string().min(1, 'Search query cannot be empty'),
|
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(),
|
limit: z.number().int().positive().max(1000).optional(),
|
||||||
caseSensitive: booleanFlagSchema.optional(),
|
total: booleanFlagSchema.optional(), // Return match count instead of files
|
||||||
contextLines: z.number().int().positive().max(10).optional(),
|
case: booleanFlagSchema.optional(), // Case sensitive search
|
||||||
...formatSchema.shape,
|
format: z.enum(['text', 'json']).optional().default('text'),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Tag search parameters
|
// Tag search parameters
|
||||||
|
|||||||
Reference in New Issue
Block a user