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:
@@ -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(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user