From 35ab9cda791b6c7c376bbd7b16b8d343a592f704 Mon Sep 17 00:00:00 2001 From: "Peter.Morton" Date: Sun, 22 Mar 2026 16:24:20 -0500 Subject: [PATCH] fix: add missing input schemas to property discovery tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed obsidian_list_properties and obsidian_get_property_count tools to properly expose their input parameters in the tools/list response. Changes: - obsidian_list_properties: Added 8 parameters (file, path, name, total, sort, counts, format, active) based on 'obsidian help properties' - obsidian_get_property_count: Added required 'name' parameter - Fixed command names: 'property' → 'properties' (correct command) - Added formatParam() for parameter quoting - Changed parameter format to match Obsidian CLI: param=value Before: Empty properties: {} meant tools appeared in list but with no documented parameters for MCP clients. After: Full parameter schemas with descriptions, types, and constraints properly exposed via tools/list handler. Build: ✅ 0 TypeScript errors Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/tools/properties.ts | 75 ++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/src/tools/properties.ts b/src/tools/properties.ts index 426a9c2..a072697 100644 --- a/src/tools/properties.ts +++ b/src/tools/properties.ts @@ -43,19 +43,48 @@ export async function registerPropertyDiscoveryTools(server: ObsidianMCPServer): 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: {} }, + { + type: 'object', + properties: { + file: { type: 'string', description: 'Show properties for file' }, + path: { type: 'string', description: 'Show properties for path' }, + name: { type: 'string', description: 'Get specific property count' }, + total: { type: 'boolean', description: 'Return property count' }, + sort: { type: 'string', enum: ['count'], description: 'Sort by count (default: name)' }, + counts: { type: 'boolean', description: 'Include occurrence counts' }, + format: { type: 'string', enum: ['yaml', 'json', 'tsv'], description: 'Output format (default: yaml)' }, + active: { type: 'boolean', description: 'Show properties for active file' }, + }, + }, createToolHandler( 'List all properties in vault', - { type: 'object', properties: {} }, + { + type: 'object', + properties: { + file: { type: 'string', description: 'Show properties for file' }, + path: { type: 'string', description: 'Show properties for path' }, + name: { type: 'string', description: 'Get specific property count' }, + total: { type: 'boolean', description: 'Return property count' }, + sort: { type: 'string', enum: ['count'], description: 'Sort by count' }, + counts: { type: 'boolean', description: 'Include occurrence counts' }, + format: { type: 'string', enum: ['yaml', 'json', 'tsv'], description: 'Output format' }, + active: { type: 'boolean', description: 'Show properties for active file' }, + }, + }, 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 cmdArgs: string[] = []; + if (sanitized.file) cmdArgs.push(formatParam('file', sanitized.file)); + if (sanitized.path) cmdArgs.push(formatParam('path', sanitized.path)); + if (sanitized.name) cmdArgs.push(formatParam('name', sanitized.name)); + if (sanitized.total) cmdArgs.push('total'); + if (sanitized.sort) cmdArgs.push(formatParam('sort', sanitized.sort)); + if (sanitized.counts) cmdArgs.push('counts'); + if (sanitized.format) cmdArgs.push(formatParam('format', sanitized.format)); + if (sanitized.active) cmdArgs.push('active'); - const result = await executeObsidianCommand('property', cmdArgs); + const result = await executeObsidianCommand('properties', cmdArgs); handleCLIResult(result, { operation: 'list_properties' }); const format = sanitized.format || 'text'; @@ -77,31 +106,39 @@ export async function registerPropertyDiscoveryTools(server: ObsidianMCPServer): 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: {} }, + { + type: 'object', + required: ['name'], + properties: { + name: { type: 'string', description: 'Property name to count (required)' }, + }, + }, createToolHandler( 'Get property usage count', - { type: 'object', properties: {} }, + { + type: 'object', + required: ['name'], + properties: { + name: { type: 'string', description: 'Property name (required)' }, + }, + }, async (args) => { const sanitized = sanitizeParameters(args as any) as any; - if (!sanitized.property) { - throw new Error('Property parameter is required'); + if (!sanitized.name) { + throw new Error('Property name parameter is required'); } - const cmdArgs: string[] = ['property-count', sanitized.property as string]; - if (sanitized.format) cmdArgs.push('--format', sanitized.format as string); + const cmdArgs: string[] = [formatParam('name', sanitized.name), 'total']; - 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); + const result = await executeObsidianCommand('properties', cmdArgs); + handleCLIResult(result, { operation: 'property_count', property: sanitized.name }); return { content: [ { type: 'text', - text: formatForMCP(parsedData, format), + text: result.stdout.trim(), }, ], };