feat: implement User Story 2 - Search and Discovery (P2)
Implemented 12 new MCP tools for search and knowledge graph navigation: Search Tools (2): - obsidian_search: Content search with folder filtering and case sensitivity - obsidian_search_with_context: Search with surrounding context lines Link Tools (5): - obsidian_get_backlinks: Show incoming links to a note - obsidian_get_outgoing_links: Show outgoing links from a note - obsidian_list_unresolved_links: Find broken wikilinks - obsidian_list_deadends: Find notes with no outgoing links - obsidian_list_orphans: Find notes with no incoming links Tag & Alias Tools (3): - obsidian_list_tags: List all tags with optional counts - obsidian_get_tag_info: Detailed tag usage information - obsidian_list_aliases: List note aliases Property Discovery Tools (2): - obsidian_list_properties: List all vault properties - obsidian_get_property_count: Get property usage counts New files created: - src/tools/search.ts (2 tools) - src/tools/links.ts (5 tools) - src/tools/tags-aliases.ts (3 tools) - src/tools/properties.ts (2 tools) Updated: - src/tools/index.ts: Register all new tool modules - src/validation/schemas.ts: Enhanced searchSchema with new parameters - manifest.json: Added 12 new tools to tools array (21 total) - tasks.md: Marked T046-T063 complete (18 tasks) Build: ✅ 0 errors Validation: ✅ Manifest passes Total tools: 21 (9 US1 + 12 US2) Tasks complete: 70/167 (41.9%) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
90
src/tools/properties.ts
Normal file
90
src/tools/properties.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Properties Tools
|
||||
* User Story 2 (P2): Property discovery and querying
|
||||
*/
|
||||
|
||||
import { ObsidianMCPServer, createToolHandler } from '../server.js';
|
||||
import { executeObsidianCommand } from '../cli/executor.js';
|
||||
import { formatForMCP, parseOutput } from '../cli/parser.js';
|
||||
import { handleCLIResult } from '../utils/error-handler.js';
|
||||
import { logger } from '../utils/logger.js';
|
||||
import { sanitizeParameters } from '../validation/sanitizer.js';
|
||||
|
||||
/**
|
||||
* Register all property tools for discovery
|
||||
*/
|
||||
export async function registerPropertyDiscoveryTools(server: ObsidianMCPServer): Promise<void> {
|
||||
logger.info('Registering property discovery tools');
|
||||
|
||||
// T057: List properties tool (vault-wide)
|
||||
server.registerTool(
|
||||
'obsidian_list_properties',
|
||||
'List all properties used in the vault. Shows property keys and optionally their types and usage counts.',
|
||||
{ type: 'object', properties: {} },
|
||||
createToolHandler(
|
||||
'List all properties in vault',
|
||||
{ type: 'object', properties: {} },
|
||||
async (args) => {
|
||||
const sanitized = sanitizeParameters(args as any) as any;
|
||||
|
||||
const cmdArgs: string[] = ['list-properties'];
|
||||
if (sanitized.counts) cmdArgs.push('--counts');
|
||||
if (sanitized.types) cmdArgs.push('--types');
|
||||
if (sanitized.format) cmdArgs.push('--format', sanitized.format as string);
|
||||
|
||||
const result = await executeObsidianCommand('property', cmdArgs);
|
||||
handleCLIResult(result, { operation: 'list_properties' });
|
||||
|
||||
const format = sanitized.format || 'text';
|
||||
const parsedData = parseOutput(result.stdout, format);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: formatForMCP(parsedData, format),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// T058: Get property count tool
|
||||
server.registerTool(
|
||||
'obsidian_get_property_count',
|
||||
'Get the usage count for a specific property across the vault. Shows how many notes use this property.',
|
||||
{ type: 'object', properties: {} },
|
||||
createToolHandler(
|
||||
'Get property usage count',
|
||||
{ type: 'object', properties: {} },
|
||||
async (args) => {
|
||||
const sanitized = sanitizeParameters(args as any) as any;
|
||||
|
||||
if (!sanitized.property) {
|
||||
throw new Error('Property parameter is required');
|
||||
}
|
||||
|
||||
const cmdArgs: string[] = ['property-count', sanitized.property as string];
|
||||
if (sanitized.format) cmdArgs.push('--format', sanitized.format as string);
|
||||
|
||||
const result = await executeObsidianCommand('property', cmdArgs);
|
||||
handleCLIResult(result, { operation: 'property_count', property: sanitized.property });
|
||||
|
||||
const format = sanitized.format || 'text';
|
||||
const parsedData = parseOutput(result.stdout, format);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: formatForMCP(parsedData, format),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
logger.info('Property discovery tools registered', { count: 2 });
|
||||
}
|
||||
Reference in New Issue
Block a user