fix: add missing input schemas to property discovery tools

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>
This commit is contained in:
2026-03-22 16:24:20 -05:00
parent 2d7418825d
commit 35ab9cda79

View File

@@ -43,19 +43,48 @@ export async function registerPropertyDiscoveryTools(server: ObsidianMCPServer):
server.registerTool( server.registerTool(
'obsidian_list_properties', 'obsidian_list_properties',
'List all properties used in the vault. Shows property keys and optionally their types and usage counts.', '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( createToolHandler(
'List all properties in vault', '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) => { async (args) => {
const sanitized = sanitizeParameters(args as any) as any; const sanitized = sanitizeParameters(args as any) as any;
const cmdArgs: string[] = ['list-properties']; const cmdArgs: string[] = [];
if (sanitized.counts) cmdArgs.push('--counts'); if (sanitized.file) cmdArgs.push(formatParam('file', sanitized.file));
if (sanitized.types) cmdArgs.push('--types'); if (sanitized.path) cmdArgs.push(formatParam('path', sanitized.path));
if (sanitized.format) cmdArgs.push('--format', sanitized.format as string); 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' }); handleCLIResult(result, { operation: 'list_properties' });
const format = sanitized.format || 'text'; const format = sanitized.format || 'text';
@@ -77,31 +106,39 @@ export async function registerPropertyDiscoveryTools(server: ObsidianMCPServer):
server.registerTool( server.registerTool(
'obsidian_get_property_count', 'obsidian_get_property_count',
'Get the usage count for a specific property across the vault. Shows how many notes use this property.', '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( createToolHandler(
'Get property usage count', 'Get property usage count',
{ type: 'object', properties: {} }, {
type: 'object',
required: ['name'],
properties: {
name: { type: 'string', description: 'Property name (required)' },
},
},
async (args) => { async (args) => {
const sanitized = sanitizeParameters(args as any) as any; const sanitized = sanitizeParameters(args as any) as any;
if (!sanitized.property) { if (!sanitized.name) {
throw new Error('Property parameter is required'); throw new Error('Property name parameter is required');
} }
const cmdArgs: string[] = ['property-count', sanitized.property as string]; const cmdArgs: string[] = [formatParam('name', sanitized.name), 'total'];
if (sanitized.format) cmdArgs.push('--format', sanitized.format as string);
const result = await executeObsidianCommand('property', cmdArgs); const result = await executeObsidianCommand('properties', cmdArgs);
handleCLIResult(result, { operation: 'property_count', property: sanitized.property }); handleCLIResult(result, { operation: 'property_count', property: sanitized.name });
const format = sanitized.format || 'text';
const parsedData = parseOutput(result.stdout, format);
return { return {
content: [ content: [
{ {
type: 'text', type: 'text',
text: formatForMCP(parsedData, format), text: result.stdout.trim(),
}, },
], ],
}; };