obsidian-mcp-server 2.0.3 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +80 -88
  3. package/dist/mcp-server/server.js +2 -2
  4. package/dist/mcp-server/tools/obsidianListFilesTool/logic.d.ts +20 -16
  5. package/dist/mcp-server/tools/obsidianListFilesTool/logic.js +142 -106
  6. package/dist/mcp-server/tools/obsidianListFilesTool/registration.d.ts +9 -5
  7. package/dist/mcp-server/tools/obsidianListFilesTool/registration.js +11 -9
  8. package/dist/mcp-server/tools/{obsidianUpdateFileTool → obsidianUpdateNoteTool}/index.d.ts +4 -4
  9. package/dist/mcp-server/tools/{obsidianUpdateFileTool → obsidianUpdateNoteTool}/index.js +4 -4
  10. package/dist/mcp-server/tools/{obsidianUpdateFileTool → obsidianUpdateNoteTool}/logic.d.ts +8 -8
  11. package/dist/mcp-server/tools/{obsidianUpdateFileTool → obsidianUpdateNoteTool}/logic.js +7 -7
  12. package/dist/mcp-server/tools/{obsidianUpdateFileTool → obsidianUpdateNoteTool}/registration.d.ts +2 -2
  13. package/dist/mcp-server/tools/{obsidianUpdateFileTool → obsidianUpdateNoteTool}/registration.js +13 -13
  14. package/dist/mcp-server/transports/{authentication → auth/core}/authContext.d.ts +2 -2
  15. package/dist/mcp-server/transports/{authentication → auth/core}/authContext.js +1 -1
  16. package/dist/mcp-server/transports/{authentication/types.d.ts → auth/core/authTypes.d.ts} +1 -1
  17. package/dist/mcp-server/transports/{authentication/types.js → auth/core/authTypes.js} +1 -1
  18. package/dist/mcp-server/transports/{authentication → auth/core}/authUtils.d.ts +1 -1
  19. package/dist/mcp-server/transports/{authentication → auth/core}/authUtils.js +3 -3
  20. package/dist/mcp-server/transports/auth/index.d.ts +10 -0
  21. package/dist/mcp-server/transports/auth/index.js +9 -0
  22. package/dist/mcp-server/transports/{authentication/authMiddleware.d.ts → auth/strategies/jwt/jwtMiddleware.d.ts} +4 -7
  23. package/dist/mcp-server/transports/{authentication/authMiddleware.js → auth/strategies/jwt/jwtMiddleware.js} +40 -36
  24. package/dist/mcp-server/transports/{authentication → auth/strategies/oauth}/oauthMiddleware.d.ts +2 -6
  25. package/dist/mcp-server/transports/{authentication → auth/strategies/oauth}/oauthMiddleware.js +33 -18
  26. package/dist/mcp-server/transports/httpErrorHandler.d.ts +26 -0
  27. package/dist/mcp-server/transports/httpErrorHandler.js +73 -0
  28. package/dist/mcp-server/transports/httpTransport.d.ts +11 -14
  29. package/dist/mcp-server/transports/httpTransport.js +91 -379
  30. package/package.json +11 -16
@@ -1,4 +1,10 @@
1
- import path from "node:path"; // Using POSIX path functions for vault path manipulation
1
+ /**
2
+ * @fileoverview Core logic for the 'obsidian_list_files' tool.
3
+ * This module defines the input schema, response types, and processing logic for
4
+ * recursively listing files and directories in an Obsidian vault with filtering.
5
+ * @module src/mcp-server/tools/obsidianListFilesTool/logic
6
+ */
7
+ import path from "node:path";
2
8
  import { z } from "zod";
3
9
  import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
4
10
  import { logger, retryWithDelay, } from "../../../utils/index.js";
