/**
* Vault Directory Tools - List files and directories in Obsidian vault
*/
import { z } from 'zod';
import { makeRequest, encodePath } from '../utils/api-client.js';
import { DirectoryListing } from '../types/obsidian.js';
// Schema definitions for tool parameters
export const listDirectorySchema = z.object({
path: z.string().optional()
.describe('Path to the directory relative to vault root. Leave empty or omit to list the root directory. Directories end with "/" in the response.'),
});
/**
* List files in a directory
*/
export async function listDirectory(args: z.infer<typeof listDirectorySchema>): Promise<string> {
const { path } = args;
// Handle root directory or specific path
let apiPath: string;
if (!path || path === '' || path === '/') {
apiPath = '/vault/';
} else {
// Ensure path doesn't start with / and ends with /
const normalizedPath = path.replace(/^\/+/, '').replace(/\/+$/, '');
apiPath = `/vault/${encodePath(normalizedPath)}/`;
}
const response = await makeRequest<DirectoryListing>({
method: 'GET',
path: apiPath,
accept: 'application/json',
});
if (!response.success) {
return `Error: ${response.error || `Failed to list directory (HTTP ${response.status})`}`;
}
if (!response.data || !response.data.files) {
return 'No files found or directory is empty.';
}
const files = response.data.files;
if (files.length === 0) {
return 'Directory is empty.';
}
// Format the output
const directories: string[] = [];
const regularFiles: string[] = [];
for (const file of files) {
if (file.endsWith('/')) {
directories.push(file);
} else {
regularFiles.push(file);
}
}
let output = `Contents of ${path || '/'}:\n\n`;
if (directories.length > 0) {
output += `๐ Directories (${directories.length}):\n`;
for (const dir of directories) {
output += ` ${dir}\n`;
}
output += '\n';
}
if (regularFiles.length > 0) {
output += `๐ Files (${regularFiles.length}):\n`;
for (const file of regularFiles) {
output += ` ${file}\n`;
}
}
return output;
}
// Tool definition for MCP server registration
export const vaultDirectoryTools = [
{
name: 'vault_list',
description: 'LIST, BROWSE, or EXPLORE files and directories in the Obsidian vault. Returns a formatted list showing directories (ending with /) and files. Use this tool to LIST the contents of a directory, BROWSE the vault structure, or EXPLORE what files exist. ESSENTIAL FOR ALL FILE OPERATIONS: Use this first to discover file paths before reading, creating, editing, or deleting files. DIRECTORIES: Directories end with "/" - pass these paths to vault_list to explore subdirectories. FILES: Use vault_get_file to read file contents. NEXT STEPS: After listing, use vault_get_file to read contents, or recurse into directories (ending with "/") to explore deeper. Workflow: vault_list โ (vault_get_file / vault_create_file / vault_delete_file) or vault_list โ vault_list (to explore subdirectories).',
inputSchema: listDirectorySchema,
handler: listDirectory,
},
];