Release v1.2.0: Enhanced Authentication & Parent Folder Detection
Phase 1.5 Complete: - Add automatic API key generation with secure random generation - Add createParents parameter to create_note tool - Fix authentication vulnerability (auth enabled without key) - Add MCP client configuration snippet generator - Improve UI/UX for authentication management - Add comprehensive test coverage Security: - Fixed critical vulnerability in authentication middleware - Implement three-layer defense (UI, server start, middleware) - Cryptographically secure key generation (32 chars) Features: - Auto-generate API key when authentication enabled - Copy/regenerate buttons for API key management - Recursive parent folder creation for nested paths - Enhanced error messages with actionable guidance - Selectable connection information and config snippets Documentation: - Updated CHANGELOG.md with v1.2.0 release notes - Updated ROADMAP.md (Phase 1.5 marked complete) - Created IMPLEMENTATION_NOTES_AUTH.md - Created RELEASE_NOTES_v1.2.0.md
This commit is contained in:
@@ -30,17 +30,21 @@ export class ToolRegistry {
|
||||
},
|
||||
{
|
||||
name: "create_note",
|
||||
description: "Create a new file in the Obsidian vault. Use this to create a new note or file. The parent folder must already exist - this will NOT auto-create folders. Path must be vault-relative with file extension. Will fail if the file already exists. Use list_notes() to verify the parent folder exists before creating.",
|
||||
description: "Create a new file in the Obsidian vault. Use this to create a new note or file. By default, parent folders must already exist. Set createParents to true to automatically create missing parent folders. Path must be vault-relative with file extension. Will fail if the file already exists. Use list_notes() to verify the parent folder exists before creating.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
path: {
|
||||
type: "string",
|
||||
description: "Vault-relative path for the new file (e.g., 'folder/note.md' or 'projects/2024/report.md'). Must include file extension. Parent folders must exist. Paths are case-sensitive on macOS/Linux. Do not use leading or trailing slashes."
|
||||
description: "Vault-relative path for the new file (e.g., 'folder/note.md' or 'projects/2024/report.md'). Must include file extension. Paths are case-sensitive on macOS/Linux. Do not use leading or trailing slashes."
|
||||
},
|
||||
content: {
|
||||
type: "string",
|
||||
description: "The complete content to write to the new file. Can include markdown formatting, frontmatter, etc."
|
||||
},
|
||||
createParents: {
|
||||
type: "boolean",
|
||||
description: "If true, automatically create missing parent folders. If false (default), returns an error if parent folders don't exist. Default: false"
|
||||
}
|
||||
},
|
||||
required: ["path", "content"]
|
||||
@@ -122,7 +126,7 @@ export class ToolRegistry {
|
||||
case "read_note":
|
||||
return await this.noteTools.readNote(args.path);
|
||||
case "create_note":
|
||||
return await this.noteTools.createNote(args.path, args.content);
|
||||
return await this.noteTools.createNote(args.path, args.content, args.createParents ?? false);
|
||||
case "update_note":
|
||||
return await this.noteTools.updateNote(args.path, args.content);
|
||||
case "delete_note":
|
||||
|
||||
@@ -53,7 +53,7 @@ export class NoteTools {
|
||||
}
|
||||
}
|
||||
|
||||
async createNote(path: string, content: string): Promise<CallToolResult> {
|
||||
async createNote(path: string, content: string, createParents: boolean = false): Promise<CallToolResult> {
|
||||
// Validate path
|
||||
if (!path || path.trim() === '') {
|
||||
return {
|
||||
@@ -88,30 +88,72 @@ export class NoteTools {
|
||||
};
|
||||
}
|
||||
|
||||
// Explicit parent folder detection (before write operation)
|
||||
const parentPath = PathUtils.getParentPath(normalizedPath);
|
||||
if (parentPath) {
|
||||
// Check if parent exists
|
||||
if (!PathUtils.pathExists(this.app, parentPath)) {
|
||||
if (createParents) {
|
||||
// Auto-create parent folders recursively
|
||||
try {
|
||||
await this.createParentFolders(parentPath);
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: ErrorMessages.operationFailed('create parent folders', parentPath, (error as Error).message) }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Return clear error before attempting file creation
|
||||
return {
|
||||
content: [{ type: "text", text: ErrorMessages.parentFolderNotFound(normalizedPath, parentPath) }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Check if parent is actually a folder (not a file)
|
||||
if (PathUtils.fileExists(this.app, parentPath)) {
|
||||
return {
|
||||
content: [{ type: "text", text: ErrorMessages.notAFolder(parentPath) }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Proceed with file creation
|
||||
try {
|
||||
const file = await this.app.vault.create(normalizedPath, content);
|
||||
return {
|
||||
content: [{ type: "text", text: `Note created successfully: ${file.path}` }]
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMsg = (error as Error).message;
|
||||
|
||||
// Check for parent folder not found error
|
||||
if (errorMsg.includes('parent folder')) {
|
||||
const parentPath = PathUtils.getParentPath(normalizedPath);
|
||||
return {
|
||||
content: [{ type: "text", text: ErrorMessages.parentFolderNotFound(normalizedPath, parentPath) }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: ErrorMessages.operationFailed('create note', normalizedPath, errorMsg) }],
|
||||
content: [{ type: "text", text: ErrorMessages.operationFailed('create note', normalizedPath, (error as Error).message) }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively create parent folders
|
||||
* @private
|
||||
*/
|
||||
private async createParentFolders(path: string): Promise<void> {
|
||||
// Get parent path
|
||||
const parentPath = PathUtils.getParentPath(path);
|
||||
|
||||
// If there's a parent and it doesn't exist, create it first (recursion)
|
||||
if (parentPath && !PathUtils.pathExists(this.app, parentPath)) {
|
||||
await this.createParentFolders(parentPath);
|
||||
}
|
||||
|
||||
// Create the current folder if it doesn't exist
|
||||
if (!PathUtils.pathExists(this.app, path)) {
|
||||
await this.app.vault.createFolder(path);
|
||||
}
|
||||
}
|
||||
|
||||
async updateNote(path: string, content: string): Promise<CallToolResult> {
|
||||
// Validate path
|
||||
if (!path || path.trim() === '') {
|
||||
|
||||
Reference in New Issue
Block a user