@@ -12,7 +18,6 @@ export const ObsidianListFilesInputSchema = z
12
18
  .object({
13
19
  /**
14
20
  * The vault-relative path to the directory whose contents should be listed.
15
- * Examples: "Attachments/Images", "Projects", "" (for vault root), "/" (for vault root).
16
21
  * The path is treated as case-sensitive by the underlying Obsidian API.
17
22
  */
18
23
  dirPath: z
@@ -20,160 +25,191 @@ export const ObsidianListFilesInputSchema = z
20
25
  .describe('The vault-relative path to the directory to list (e.g., "developer/atlas-mcp-server", "/" for root). Case-sensitive.'),
21
26
  /**
22
27
  * Optional array of file extensions (including the leading dot) to filter the results.
23
- * Only files matching one of these extensions will be included. Directories are always included regardless of this filter.
24
- * Example: [".md", ".png"]
28
+ * Only files matching one of these extensions will be included. Directories are always included.
25
29
  */
26
30
  fileExtensionFilter: z
27
31
  .array(z.string().startsWith(".", "Extension must start with a dot '.'"))
28
32
  .optional()
29
- .describe('Optional array of file extensions (e.g., [".md") to filter files. Directories are always included.'),
33
+ .describe('Optional array of file extensions (e.g., [".md"]) to filter files. Directories are always included.'),
30
34
  /**
31
35
  * Optional JavaScript-compatible regular expression pattern string to filter results by name.
32
36
  * Only files and directories whose names match the regex will be included.
33
- * Example: "^\\d{4}-\\d{2}-\\d{2}" (matches names starting with YYYY-MM-DD)
34
37
  */
35
38
  nameRegexFilter: z
36
39
  .string()
37
40
  .nullable()
38
- .optional() // Allow null in addition to string/undefined
41
+ .optional()
39
42
  .describe("Optional regex pattern (JavaScript syntax) to filter results by name."),
43
+ /**
44
+ * The maximum depth of subdirectories to list recursively.
45
+ * - A value of `0` lists only the files and directories in the specified `dirPath`.
46
+ * - A value of `1` lists the contents of `dirPath` and the contents of its immediate subdirectories.
47
+ * - A value of `-1` (the default) indicates infinite recursion, listing all subdirectories.
48
+ */
49
+ recursionDepth: z
50
+ .number()
51
+ .int()
52
+ .default(-1)
53
+ .describe("Maximum recursion depth. 0 for no recursion, -1 for infinite (default)."),
40
54
  })
41
- .describe("Input parameters for listing files and subdirectories within a specified Obsidian vault directory, with optional filtering.");
55
+ .describe("Input parameters for listing files and subdirectories within a specified Obsidian vault directory, with optional filtering and recursion.");
42
56
  // ====================================================================================
43
57
  // Helper Functions
44
58
  // ====================================================================================
45
59
  /**
46
- * Formats a list of file and directory names into a simple tree-like string representation.
47
- * Directories (indicated by a trailing '/') are listed first, then files, both sorted alphabetically.
60
+ * Recursively builds a formatted tree string from a nested array of FileTreeNode objects.
48
61
  *
49
- * @param {string[]} fileNames - An array of file and directory names (directories should end with '/').
50
- * @returns {string} A formatted string representing the directory tree, or "(empty directory)" if the input array is empty.
62
+ * @param {FileTreeNode[]} nodes - The array of nodes to format.
63
+ * @param {string} [indent=""] - The indentation prefix for the current level.
64
+ * @returns {{ tree: string, count: number }} An object containing the formatted tree string and the total count of entries.
51
65
  */
