diff --git a/manifest.json b/manifest.json index 2bb9610..54b357c 100644 --- a/manifest.json +++ b/manifest.json @@ -99,7 +99,7 @@ "description": "List all tags in the vault or in a specific note. Optionally include usage counts and sort by frequency or name." }, { - "name": "obsidian_get_tag_info", + "name": "obsidian_search_by_tag", "description": "Get detailed information about a specific tag, including which notes use it and how many times." }, { diff --git a/src/tools/links.ts b/src/tools/links.ts index 469d014..f3b6271 100644 --- a/src/tools/links.ts +++ b/src/tools/links.ts @@ -22,10 +22,28 @@ export async function registerLinkTools(server: ObsidianMCPServer): Promise { const validated = fileIdentifierSchema.parse(args) as any; const sanitized = sanitizeParameters(validated) as any; @@ -34,6 +52,7 @@ export async function registerLinkTools(server: ObsidianMCPServer): Promise { const validated = fileIdentifierSchema.parse(args) as any; const sanitized = sanitizeParameters(validated) as any; @@ -69,19 +102,18 @@ export async function registerLinkTools(server: ObsidianMCPServer): Promise { const sanitized = sanitizeParameters(args as any) as any; const cmdArgs: string[] = []; + if (sanitized.total) cmdArgs.push('total'); + if (sanitized.counts) cmdArgs.push('counts'); + if (sanitized.verbose) cmdArgs.push('verbose'); if (sanitized.format) cmdArgs.push(formatParam('format', sanitized.format)); const result = await executeObsidianCommand('unresolved', cmdArgs); @@ -125,27 +176,39 @@ export async function registerLinkTools(server: ObsidianMCPServer): Promise { const sanitized = sanitizeParameters(args as any) as any; const cmdArgs: string[] = []; - if (sanitized.format) cmdArgs.push(formatParam('format', sanitized.format)); + if (sanitized.total) cmdArgs.push('total'); + if (sanitized.all) cmdArgs.push('all'); const result = await executeObsidianCommand('deadends', cmdArgs); handleCLIResult(result, { operation: 'deadends' }); - const format = sanitized.format || 'text'; - const parsedData = parseOutput(result.stdout, format); + const parsedData = parseOutput(result.stdout, 'text'); return { content: [ { type: 'text', - text: formatForMCP(parsedData, format), + text: formatForMCP(parsedData, 'text'), }, ], }; @@ -157,27 +220,39 @@ export async function registerLinkTools(server: ObsidianMCPServer): Promise { const sanitized = sanitizeParameters(args as any) as any; const cmdArgs: string[] = []; - if (sanitized.format) cmdArgs.push(formatParam('format', sanitized.format)); + if (sanitized.total) cmdArgs.push('total'); + if (sanitized.all) cmdArgs.push('all'); const result = await executeObsidianCommand('orphans', cmdArgs); handleCLIResult(result, { operation: 'orphans' }); - const format = sanitized.format || 'text'; - const parsedData = parseOutput(result.stdout, format); + const parsedData = parseOutput(result.stdout, 'text'); return { content: [ { type: 'text', - text: formatForMCP(parsedData, format), + text: formatForMCP(parsedData, 'text'), }, ], }; diff --git a/src/tools/tags-aliases.ts b/src/tools/tags-aliases.ts index e1828a3..71189e1 100644 --- a/src/tools/tags-aliases.ts +++ b/src/tools/tags-aliases.ts @@ -21,10 +21,32 @@ export async function registerTagsAndAliasesTools(server: ObsidianMCPServer): Pr server.registerTool( 'obsidian_list_tags', 'List all tags in the vault or in a specific note. Optionally include usage counts and sort by frequency or name.', - { type: 'object', properties: {} }, + { + type: 'object', + properties: { + file: { type: 'string', description: 'File name' }, + path: { type: 'string', description: 'File path' }, + total: { type: 'boolean', description: 'Return tag count' }, + counts: { type: 'boolean', description: 'Include tag counts' }, + sort: { type: 'string', enum: ['count'], description: 'Sort by count (default: name)' }, + format: { type: 'string', enum: ['json', 'tsv', 'csv'], description: 'Output format (default: tsv)' }, + active: { type: 'boolean', description: 'Show tags for active file' }, + }, + }, createToolHandler( 'List tags in vault or note', - { type: 'object', properties: {} }, + { + type: 'object', + properties: { + file: { type: 'string', description: 'File name' }, + path: { type: 'string', description: 'File path' }, + total: { type: 'boolean', description: 'Return tag count' }, + counts: { type: 'boolean', description: 'Include tag counts' }, + sort: { type: 'string', enum: ['count'], description: 'Sort by count' }, + format: { type: 'string', enum: ['json', 'tsv', 'csv'], description: 'Output format' }, + active: { type: 'boolean', description: 'Show tags for active file' }, + }, + }, async (args) => { const sanitized = sanitizeParameters(args as any) as any; @@ -37,9 +59,11 @@ export async function registerTagsAndAliasesTools(server: ObsidianMCPServer): Pr cmdArgs.push(formatParam('path', sanitized.path)); } + if (sanitized.total) cmdArgs.push('total'); if (sanitized.counts) cmdArgs.push('counts'); - if (sanitized.sortBy) cmdArgs.push(formatParam('sort', sanitized.sortBy)); + if (sanitized.sort) cmdArgs.push(formatParam('sort', sanitized.sort)); if (sanitized.format) cmdArgs.push(formatParam('format', sanitized.format)); + if (sanitized.active) cmdArgs.push('active'); const result = await executeObsidianCommand('tags', cmdArgs); handleCLIResult(result, { operation: 'list_tags' }); @@ -61,33 +85,49 @@ export async function registerTagsAndAliasesTools(server: ObsidianMCPServer): Pr // T055: Get tag info tool server.registerTool( - 'obsidian_get_tag_info', + 'obsidian_search_by_tag', 'Get detailed information about a specific tag, including which notes use it and how many times.', - { type: 'object', properties: {} }, + { + type: 'object', + required: ['name'], + properties: { + name: { type: 'string', description: 'Tag name (required)' }, + total: { type: 'boolean', description: 'Return occurrence count' }, + verbose: { type: 'boolean', description: 'Include file list and count' }, + }, + }, createToolHandler( 'Get information about a tag', - { type: 'object', properties: {} }, + { + type: 'object', + required: ['name'], + properties: { + name: { type: 'string', description: 'Tag name (required)' }, + total: { type: 'boolean', description: 'Return occurrence count' }, + verbose: { type: 'boolean', description: 'Include file list and count' }, + }, + }, async (args) => { const sanitized = sanitizeParameters(args as any) as any; - if (!sanitized.tag) { - throw new Error('Tag parameter is required'); + if (!sanitized.name) { + throw new Error('Tag name parameter is required'); } - const cmdArgs: string[] = [formatParam('name', sanitized.tag)]; - if (sanitized.format) cmdArgs.push(formatParam('format', sanitized.format)); + const cmdArgs: string[] = [formatParam('name', sanitized.name)]; + if (sanitized.total) cmdArgs.push('total'); + if (sanitized.verbose) cmdArgs.push('verbose'); const result = await executeObsidianCommand('tag', cmdArgs); - handleCLIResult(result, { operation: 'tag_info', tag: sanitized.tag }); + handleCLIResult(result, { operation: 'tag_info', tag: sanitized.name }); - const format = sanitized.format || 'text'; - const parsedData = parseOutput(result.stdout, format); + const parsedData = parseOutput(result.stdout, 'text'); return { content: [ { type: 'text', - text: formatForMCP(parsedData, format), + text: formatForMCP(parsedData, 'text'), }, ], }; @@ -99,10 +139,28 @@ export async function registerTagsAndAliasesTools(server: ObsidianMCPServer): Pr server.registerTool( 'obsidian_list_aliases', 'List all aliases in the vault or for a specific note. Aliases are alternative names for notes.', - { type: 'object', properties: {} }, + { + type: 'object', + properties: { + file: { type: 'string', description: 'File name' }, + path: { type: 'string', description: 'File path' }, + total: { type: 'boolean', description: 'Return alias count' }, + verbose: { type: 'boolean', description: 'Include file paths' }, + active: { type: 'boolean', description: 'Show aliases for active file' }, + }, + }, createToolHandler( 'List aliases in vault or note', - { type: 'object', properties: {} }, + { + type: 'object', + properties: { + file: { type: 'string', description: 'File name' }, + path: { type: 'string', description: 'File path' }, + total: { type: 'boolean', description: 'Return alias count' }, + verbose: { type: 'boolean', description: 'Include file paths' }, + active: { type: 'boolean', description: 'Show aliases for active file' }, + }, + }, async (args) => { const sanitized = sanitizeParameters(args as any) as any; @@ -115,7 +173,9 @@ export async function registerTagsAndAliasesTools(server: ObsidianMCPServer): Pr cmdArgs.push(formatParam('path', sanitized.path)); } - if (sanitized.format) cmdArgs.push(formatParam('format', sanitized.format)); + if (sanitized.total) cmdArgs.push('total'); + if (sanitized.verbose) cmdArgs.push('verbose'); + if (sanitized.active) cmdArgs.push('active'); const result = await executeObsidianCommand('aliases', cmdArgs); handleCLIResult(result, { operation: 'list_aliases' }); @@ -135,5 +195,49 @@ export async function registerTagsAndAliasesTools(server: ObsidianMCPServer): Pr ) ); - logger.info('Tags and aliases tools registered', { count: 3 }); + // Additional tool: Get tag count (wrapper for tag with total flag) + server.registerTool( + 'obsidian_get_tag_count', + 'Count how many notes use a specific tag.', + { + type: 'object', + required: ['name'], + properties: { + name: { type: 'string', description: 'Tag name (required)' }, + }, + }, + createToolHandler( + 'Get tag usage count', + { + type: 'object', + required: ['name'], + properties: { + name: { type: 'string', description: 'Tag name (required)' }, + }, + }, + async (args) => { + const sanitized = sanitizeParameters(args as any) as any; + + if (!sanitized.name) { + throw new Error('Tag name parameter is required'); + } + + const cmdArgs: string[] = [formatParam('name', sanitized.name), 'total']; + + const result = await executeObsidianCommand('tag', cmdArgs); + handleCLIResult(result, { operation: 'tag_count', tag: sanitized.name }); + + return { + content: [ + { + type: 'text', + text: result.stdout.trim(), + }, + ], + }; + } + ) + ); + + logger.info('Tags and aliases tools registered', { count: 4 }); }