52
- function formatAsTree(fileNames) {
53
- if (!fileNames || fileNames.length === 0) {
54
- return "(empty directory)";
55
- }
56
- // Sort entries: directories first, then files, alphabetically within each group.
57
- fileNames.sort((a, b) => {
58
- const aIsDir = a.endsWith("/");
59
- const bIsDir = b.endsWith("/");
60
- // Group directories before files
61
- if (aIsDir && !bIsDir)
62
- return -1; // a (dir) comes before b (file)
63
- if (!aIsDir && bIsDir)
64
- return 1; // b (dir) comes before a (file)
65
- // Within the same type (both dirs or both files), sort alphabetically.
66
- // Remove trailing slash for comparison if it's a directory.
67
- const nameA = aIsDir ? a.slice(0, -1) : a;
68
- const nameB = bIsDir ? b.slice(0, -1) : b;
69
- return nameA.localeCompare(nameB);
70
- });
71
- // Build the tree string with prefixes
66
+ function formatTree(nodes, indent = "") {
72
67
  let treeString = "";
73
- const lastIndex = fileNames.length - 1;
74
- fileNames.forEach((name, index) => {
75
- const isLast = index === lastIndex;
76
- const prefix = isLast ? "└── " : "├── "; // Use different connectors for the last item
77
- treeString += prefix + name + (isLast ? "" : "\n"); // Add newline except for the last item
68
+ let count = nodes.length;
69
+ nodes.forEach((node, index) => {
70
+ const isLast = index === nodes.length - 1;
71
+ const prefix = isLast ? "└── " : "├── ";
72
+ const childIndent = isLast ? " " : "";
73
+ treeString += `${indent}${prefix}${node.name}\n`;
74
+ if (node.children && node.children.length > 0) {
75
+ const result = formatTree(node.children, indent + childIndent);
76
+ treeString += result.tree;
77
+ count += result.count;
78
+ }
79
+ });
80
+ return { tree: treeString, count };
81
+ }
82
+ /**
83
+ * Recursively builds a file tree by fetching directory contents from the Obsidian API.
84
+ *
85
+ * @param {string} dirPath - The path of the directory to process.
86
+ * @param {number} currentDepth - The current recursion depth.
87
+ * @param {ObsidianListFilesInput} params - The original validated input parameters, including filters and max depth.
88
+ * @param {RequestContext} context - The request context for logging.
89
+ * @param {ObsidianRestApiService} obsidianService - The Obsidian API service instance.
90
+ * @returns {Promise<FileTreeNode[]>} A promise that resolves to an array of file tree nodes.
91
+ */
92
+ async function buildFileTree(dirPath, currentDepth, params, context, obsidianService) {
93
+ const { recursionDepth, fileExtensionFilter, nameRegexFilter } = params;
94
+ // Stop recursion if max depth is reached (and it's not infinite)
95
+ if (recursionDepth !== -1 && currentDepth > recursionDepth) {
96
+ return [];
97
+ }
98
+ let fileNames;
99
+ try {
100
+ fileNames = await obsidianService.listFiles(dirPath, context);
101
+ }
102
+ catch (error) {
103
+ if (error instanceof McpError && error.code === BaseErrorCode.NOT_FOUND) {
104
+ logger.warning(`Directory not found during recursive list: ${dirPath}. Skipping.`, context);
105
+ return []; // Return empty array if a subdirectory is not found
106
+ }
107
+ throw error; // Re-throw other errors
108
+ }
109
+ const regex = nameRegexFilter && nameRegexFilter.trim() !== ""
110
+ ? new RegExp(nameRegexFilter)
111
+ : null;
112
+ const treeNodes = [];
113
+ for (const name of fileNames) {
114
+ const fullPath = path.posix.join(dirPath, name);
115
+ const isDirectory = name.endsWith("/");
116
+ const cleanName = isDirectory ? name.slice(0, -1) : name;
117
+ // Apply filters
118
+ if (regex && !regex.test(cleanName)) {
119
+ continue;
120
+ }
121
+ if (!isDirectory && fileExtensionFilter && fileExtensionFilter.length > 0) {
122
+ const extension = path.posix.extname(name);
123
+ if (!fileExtensionFilter.includes(extension)) {
124
+ continue;
125
+ }
126
+ }
127
+ const node = {
128
+ name: cleanName,
129
+ type: isDirectory ? "directory" : "file",
130
+ children: [],
131
+ };
132
+ if (isDirectory) {
133
+ node.name += "/"; // Add trailing slash back for display
134
+ node.children = await buildFileTree(fullPath, currentDepth + 1, params, context, obsidianService);
135
+ }
136
+ treeNodes.push(node);
137
+ }
138
+ // Sort entries: directories first, then files, alphabetically
139
+ treeNodes.sort((a, b) => {
140
+ if (a.type === "directory" && b.type === "file")
141
+ return -1;
142
+ if (a.type === "file" && b.type === "directory")
143
+ return 1;
144
+ return a.name.localeCompare(b.name);
78
145
  });
79
- return treeString;
146
+ return treeNodes;
80
147
  }
81
148
  // ====================================================================================
82
149
  // Core Logic Function
83
150
  // ====================================================================================
84
151
  /**
85
- * Processes the core logic for listing files and directories within a specified
86
- * directory in the Obsidian vault. Applies optional filters and formats the output.
152
+ * Processes the core logic for listing files and directories recursively within the Obsidian vault.
87
153
  *
88
154
  * @param {ObsidianListFilesInput} params - The validated input parameters.
89
155
  * @param {RequestContext} context - The request context for logging and correlation.
90
156
  * @param {ObsidianRestApiService} obsidianService - An instance of the Obsidian REST API service.
91
- * @returns {Promise<ObsidianListFilesResponse>} A promise resolving to the structured success response
92
- * containing the listed directory path, a formatted tree string, and the total entry count.
93
- * @throws {McpError} Throws an McpError if the directory cannot be listed (e.g., not found)
94
- * or if any other API interaction or validation fails.
157
+ * @returns {Promise<ObsidianListFilesResponse>} A promise resolving to the structured success response.
158
+ * @throws {McpError} Throws an McpError if the initial directory is not found or another error occurs.
95
159
  */
96
160
  export const processObsidianListFiles = async (params, context, obsidianService) => {
97
- const { dirPath, fileExtensionFilter, nameRegexFilter } = params;
98
- // Normalize dirPath for logging and response (use "/" for root)
161
+ const { dirPath } = params;
99
162
  const dirPathForLog = dirPath === "" || dirPath === "/" ? "/" : dirPath;
100
- logger.debug(`Processing obsidian_list_files request for path: ${dirPathForLog}`, { ...context, fileExtensionFilter, nameRegexFilter });
163
+ logger.debug(`Processing obsidian_list_files request for path: ${dirPathForLog}`, { ...context, params });
101
164
  try {
102
- // Normalize path for the API call as well
103
165
  const effectiveDirPath = dirPath === "" ? "/" : dirPath;
104
- // --- Step 1: Fetch initial list from Obsidian API ---
105
- const listContext = { ...context, operation: "listFilesApiCall" };
106
- logger.debug(`Calling Obsidian API to list directory: ${effectiveDirPath}`, listContext);
166
+ // --- Step 1: Build the file tree recursively with retry for the initial call ---
167
+ const buildTreeContext = {
168
+ ...context,
169
+ operation: "buildFileTreeWithRetry",
170
+ };
107
171
  const shouldRetryNotFound = (err) => err instanceof McpError && err.code === BaseErrorCode.NOT_FOUND;
108
- let fileNames = await retryWithDelay(() => obsidianService.listFiles(effectiveDirPath, listContext), {
109
- operationName: "listFilesWithRetry",
110
- context: listContext,
172
+ const fileTree = await retryWithDelay(() => buildFileTree(effectiveDirPath, 0, // Start at depth 0
173
+ params, buildTreeContext, obsidianService), {
174
+ operationName: "buildFileTreeWithRetry",
175
+ context: buildTreeContext,
111
176
  maxRetries: 3,
112
177
  delayMs: 300,
113
178
  shouldRetry: shouldRetryNotFound,
114
179
  });
115
- logger.debug(`Successfully listed ${fileNames.length} initial items in: ${dirPathForLog}`, listContext);
116
- // --- Step 2: Apply Filters ---
117
- const filterContext = { ...context, operation: "applyFilters" };
118
- // Apply extension filter if provided
119
- if (fileExtensionFilter && fileExtensionFilter.length > 0) {
120
- const initialCount = fileNames.length;
121
- fileNames = fileNames.filter((fileName) => {
122
- // Always keep directories (identified by trailing '/')
123
- if (fileName.endsWith("/"))
124
- return true;
125
- // Check if the file's extension is in the filter list
126
- const extension = path.posix.extname(fileName); // Use path.posix.extname for consistency
127
- return fileExtensionFilter.includes(extension);
128
- });
129
- logger.debug(`Applied extension filter (${fileExtensionFilter.join(", ")}). ${initialCount} -> ${fileNames.length} items remaining.`, filterContext);
130
- }
131
- // Apply regex name filter if provided and is a non-empty string
132
- if (nameRegexFilter && nameRegexFilter.trim() !== "") {
133
- const initialCount = fileNames.length;
134
- try {
135
- const regex = new RegExp(nameRegexFilter); // Compile the regex pattern
136
- fileNames = fileNames.filter((fileName) => regex.test(fileName)); // Test each name against the regex
137
- logger.debug(`Applied regex filter /${nameRegexFilter}/. ${initialCount} -> ${fileNames.length} items remaining.`, filterContext);
138
- }
139
- catch (regexError) {
140
- // Handle invalid regex patterns provided by the user
141
- logger.error(`Invalid regex pattern provided: ${nameRegexFilter}`, regexError instanceof Error ? regexError : undefined, filterContext);
142
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, // It's an input validation issue
143
- `Invalid regex pattern provided for nameRegexFilter: ${nameRegexFilter}. Error: ${regexError instanceof Error ? regexError.message : "Unknown regex error"}`, filterContext);
144
- }
145
- }
146
- // --- Step 3: Format Output and Return ---
180
+ // --- Step 2: Format the tree and count entries ---
147
181
  const formatContext = { ...context, operation: "formatResponse" };
148
- const totalEntries = fileNames.length;
149
- logger.debug(`Formatting final list of ${totalEntries} entries as tree.`, formatContext);
150
- // Format the potentially filtered list into a tree string
151
- const treeString = formatAsTree(fileNames);
152
- // Construct the final response object
182
+ if (fileTree.length === 0) {
183
+ logger.debug("Directory is empty or all items were filtered out.", formatContext);
184
+ return {
185
+ directoryPath: dirPathForLog,
186
+ tree: "(empty or all items filtered)",
187
+ totalEntries: 0,
188
+ };
189
+ }
190
+ const { tree, count } = formatTree(fileTree);
191
+ // --- Step 3: Construct and return the response ---
153
192
  const response = {
154
- directoryPath: dirPathForLog, // Return the normalized path
155
- tree: treeString,
156
- totalEntries: totalEntries,
193
+ directoryPath: dirPathForLog,
194
+ tree: tree.trimEnd(), // Remove trailing newline
195
+ totalEntries: count,
157
196
  };
158
- logger.debug(`Successfully processed list request for ${dirPathForLog}.`, context);
197
+ logger.debug(`Successfully processed list request for ${dirPathForLog}. Found ${count} entries.`, context);
159
198
  return response;
160
199
  }
161
200
  catch (error) {
162
- // Handle errors, ensuring they are McpError instances before re-throwing.
163
201
  if (error instanceof McpError) {
164
- // Provide a more specific message if the directory wasn't found
202
+ // Provide a more specific message if the directory wasn't found after retries
165
203
  if (error.code === BaseErrorCode.NOT_FOUND) {
166
- logger.error(`Directory not found for listing: ${dirPathForLog}`, error, context);
167
- throw new McpError(error.code, `Directory not found for listing: ${dirPathForLog}`, context);
204
+ const notFoundMsg = `Directory not found after retries: ${dirPathForLog}`;
205
+ logger.error(notFoundMsg, error, context);
206
+ throw new McpError(error.code, notFoundMsg, context);
168
207
  }
169
208
  logger.error(`McpError during file listing for ${dirPathForLog}: ${error.message}`, error, context);
170
- throw error; // Re-throw known McpError
171
- }
172
- else {
173
- // Catch and wrap unexpected errors
174
- const errorMessage = `Unexpected error listing Obsidian files in ${dirPathForLog}`;
175
- logger.error(errorMessage, error instanceof Error ? error : undefined, context);
176
- throw new McpError(BaseErrorCode.INTERNAL_ERROR, `${errorMessage}: ${error instanceof Error ? error.message : String(error)}`, context);
209
+ throw error;
177
210
  }
211
+ const errorMessage = `Unexpected error listing Obsidian files in ${dirPathForLog}`;
212
+ logger.error(errorMessage, error instanceof Error ? error : undefined, context);
213
+ throw new McpError(BaseErrorCode.INTERNAL_ERROR, `${errorMessage}: ${error instanceof Error ? error.message : String(error)}`, context);
178
214
  }
179
215
  };
@@ -1,14 +1,18 @@
1
+ /**
2
+ * @fileoverview Registers the 'obsidian_list_files' tool with the MCP server.
3
+ * This file defines the tool's metadata and sets up the handler that links
4
+ * the tool call to its core processing logic.
5
+ * @module src/mcp-server/tools/obsidianListFilesTool/registration
6
+ */
1
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
8
  import { ObsidianRestApiService } from "../../../services/obsidianRestAPI/index.js";
3
9
  /**
4
10
  * Registers the 'obsidian_list_files' tool with the MCP server.
5
11
  *
6
12
  * This tool lists the files and subdirectories within a specified directory
7
- * in the user's Obsidian vault. It supports optional filtering by file extension
8
- * or by a regular expression matching the entry name.
9
- *
10
- * The response includes the path of the listed directory, a formatted tree string
11
- * representing the contents, and the total count of entries listed after filtering.
13
+ * in the user's Obsidian vault. It supports optional filtering by file extension,
14
+ * by a regular expression matching the entry name, and recursive listing up to a
15
+ * specified depth.
12
16
  *
13
17
  * @param {McpServer} server - The MCP server instance to register the tool with.
14
18
  * @param {ObsidianRestApiService} obsidianService - An instance of the Obsidian REST API service
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @fileoverview Registers the 'obsidian_list_files' tool with the MCP server.
3
+ * This file defines the tool's metadata and sets up the handler that links
4
+ * the tool call to its core processing logic.
5
+ * @module src/mcp-server/tools/obsidianListFilesTool/registration
6
+ */
1
7
  import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
2
8
  import { ErrorHandler, logger, requestContextService, } from "../../../utils/index.js";
3
9
  import { ObsidianListFilesInputSchema, processObsidianListFiles, } from "./logic.js";
@@ -5,11 +11,9 @@ import { ObsidianListFilesInputSchema, processObsidianListFiles, } from "./logic
5
11
  * Registers the 'obsidian_list_files' tool with the MCP server.
6
12
  *
7
13
  * This tool lists the files and subdirectories within a specified directory
8
- * in the user's Obsidian vault. It supports optional filtering by file extension
9
- * or by a regular expression matching the entry name.
10
- *
11
- * The response includes the path of the listed directory, a formatted tree string
12
- * representing the contents, and the total count of entries listed after filtering.
14
+ * in the user's Obsidian vault. It supports optional filtering by file extension,
15
+ * by a regular expression matching the entry name, and recursive listing up to a
16
+ * specified depth.
13
17
  *
14
18
  * @param {McpServer} server - The MCP server instance to register the tool with.
15
19
  * @param {ObsidianRestApiService} obsidianService - An instance of the Obsidian REST API service
@@ -19,8 +23,7 @@ import { ObsidianListFilesInputSchema, processObsidianListFiles, } from "./logic
19
23
  */
20
24
  export const registerObsidianListFilesTool = async (server, obsidianService) => {
21
25
  const toolName = "obsidian_list_files";
22
- // Updated description to reflect the simplified response (path, tree, count)
23
- const toolDescription = "Lists files and subdirectories within a specified Obsidian vault folder. Supports optional filtering by extension or name regex. Returns an object containing the listed directory path, a formatted tree string of its contents, and the total entry count. Use an empty string or '/' for dirPath to list the vault root.";
26
+ const toolDescription = "Lists files and subdirectories within a specified Obsidian vault folder. Supports optional filtering by extension or name regex, and recursive listing to a specified depth (-1 for infinite). Returns an object containing the listed directory path, a formatted tree string of its contents, and the total entry count. Use an empty string or '/' for dirPath to list the vault root.";
24
27
  // Create a context specifically for the registration process.
25
28
  const registrationContext = requestContextService.createRequestContext({
26
29
  operation: "RegisterObsidianListFilesTool",
@@ -52,17 +55,16 @@ export const registerObsidianListFilesTool = async (server, obsidianService) =>
52
55
  dirPath: params.dirPath,
53
56
  fileExtensionFilter: params.fileExtensionFilter,
54
57
  nameRegexFilter: params.nameRegexFilter,
58
+ recursionDepth: params.recursionDepth,
55
59
  },
56
60
  });
57
61
  logger.debug(`Handling '${toolName}' request`, handlerContext);
58
62
  // Wrap the core logic execution in a tryCatch block.
59
63
  return await ErrorHandler.tryCatch(async () => {
60
64
  // Delegate the actual file listing and filtering logic to the processing function.
61
- // Note: The input schema and shape are identical here, so no separate refinement parse is needed.
62
65
  const response = await processObsidianListFiles(params, handlerContext, obsidianService);
63
66
  logger.debug(`'${toolName}' processed successfully`, handlerContext);
64
67
  // Format the successful response object from the logic function into the required MCP CallToolResult structure.
65
- // The entire response object (directoryPath, tree, totalEntries) is serialized to JSON.
66
68
  return {
67
69
  content: [
68
70
  {
@@ -1,12 +1,12 @@
1
1
  /**
2
- * @fileoverview Barrel file for the 'obsidian_update_file' MCP tool.
2
+ * @fileoverview Barrel file for the 'obsidian_update_note' MCP tool.
3
3
  *
4
- * This file serves as the public entry point for the obsidian_update_file tool module.
5
- * It re-exports the primary registration function (`registerObsidianUpdateFileTool`)
4
+ * This file serves as the public entry point for the obsidian_update_note tool module.
5
+ * It re-exports the primary registration function (`registerObsidianUpdateNoteTool`)
6
6
  * from the './registration.js' module. This pattern simplifies imports for consumers
7
7
  * of the tool, allowing them to import necessary components from a single location.
8
8
  *
9
9
  * Consumers (like the main server setup) should import the registration function
10
10
  * from this file to integrate the tool into the MCP server instance.
11
11
  */
12
- export { registerObsidianUpdateFileTool } from "./registration.js";
12
+ export { registerObsidianUpdateNoteTool } from "./registration.js";
@@ -1,12 +1,12 @@
1
1
  /**
2
- * @fileoverview Barrel file for the 'obsidian_update_file' MCP tool.
2
+ * @fileoverview Barrel file for the 'obsidian_update_note' MCP tool.
3
3
  *
4
- * This file serves as the public entry point for the obsidian_update_file tool module.
5
- * It re-exports the primary registration function (`registerObsidianUpdateFileTool`)
4
+ * This file serves as the public entry point for the obsidian_update_note tool module.
5
+ * It re-exports the primary registration function (`registerObsidianUpdateNoteTool`)
6
6
  * from the './registration.js' module. This pattern simplifies imports for consumers
7
7
  * of the tool, allowing them to import necessary components from a single location.
8
8
  *
9
9
  * Consumers (like the main server setup) should import the registration function
10
10
  * from this file to integrate the tool into the MCP server instance.
11
11
  */
12
- export { registerObsidianUpdateFileTool } from "./registration.js";
12
+ export { registerObsidianUpdateNoteTool } from "./registration.js";
@@ -8,7 +8,7 @@ import { RequestContext } from "../../../utils/index.js";
8
8
  * relying on the refined schema (`ObsidianUpdateFileInputSchema`) for stricter validation
9
9
  * within the handler logic.
10
10
  */
11
- declare const ObsidianUpdateFileRegistrationSchema: z.ZodObject<{
11
+ declare const ObsidianUpdateNoteRegistrationSchema: z.ZodObject<{
12
12
  /** Specifies the target note: 'filePath' (requires targetIdentifier), 'activeFile' (currently open file), or 'periodicNote' (requires targetIdentifier with period like 'daily'). */
13
13
  targetType: z.ZodEnum<["filePath", "activeFile", "periodicNote"]>;
14
14
  /** The content for the modification. Must be a string for whole-file operations. */
@@ -48,7 +48,7 @@ declare const ObsidianUpdateFileRegistrationSchema: z.ZodObject<{
48
48
  * The shape of the registration schema, used by `server.tool` for basic validation.
49
49
  * @see ObsidianUpdateFileRegistrationSchema
50
50
  */
51
- export declare const ObsidianUpdateFileInputSchemaShape: {
51
+ export declare const ObsidianUpdateNoteInputSchemaShape: {
52
52
  /** Specifies the target note: 'filePath' (requires targetIdentifier), 'activeFile' (currently open file), or 'periodicNote' (requires targetIdentifier with period like 'daily'). */
53
53
  targetType: z.ZodEnum<["filePath", "activeFile", "periodicNote"]>;
54
54
  /** The content for the modification. Must be a string for whole-file operations. */
@@ -71,13 +71,13 @@ export declare const ObsidianUpdateFileInputSchemaShape: {
71
71
  * received by the tool handler *before* refinement.
72
72
  * @see ObsidianUpdateFileRegistrationSchema
73
73
  */
74
- export type ObsidianUpdateFileRegistrationInput = z.infer<typeof ObsidianUpdateFileRegistrationSchema>;
74
+ export type ObsidianUpdateNoteRegistrationInput = z.infer<typeof ObsidianUpdateNoteRegistrationSchema>;
75
75
  /**
76
76
  * Refined Zod schema used internally within the tool's logic for strict validation.
77
77
  * It builds upon `WholeFileUpdateSchema` and adds cross-field validation rules using `.refine()`.
78
78
  * This ensures that `targetIdentifier` is provided and valid when required by `targetType`.
79
79
  */
80
- export declare const ObsidianUpdateFileInputSchema: z.ZodEffects<z.ZodObject<{
80
+ export declare const ObsidianUpdateNoteInputSchema: z.ZodEffects<z.ZodObject<{
81
81
  /** Specifies the type of target note. */
82
82
  targetType: z.ZodEnum<["filePath", "activeFile", "periodicNote"]>;
83
83
  /** The content to use for the modification. Must be a string for whole-file operations. */
@@ -140,7 +140,7 @@ export declare const ObsidianUpdateFileInputSchema: z.ZodEffects<z.ZodObject<{
140
140
  * TypeScript type inferred from the *refined* input schema (`ObsidianUpdateFileInputSchema`).
141
141
  * This type represents the validated and structured input used within the core processing logic.
142
142
  */
143
- export type ObsidianUpdateFileInput = z.infer<typeof ObsidianUpdateFileInputSchema>;
143
+ export type ObsidianUpdateNoteInput = z.infer<typeof ObsidianUpdateNoteInputSchema>;
144
144
  /**
145
145
  * Represents the structure of file statistics after formatting, including
146
146
  * human-readable timestamps and an estimated token count.
@@ -157,7 +157,7 @@ type FormattedStat = {
157
157
  * Defines the structure of the successful response returned by the `processObsidianUpdateFile` function.
158
158
  * This object is typically serialized to JSON and sent back to the client.
159
159
  */
160
- export interface ObsidianUpdateFileResponse {
160
+ export interface ObsidianUpdateNoteResponse {
161
161
  /** Indicates whether the operation was successful. */
162
162
  success: boolean;
163
163
  /** A human-readable message describing the outcome of the operation. */
@@ -178,6 +178,6 @@ export interface ObsidianUpdateFileResponse {
178
178
  * @returns {Promise<ObsidianUpdateFileResponse>} A promise resolving to the structured success response.
179
179
  * @throws {McpError} Throws an McpError if validation fails or the API interaction results in an error.
180
180
  */
181
- export declare const processObsidianUpdateFile: (params: ObsidianUpdateFileInput, // Use the refined, validated type
182
- context: RequestContext, obsidianService: ObsidianRestApiService, vaultCacheService: VaultCacheService | undefined) => Promise<ObsidianUpdateFileResponse>;
181
+ export declare const processObsidianUpdateNote: (params: ObsidianUpdateNoteInput, // Use the refined, validated type
182
+ context: RequestContext, obsidianService: ObsidianRestApiService, vaultCacheService: VaultCacheService | undefined) => Promise<ObsidianUpdateNoteResponse>;
183
183
  export {};
@@ -79,7 +79,7 @@ const WholeFileUpdateSchema = BaseUpdateSchema.extend({
79
79
  * relying on the refined schema (`ObsidianUpdateFileInputSchema`) for stricter validation
80
80
  * within the handler logic.
81
81
  */
82
- const ObsidianUpdateFileRegistrationSchema = z
82
+ const ObsidianUpdateNoteRegistrationSchema = z
83
83
  .object({
84
84
  /** Specifies the target note: 'filePath' (requires targetIdentifier), 'activeFile' (currently open file), or 'periodicNote' (requires targetIdentifier with period like 'daily'). */
85
85
  targetType: TargetTypeSchema,
@@ -122,7 +122,7 @@ const ObsidianUpdateFileRegistrationSchema = z
122
122
  * The shape of the registration schema, used by `server.tool` for basic validation.
123
123
  * @see ObsidianUpdateFileRegistrationSchema
124
124
  */
125
- export const ObsidianUpdateFileInputSchemaShape = ObsidianUpdateFileRegistrationSchema.shape;
125
+ export const ObsidianUpdateNoteInputSchemaShape = ObsidianUpdateNoteRegistrationSchema.shape;
126
126
  // ====================================================================================
127
127
  // Refined Schema for Internal Logic and Strict Validation
128
128
  // ====================================================================================
@@ -131,7 +131,7 @@ export const ObsidianUpdateFileInputSchemaShape = ObsidianUpdateFileRegistration
131
131
  * It builds upon `WholeFileUpdateSchema` and adds cross-field validation rules using `.refine()`.
132
132
  * This ensures that `targetIdentifier` is provided and valid when required by `targetType`.
133
133
  */
134
- export const ObsidianUpdateFileInputSchema = WholeFileUpdateSchema.refine((data) => {
134
+ export const ObsidianUpdateNoteInputSchema = WholeFileUpdateSchema.refine((data) => {
135
135
  // Rule 1: If targetType is 'filePath' or 'periodicNote', targetIdentifier must be provided.
136
136
  if ((data.targetType === "filePath" || data.targetType === "periodicNote") &&
137
137
  !data.targetIdentifier) {
@@ -207,9 +207,9 @@ async function getFinalState(targetType, targetIdentifier, period, obsidianServi
207
207
  * @returns {Promise<ObsidianUpdateFileResponse>} A promise resolving to the structured success response.
208
208
  * @throws {McpError} Throws an McpError if validation fails or the API interaction results in an error.
209
209
  */
210
- export const processObsidianUpdateFile = async (params, // Use the refined, validated type
210
+ export const processObsidianUpdateNote = async (params, // Use the refined, validated type
211
211
  context, obsidianService, vaultCacheService) => {
212
- logger.debug(`Processing obsidian_update_file request (wholeFile mode)`, {
212
+ logger.debug(`Processing obsidian_update_note request (wholeFile mode)`, {
213
213
  ...context,
214
214
  targetType: params.targetType,
215
215
  wholeFileMode: params.wholeFileMode,
@@ -251,7 +251,7 @@ context, obsidianService, vaultCacheService) => {
251
251
  existsBefore = true;
252
252
  logger.debug(`Target exists before operation.`, checkContext);
253
253
  }, {
254
- operationName: "existenceCheckObsidianUpdateFile",
254
+ operationName: "existenceCheckObsidianUpdateNote",
255
255
  context: checkContext,
256
256
  maxRetries: 3, // Total attempts: 1 initial + 2 retries
257
257
  delayMs: 250,
@@ -263,7 +263,7 @@ context, obsidianService, vaultCacheService) => {
263
263
  params.createIfNeeded;
264
264
  if (error instanceof McpError &&
265
265
  error.code === BaseErrorCode.NOT_FOUND) {
266
- logger.debug(`existenceCheckObsidianUpdateFile: shouldRetry=${should} for NOT_FOUND (createIfNeeded: ${params.createIfNeeded})`, checkContext);
266
+ logger.debug(`existenceCheckObsidianUpdateNote: shouldRetry=${should} for NOT_FOUND (createIfNeeded: ${params.createIfNeeded})`, checkContext);
267
267
  }
268
268
  return should;
269
269
  },
@@ -1,7 +1,7 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { ObsidianRestApiService, VaultCacheService } from "../../../services/obsidianRestAPI/index.js";
3
3
  /**
4
- * Registers the 'obsidian_update_file' tool with the MCP server.
4
+ * Registers the 'obsidian_update_note' tool with the MCP server.
5
5
  *
6
6
  * This tool allows modification of Obsidian notes (specified by file path,
7
7
  * the active file, or a periodic note) using whole-file operations:
@@ -18,4 +18,4 @@ import { ObsidianRestApiService, VaultCacheService } from "../../../services/obs
18
18
  * @returns {Promise<void>} A promise that resolves when the tool registration is complete or rejects on error.
19
19
  * @throws {McpError} Throws an McpError if registration fails critically.
20
20
  */
21
- export declare const registerObsidianUpdateFileTool: (server: McpServer, obsidianService: ObsidianRestApiService, vaultCacheService: VaultCacheService | undefined) => Promise<void>;
21
+ export declare const registerObsidianUpdateNoteTool: (server: McpServer, obsidianService: ObsidianRestApiService, vaultCacheService: VaultCacheService | undefined) => Promise<void>;