obsidian-mcp-server 2.0.7 → 3.1.0
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.
- package/CLAUDE.md +364 -0
- package/Dockerfile +99 -0
- package/LICENSE +4 -6
- package/README.md +246 -206
- package/changelog/3.0.x/3.0.0.md +102 -0
- package/changelog/3.1.x/3.1.0.md +26 -0
- package/changelog/template.md +51 -0
- package/dist/config/server-config.d.ts +19 -0
- package/dist/config/server-config.d.ts.map +1 -0
- package/dist/config/server-config.js +55 -0
- package/dist/config/server-config.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -295
- package/dist/index.js.map +1 -0
- package/dist/mcp-server/prompts/definitions/index.d.ts +8 -0
- package/dist/mcp-server/prompts/definitions/index.d.ts.map +1 -0
- package/dist/mcp-server/prompts/definitions/index.js +8 -0
- package/dist/mcp-server/prompts/definitions/index.js.map +1 -0
- package/dist/mcp-server/resources/definitions/index.d.ts +36 -0
- package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/index.js +9 -0
- package/dist/mcp-server/resources/definitions/index.js.map +1 -0
- package/dist/mcp-server/resources/definitions/obsidian-status.resource.d.ts +23 -0
- package/dist/mcp-server/resources/definitions/obsidian-status.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/obsidian-status.resource.js +47 -0
- package/dist/mcp-server/resources/definitions/obsidian-status.resource.js.map +1 -0
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.d.ts +13 -0
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.js +30 -0
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.js.map +1 -0
- package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.d.ts +21 -0
- package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.js +38 -0
- package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.js.map +1 -0
- package/dist/mcp-server/tools/definitions/_shared/schemas.d.ts +45 -0
- package/dist/mcp-server/tools/definitions/_shared/schemas.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/_shared/schemas.js +66 -0
- package/dist/mcp-server/tools/definitions/_shared/schemas.js.map +1 -0
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.d.ts +51 -0
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.js +120 -0
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.js.map +1 -0
- package/dist/mcp-server/tools/definitions/index.d.ts +582 -0
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/index.js +40 -0
- package/dist/mcp-server/tools/definitions/index.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts +42 -0
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js +58 -0
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts +46 -0
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js +66 -0
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.d.ts +19 -0
- package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.js +43 -0
- package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts +92 -0
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js +245 -0
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.d.ts +13 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.js +38 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts +59 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js +273 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.d.ts +13 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js +38 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts +68 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js +178 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts +77 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js +172 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.d.ts +22 -0
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.js +88 -0
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts +81 -0
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js +83 -0
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.d.ts +59 -0
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js +167 -0
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts +76 -0
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js +244 -0
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts +48 -0
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js +100 -0
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js.map +1 -0
- package/dist/services/obsidian/frontmatter-ops.d.ts +34 -0
- package/dist/services/obsidian/frontmatter-ops.d.ts.map +1 -0
- package/dist/services/obsidian/frontmatter-ops.js +230 -0
- package/dist/services/obsidian/frontmatter-ops.js.map +1 -0
- package/dist/services/obsidian/obsidian-service.d.ts +83 -0
- package/dist/services/obsidian/obsidian-service.d.ts.map +1 -0
- package/dist/services/obsidian/obsidian-service.js +435 -0
- package/dist/services/obsidian/obsidian-service.js.map +1 -0
- package/dist/services/obsidian/section-extractor.d.ts +13 -0
- package/dist/services/obsidian/section-extractor.d.ts.map +1 -0
- package/dist/services/obsidian/section-extractor.js +124 -0
- package/dist/services/obsidian/section-extractor.js.map +1 -0
- package/dist/services/obsidian/types.d.ts +91 -0
- package/dist/services/obsidian/types.d.ts.map +1 -0
- package/dist/services/obsidian/types.js +7 -0
- package/dist/services/obsidian/types.js.map +1 -0
- package/package.json +63 -69
- package/server.json +167 -0
- package/CHANGELOG.md +0 -124
- package/dist/config/index.d.ts +0 -41
- package/dist/config/index.js +0 -191
- package/dist/mcp-server/server.d.ts +0 -33
- package/dist/mcp-server/server.js +0 -211
- package/dist/mcp-server/tools/obsidianDeleteNoteTool/index.d.ts +0 -12
- package/dist/mcp-server/tools/obsidianDeleteNoteTool/index.js +0 -12
- package/dist/mcp-server/tools/obsidianDeleteNoteTool/logic.d.ts +0 -51
- package/dist/mcp-server/tools/obsidianDeleteNoteTool/logic.js +0 -168
- package/dist/mcp-server/tools/obsidianDeleteNoteTool/registration.d.ts +0 -19
- package/dist/mcp-server/tools/obsidianDeleteNoteTool/registration.js +0 -91
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/index.d.ts +0 -12
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/index.js +0 -12
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/logic.d.ts +0 -77
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/logic.js +0 -341
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/registration.d.ts +0 -18
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/registration.js +0 -69
- package/dist/mcp-server/tools/obsidianListNotesTool/index.d.ts +0 -12
- package/dist/mcp-server/tools/obsidianListNotesTool/index.js +0 -12
- package/dist/mcp-server/tools/obsidianListNotesTool/logic.d.ts +0 -68
- package/dist/mcp-server/tools/obsidianListNotesTool/logic.js +0 -215
- package/dist/mcp-server/tools/obsidianListNotesTool/registration.d.ts +0 -23
- package/dist/mcp-server/tools/obsidianListNotesTool/registration.js +0 -98
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/index.d.ts +0 -3
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/index.js +0 -2
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/logic.d.ts +0 -42
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/logic.js +0 -152
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/registration.d.ts +0 -3
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/registration.js +0 -52
- package/dist/mcp-server/tools/obsidianManageTagsTool/index.d.ts +0 -3
- package/dist/mcp-server/tools/obsidianManageTagsTool/index.js +0 -2
- package/dist/mcp-server/tools/obsidianManageTagsTool/logic.d.ts +0 -28
- package/dist/mcp-server/tools/obsidianManageTagsTool/logic.js +0 -161
- package/dist/mcp-server/tools/obsidianManageTagsTool/registration.d.ts +0 -3
- package/dist/mcp-server/tools/obsidianManageTagsTool/registration.js +0 -52
- package/dist/mcp-server/tools/obsidianReadNoteTool/index.d.ts +0 -12
- package/dist/mcp-server/tools/obsidianReadNoteTool/index.js +0 -12
- package/dist/mcp-server/tools/obsidianReadNoteTool/logic.d.ts +0 -87
- package/dist/mcp-server/tools/obsidianReadNoteTool/logic.js +0 -216
- package/dist/mcp-server/tools/obsidianReadNoteTool/registration.d.ts +0 -20
- package/dist/mcp-server/tools/obsidianReadNoteTool/registration.js +0 -101
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/index.d.ts +0 -12
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/index.js +0 -12
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/logic.d.ts +0 -255
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/logic.js +0 -583
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/registration.d.ts +0 -22
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/registration.js +0 -111
- package/dist/mcp-server/tools/obsidianUpdateNoteTool/index.d.ts +0 -12
- package/dist/mcp-server/tools/obsidianUpdateNoteTool/index.js +0 -12
- package/dist/mcp-server/tools/obsidianUpdateNoteTool/logic.d.ts +0 -183
- package/dist/mcp-server/tools/obsidianUpdateNoteTool/logic.js +0 -490
- package/dist/mcp-server/tools/obsidianUpdateNoteTool/registration.d.ts +0 -21
- package/dist/mcp-server/tools/obsidianUpdateNoteTool/registration.js +0 -108
- package/dist/mcp-server/transports/auth/core/authContext.d.ts +0 -33
- package/dist/mcp-server/transports/auth/core/authContext.js +0 -24
- package/dist/mcp-server/transports/auth/core/authTypes.d.ts +0 -17
- package/dist/mcp-server/transports/auth/core/authTypes.js +0 -5
- package/dist/mcp-server/transports/auth/core/authUtils.d.ts +0 -18
- package/dist/mcp-server/transports/auth/core/authUtils.js +0 -45
- package/dist/mcp-server/transports/auth/index.d.ts +0 -10
- package/dist/mcp-server/transports/auth/index.js +0 -9
- package/dist/mcp-server/transports/auth/strategies/jwt/jwtMiddleware.d.ts +0 -27
- package/dist/mcp-server/transports/auth/strategies/jwt/jwtMiddleware.js +0 -149
- package/dist/mcp-server/transports/auth/strategies/oauth/oauthMiddleware.d.ts +0 -20
- package/dist/mcp-server/transports/auth/strategies/oauth/oauthMiddleware.js +0 -124
- package/dist/mcp-server/transports/httpErrorHandler.d.ts +0 -26
- package/dist/mcp-server/transports/httpErrorHandler.js +0 -73
- package/dist/mcp-server/transports/httpTransport.d.ts +0 -21
- package/dist/mcp-server/transports/httpTransport.js +0 -208
- package/dist/mcp-server/transports/stdioTransport.d.ts +0 -42
- package/dist/mcp-server/transports/stdioTransport.js +0 -63
- package/dist/services/obsidianRestAPI/index.d.ts +0 -15
- package/dist/services/obsidianRestAPI/index.js +0 -17
- package/dist/services/obsidianRestAPI/methods/activeFileMethods.d.ts +0 -38
- package/dist/services/obsidianRestAPI/methods/activeFileMethods.js +0 -62
- package/dist/services/obsidianRestAPI/methods/commandMethods.d.ts +0 -22
- package/dist/services/obsidianRestAPI/methods/commandMethods.js +0 -31
- package/dist/services/obsidianRestAPI/methods/openMethods.d.ts +0 -16
- package/dist/services/obsidianRestAPI/methods/openMethods.js +0 -21
- package/dist/services/obsidianRestAPI/methods/patchMethods.d.ts +0 -37
- package/dist/services/obsidianRestAPI/methods/patchMethods.js +0 -94
- package/dist/services/obsidianRestAPI/methods/periodicNoteMethods.d.ts +0 -42
- package/dist/services/obsidianRestAPI/methods/periodicNoteMethods.js +0 -66
- package/dist/services/obsidianRestAPI/methods/searchMethods.d.ts +0 -25
- package/dist/services/obsidianRestAPI/methods/searchMethods.js +0 -36
- package/dist/services/obsidianRestAPI/methods/vaultMethods.d.ts +0 -58
- package/dist/services/obsidianRestAPI/methods/vaultMethods.js +0 -144
- package/dist/services/obsidianRestAPI/service.d.ts +0 -195
- package/dist/services/obsidianRestAPI/service.js +0 -379
- package/dist/services/obsidianRestAPI/types.d.ts +0 -127
- package/dist/services/obsidianRestAPI/types.js +0 -7
- package/dist/services/obsidianRestAPI/vaultCache/index.d.ts +0 -4
- package/dist/services/obsidianRestAPI/vaultCache/index.js +0 -4
- package/dist/services/obsidianRestAPI/vaultCache/service.d.ts +0 -88
- package/dist/services/obsidianRestAPI/vaultCache/service.js +0 -299
- package/dist/types-global/errors.d.ts +0 -73
- package/dist/types-global/errors.js +0 -71
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/index.js +0 -13
- package/dist/utils/internal/asyncUtils.d.ts +0 -54
- package/dist/utils/internal/asyncUtils.js +0 -101
- package/dist/utils/internal/errorHandler.d.ts +0 -176
- package/dist/utils/internal/errorHandler.js +0 -351
- package/dist/utils/internal/index.d.ts +0 -4
- package/dist/utils/internal/index.js +0 -4
- package/dist/utils/internal/logger.d.ts +0 -141
- package/dist/utils/internal/logger.js +0 -406
- package/dist/utils/internal/requestContext.d.ts +0 -83
- package/dist/utils/internal/requestContext.js +0 -72
- package/dist/utils/metrics/index.d.ts +0 -1
- package/dist/utils/metrics/index.js +0 -1
- package/dist/utils/metrics/tokenCounter.d.ts +0 -27
- package/dist/utils/metrics/tokenCounter.js +0 -128
- package/dist/utils/obsidian/index.d.ts +0 -5
- package/dist/utils/obsidian/index.js +0 -5
- package/dist/utils/obsidian/obsidianApiUtils.d.ts +0 -14
- package/dist/utils/obsidian/obsidianApiUtils.js +0 -29
- package/dist/utils/obsidian/obsidianStatUtils.d.ts +0 -68
- package/dist/utils/obsidian/obsidianStatUtils.js +0 -143
- package/dist/utils/parsing/dateParser.d.ts +0 -56
- package/dist/utils/parsing/dateParser.js +0 -104
- package/dist/utils/parsing/index.d.ts +0 -2
- package/dist/utils/parsing/index.js +0 -3
- package/dist/utils/parsing/jsonParser.d.ts +0 -80
- package/dist/utils/parsing/jsonParser.js +0 -133
- package/dist/utils/security/idGenerator.d.ts +0 -140
- package/dist/utils/security/idGenerator.js +0 -194
- package/dist/utils/security/index.d.ts +0 -3
- package/dist/utils/security/index.js +0 -3
- package/dist/utils/security/rateLimiter.d.ts +0 -156
- package/dist/utils/security/rateLimiter.js +0 -235
- package/dist/utils/security/sanitization.d.ts +0 -244
- package/dist/utils/security/sanitization.js +0 -599
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Core logic for the 'obsidian_list_notes' 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/obsidianListNotesTool/logic
|
|
6
|
-
*/
|
|
7
|
-
import path from "node:path";
|
|
8
|
-
import { z } from "zod";
|
|
9
|
-
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
|
|
10
|
-
import { logger, retryWithDelay, } from "../../../utils/index.js";
|
|
11
|
-
// ====================================================================================
|
|
12
|
-
// Schema Definitions for Input Validation
|
|
13
|
-
// ====================================================================================
|
|
14
|
-
/**
|
|
15
|
-
* Zod schema for validating the input parameters of the 'obsidian_list_notes' tool.
|
|
16
|
-
*/
|
|
17
|
-
export const ObsidianListNotesInputSchema = z
|
|
18
|
-
.object({
|
|
19
|
-
/**
|
|
20
|
-
* The vault-relative path to the directory whose contents should be listed.
|
|
21
|
-
* The path is treated as case-sensitive by the underlying Obsidian API.
|
|
22
|
-
*/
|
|
23
|
-
dirPath: z
|
|
24
|
-
.string()
|
|
25
|
-
.describe('The vault-relative path to the directory to list (e.g., "developer/atlas-mcp-server", "/" for root). Case-sensitive.'),
|
|
26
|
-
/**
|
|
27
|
-
* Optional array of file extensions (including the leading dot) to filter the results.
|
|
28
|
-
* Only files matching one of these extensions will be included. Directories are always included.
|
|
29
|
-
*/
|
|
30
|
-
fileExtensionFilter: z
|
|
31
|
-
.array(z.string().startsWith(".", "Extension must start with a dot '.'"))
|
|
32
|
-
.optional()
|
|
33
|
-
.describe('Optional array of file extensions (e.g., [".md"]) to filter files. Directories are always included.'),
|
|
34
|
-
/**
|
|
35
|
-
* Optional JavaScript-compatible regular expression pattern string to filter results by name.
|
|
36
|
-
* Only files and directories whose names match the regex will be included.
|
|
37
|
-
*/
|
|
38
|
-
nameRegexFilter: z
|
|
39
|
-
.string()
|
|
40
|
-
.nullable()
|
|
41
|
-
.optional()
|
|
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)."),
|
|
54
|
-
})
|
|
55
|
-
.describe("Input parameters for listing files and subdirectories within a specified Obsidian vault directory, with optional filtering and recursion.");
|
|
56
|
-
// ====================================================================================
|
|
57
|
-
// Helper Functions
|
|
58
|
-
// ====================================================================================
|
|
59
|
-
/**
|
|
60
|
-
* Recursively builds a formatted tree string from a nested array of FileTreeNode objects.
|
|
61
|
-
*
|
|
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.
|
|
65
|
-
*/
|
|
66
|
-
function formatTree(nodes, indent = "") {
|
|
67
|
-
let treeString = "";
|
|
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 {ObsidianListNotesInput} 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);
|
|
145
|
-
});
|
|
146
|
-
return treeNodes;
|
|
147
|
-
}
|
|
148
|
-
// ====================================================================================
|
|
149
|
-
// Core Logic Function
|
|
150
|
-
// ====================================================================================
|
|
151
|
-
/**
|
|
152
|
-
* Processes the core logic for listing files and directories recursively within the Obsidian vault.
|
|
153
|
-
*
|
|
154
|
-
* @param {ObsidianListNotesInput} params - The validated input parameters.
|
|
155
|
-
* @param {RequestContext} context - The request context for logging and correlation.
|
|
156
|
-
* @param {ObsidianRestApiService} obsidianService - An instance of the Obsidian REST API service.
|
|
157
|
-
* @returns {Promise<ObsidianListNotesResponse>} 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.
|
|
159
|
-
*/
|
|
160
|
-
export const processObsidianListNotes = async (params, context, obsidianService) => {
|
|
161
|
-
const { dirPath } = params;
|
|
162
|
-
const dirPathForLog = dirPath === "" || dirPath === "/" ? "/" : dirPath;
|
|
163
|
-
logger.debug(`Processing obsidian_list_notes request for path: ${dirPathForLog}`, { ...context, params });
|
|
164
|
-
try {
|
|
165
|
-
const effectiveDirPath = dirPath === "" ? "/" : dirPath;
|
|
166
|
-
// --- Step 1: Build the file tree recursively with retry for the initial call ---
|
|
167
|
-
const buildTreeContext = {
|
|
168
|
-
...context,
|
|
169
|
-
operation: "buildFileTreeWithRetry",
|
|
170
|
-
};
|
|
171
|
-
const shouldRetryNotFound = (err) => err instanceof McpError && err.code === BaseErrorCode.NOT_FOUND;
|
|
172
|
-
const fileTree = await retryWithDelay(() => buildFileTree(effectiveDirPath, 0, // Start at depth 0
|
|
173
|
-
params, buildTreeContext, obsidianService), {
|
|
174
|
-
operationName: "buildFileTreeWithRetry",
|
|
175
|
-
context: buildTreeContext,
|
|
176
|
-
maxRetries: 3,
|
|
177
|
-
delayMs: 300,
|
|
178
|
-
shouldRetry: shouldRetryNotFound,
|
|
179
|
-
});
|
|
180
|
-
// --- Step 2: Format the tree and count entries ---
|
|
181
|
-
const formatContext = { ...context, operation: "formatResponse" };
|
|
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 ---
|
|
192
|
-
const response = {
|
|
193
|
-
directoryPath: dirPathForLog,
|
|
194
|
-
tree: tree.trimEnd(), // Remove trailing newline
|
|
195
|
-
totalEntries: count,
|
|
196
|
-
};
|
|
197
|
-
logger.debug(`Successfully processed list request for ${dirPathForLog}. Found ${count} entries.`, context);
|
|
198
|
-
return response;
|
|
199
|
-
}
|
|
200
|
-
catch (error) {
|
|
201
|
-
if (error instanceof McpError) {
|
|
202
|
-
// Provide a more specific message if the directory wasn't found after retries
|
|
203
|
-
if (error.code === BaseErrorCode.NOT_FOUND) {
|
|
204
|
-
const notFoundMsg = `Directory not found after retries: ${dirPathForLog}`;
|
|
205
|
-
logger.error(notFoundMsg, error, context);
|
|
206
|
-
throw new McpError(error.code, notFoundMsg, context);
|
|
207
|
-
}
|
|
208
|
-
logger.error(`McpError during file listing for ${dirPathForLog}: ${error.message}`, error, context);
|
|
209
|
-
throw error;
|
|
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);
|
|
214
|
-
}
|
|
215
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Registers the 'obsidian_list_notes' 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/obsidianListNotesTool/registration
|
|
6
|
-
*/
|
|
7
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
-
import { ObsidianRestApiService } from "../../../services/obsidianRestAPI/index.js";
|
|
9
|
-
/**
|
|
10
|
-
* Registers the 'obsidian_list_notes' tool with the MCP server.
|
|
11
|
-
*
|
|
12
|
-
* This tool lists the files and subdirectories within a specified directory
|
|
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.
|
|
16
|
-
*
|
|
17
|
-
* @param {McpServer} server - The MCP server instance to register the tool with.
|
|
18
|
-
* @param {ObsidianRestApiService} obsidianService - An instance of the Obsidian REST API service
|
|
19
|
-
* used to interact with the user's Obsidian vault.
|
|
20
|
-
* @returns {Promise<void>} A promise that resolves when the tool registration is complete or rejects on error.
|
|
21
|
-
* @throws {McpError} Throws an McpError if registration fails critically.
|
|
22
|
-
*/
|
|
23
|
-
export declare const registerObsidianListNotesTool: (server: McpServer, obsidianService: ObsidianRestApiService) => Promise<void>;
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Registers the 'obsidian_list_notes' 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/obsidianListNotesTool/registration
|
|
6
|
-
*/
|
|
7
|
-
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
|
|
8
|
-
import { ErrorHandler, logger, requestContextService, } from "../../../utils/index.js";
|
|
9
|
-
import { ObsidianListNotesInputSchema, processObsidianListNotes, } from "./logic.js";
|
|
10
|
-
/**
|
|
11
|
-
* Registers the 'obsidian_list_notes' tool with the MCP server.
|
|
12
|
-
*
|
|
13
|
-
* This tool lists the files and subdirectories within a specified directory
|
|
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.
|
|
17
|
-
*
|
|
18
|
-
* @param {McpServer} server - The MCP server instance to register the tool with.
|
|
19
|
-
* @param {ObsidianRestApiService} obsidianService - An instance of the Obsidian REST API service
|
|
20
|
-
* used to interact with the user's Obsidian vault.
|
|
21
|
-
* @returns {Promise<void>} A promise that resolves when the tool registration is complete or rejects on error.
|
|
22
|
-
* @throws {McpError} Throws an McpError if registration fails critically.
|
|
23
|
-
*/
|
|
24
|
-
export const registerObsidianListNotesTool = async (server, obsidianService) => {
|
|
25
|
-
const toolName = "obsidian_list_notes";
|
|
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.";
|
|
27
|
-
// Create a context specifically for the registration process.
|
|
28
|
-
const registrationContext = requestContextService.createRequestContext({
|
|
29
|
-
operation: "RegisterObsidianListNotesTool",
|
|
30
|
-
toolName: toolName,
|
|
31
|
-
module: "ObsidianListNotesRegistration", // Identify the module
|
|
32
|
-
});
|
|
33
|
-
logger.info(`Attempting to register tool: ${toolName}`, registrationContext);
|
|
34
|
-
// Wrap the registration logic in a tryCatch block for robust error handling during server setup.
|
|
35
|
-
await ErrorHandler.tryCatch(async () => {
|
|
36
|
-
// Use the high-level SDK method `server.tool` for registration.
|
|
37
|
-
server.tool(toolName, toolDescription, ObsidianListNotesInputSchema.shape, // Provide the Zod schema shape for input definition.
|
|
38
|
-
/**
|
|
39
|
-
* The handler function executed when the 'obsidian_list_notes' tool is called by the client.
|
|
40
|
-
*
|
|
41
|
-
* @param {ObsidianListNotesInput} params - The input parameters received from the client,
|
|
42
|
-
* validated against the ObsidianListNotesInputSchema shape.
|
|
43
|
-
* @returns {Promise<CallToolResult>} A promise resolving to the structured result for the MCP client,
|
|
44
|
-
* containing either the successful response data (serialized JSON) or an error indication.
|
|
45
|
-
*/
|
|
46
|
-
async (params) => {
|
|
47
|
-
// Type matches the inferred input schema
|
|
48
|
-
// Create a specific context for this handler invocation.
|
|
49
|
-
const handlerContext = requestContextService.createRequestContext({
|
|
50
|
-
parentContext: registrationContext, // Link to registration context
|
|
51
|
-
operation: "HandleObsidianListNotesRequest",
|
|
52
|
-
toolName: toolName,
|
|
53
|
-
params: {
|
|
54
|
-
// Log all relevant parameters for debugging
|
|
55
|
-
dirPath: params.dirPath,
|
|
56
|
-
fileExtensionFilter: params.fileExtensionFilter,
|
|
57
|
-
nameRegexFilter: params.nameRegexFilter,
|
|
58
|
-
recursionDepth: params.recursionDepth,
|
|
59
|
-
},
|
|
60
|
-
});
|
|
61
|
-
logger.debug(`Handling '${toolName}' request`, handlerContext);
|
|
62
|
-
// Wrap the core logic execution in a tryCatch block.
|
|
63
|
-
return await ErrorHandler.tryCatch(async () => {
|
|
64
|
-
// Delegate the actual file listing and filtering logic to the processing function.
|
|
65
|
-
const response = await processObsidianListNotes(params, handlerContext, obsidianService);
|
|
66
|
-
logger.debug(`'${toolName}' processed successfully`, handlerContext);
|
|
67
|
-
// Format the successful response object from the logic function into the required MCP CallToolResult structure.
|
|
68
|
-
return {
|
|
69
|
-
content: [
|
|
70
|
-
{
|
|
71
|
-
type: "text", // Standard content type for structured JSON data
|
|
72
|
-
text: JSON.stringify(response, null, 2), // Pretty-print JSON
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
isError: false, // Indicate successful execution
|
|
76
|
-
};
|
|
77
|
-
}, {
|
|
78
|
-
// Configuration for the inner error handler (processing logic).
|
|
79
|
-
operation: `processing ${toolName} handler`,
|
|
80
|
-
context: handlerContext,
|
|
81
|
-
input: params, // Log the full input parameters if an error occurs.
|
|
82
|
-
// Custom error mapping for consistent error reporting.
|
|
83
|
-
errorMapper: (error) => new McpError(error instanceof McpError
|
|
84
|
-
? error.code
|
|
85
|
-
: BaseErrorCode.INTERNAL_ERROR, `Error processing ${toolName} tool: ${error instanceof Error ? error.message : "Unknown error"}`, { ...handlerContext }),
|
|
86
|
-
}); // End of inner ErrorHandler.tryCatch
|
|
87
|
-
}); // End of server.tool call
|
|
88
|
-
logger.info(`Tool registered successfully: ${toolName}`, registrationContext);
|
|
89
|
-
}, {
|
|
90
|
-
// Configuration for the outer error handler (registration process).
|
|
91
|
-
operation: `registering tool ${toolName}`,
|
|
92
|
-
context: registrationContext,
|
|
93
|
-
errorCode: BaseErrorCode.INTERNAL_ERROR, // Default error code for registration failure.
|
|
94
|
-
// Custom error mapping for registration failures.
|
|
95
|
-
errorMapper: (error) => new McpError(error instanceof McpError ? error.code : BaseErrorCode.INTERNAL_ERROR, `Failed to register tool '${toolName}': ${error instanceof Error ? error.message : "Unknown error"}`, { ...registrationContext }),
|
|
96
|
-
critical: true, // Treat registration failure as critical.
|
|
97
|
-
}); // End of outer ErrorHandler.tryCatch
|
|
98
|
-
};
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
export { ObsidianManageFrontmatterInputSchemaShape, processObsidianManageFrontmatter, } from "./logic.js";
|
|
2
|
-
export type { ObsidianManageFrontmatterInput, ObsidianManageFrontmatterResponse, } from "./logic.js";
|
|
3
|
-
export { registerObsidianManageFrontmatterTool } from "./registration.js";
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { ObsidianRestApiService, VaultCacheService } from "../../../services/obsidianRestAPI/index.js";
|
|
3
|
-
import { RequestContext } from "../../../utils/index.js";
|
|
4
|
-
export declare const ObsidianManageFrontmatterInputSchemaShape: {
|
|
5
|
-
filePath: z.ZodString;
|
|
6
|
-
operation: z.ZodEnum<["get", "set", "delete"]>;
|
|
7
|
-
key: z.ZodString;
|
|
8
|
-
value: z.ZodOptional<z.ZodAny>;
|
|
9
|
-
};
|
|
10
|
-
export declare const ManageFrontmatterInputSchema: z.ZodEffects<z.ZodObject<{
|
|
11
|
-
filePath: z.ZodString;
|
|
12
|
-
operation: z.ZodEnum<["get", "set", "delete"]>;
|
|
13
|
-
key: z.ZodString;
|
|
14
|
-
value: z.ZodOptional<z.ZodAny>;
|
|
15
|
-
}, "strip", z.ZodTypeAny, {
|
|
16
|
-
operation: "get" | "delete" | "set";
|
|
17
|
-
key: string;
|
|
18
|
-
filePath: string;
|
|
19
|
-
value?: any;
|
|
20
|
-
}, {
|
|
21
|
-
operation: "get" | "delete" | "set";
|
|
22
|
-
key: string;
|
|
23
|
-
filePath: string;
|
|
24
|
-
value?: any;
|
|
25
|
-
}>, {
|
|
26
|
-
operation: "get" | "delete" | "set";
|
|
27
|
-
key: string;
|
|
28
|
-
filePath: string;
|
|
29
|
-
value?: any;
|
|
30
|
-
}, {
|
|
31
|
-
operation: "get" | "delete" | "set";
|
|
32
|
-
key: string;
|
|
33
|
-
filePath: string;
|
|
34
|
-
value?: any;
|
|
35
|
-
}>;
|
|
36
|
-
export type ObsidianManageFrontmatterInput = z.infer<typeof ManageFrontmatterInputSchema>;
|
|
37
|
-
export interface ObsidianManageFrontmatterResponse {
|
|
38
|
-
success: boolean;
|
|
39
|
-
message: string;
|
|
40
|
-
value?: any;
|
|
41
|
-
}
|
|
42
|
-
export declare const processObsidianManageFrontmatter: (params: ObsidianManageFrontmatterInput, context: RequestContext, obsidianService: ObsidianRestApiService, vaultCacheService: VaultCacheService | undefined) => Promise<ObsidianManageFrontmatterResponse>;
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { dump } from "js-yaml";
|
|
3
|
-
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
|
|
4
|
-
import { logger, retryWithDelay, } from "../../../utils/index.js";
|
|
5
|
-
// ====================================================================================
|
|
6
|
-
// Schema Definitions
|
|
7
|
-
// ====================================================================================
|
|
8
|
-
const ManageFrontmatterInputSchemaBase = z.object({
|
|
9
|
-
filePath: z
|
|
10
|
-
.string()
|
|
11
|
-
.min(1)
|
|
12
|
-
.describe("The vault-relative path to the target note (e.g., 'Projects/Active/My Note.md')."),
|
|
13
|
-
operation: z
|
|
14
|
-
.enum(["get", "set", "delete"])
|
|
15
|
-
.describe("The operation to perform on the frontmatter: 'get' to read a key, 'set' to create or update a key, or 'delete' to remove a key."),
|
|
16
|
-
key: z
|
|
17
|
-
.string()
|
|
18
|
-
.min(1)
|
|
19
|
-
.describe("The name of the frontmatter key to target, such as 'status', 'tags', or 'aliases'."),
|
|
20
|
-
value: z
|
|
21
|
-
.any()
|
|
22
|
-
.optional()
|
|
23
|
-
.describe("The value to assign when using the 'set' operation. Can be a string, number, boolean, array, or a JSON object."),
|
|
24
|
-
});
|
|
25
|
-
export const ObsidianManageFrontmatterInputSchemaShape = ManageFrontmatterInputSchemaBase.shape;
|
|
26
|
-
export const ManageFrontmatterInputSchema = ManageFrontmatterInputSchemaBase.refine((data) => {
|
|
27
|
-
if (data.operation === "set" && data.value === undefined) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
return true;
|
|
31
|
-
}, {
|
|
32
|
-
message: "A 'value' is required when the 'operation' is 'set'.",
|
|
33
|
-
path: ["value"],
|
|
34
|
-
});
|
|
35
|
-
// ====================================================================================
|
|
36
|
-
// Core Logic Function
|
|
37
|
-
// ====================================================================================
|
|
38
|
-
export const processObsidianManageFrontmatter = async (params, context, obsidianService, vaultCacheService) => {
|
|
39
|
-
logger.debug(`Processing obsidian_manage_frontmatter request`, {
|
|
40
|
-
...context,
|
|
41
|
-
operation: params.operation,
|
|
42
|
-
filePath: params.filePath,
|
|
43
|
-
key: params.key,
|
|
44
|
-
});
|
|
45
|
-
const { filePath, operation, key, value } = params;
|
|
46
|
-
const shouldRetryNotFound = (err) => err instanceof McpError && err.code === BaseErrorCode.NOT_FOUND;
|
|
47
|
-
const getFileWithRetry = async (opContext, format = "json") => {
|
|
48
|
-
return await retryWithDelay(() => obsidianService.getFileContent(filePath, format, opContext), {
|
|
49
|
-
operationName: `getFileContentForFrontmatter`,
|
|
50
|
-
context: opContext,
|
|
51
|
-
maxRetries: 3,
|
|
52
|
-
delayMs: 300,
|
|
53
|
-
shouldRetry: shouldRetryNotFound,
|
|
54
|
-
});
|
|
55
|
-
};
|
|
56
|
-
switch (operation) {
|
|
57
|
-
case "get": {
|
|
58
|
-
const note = (await getFileWithRetry(context));
|
|
59
|
-
const frontmatter = note.frontmatter ?? {};
|
|
60
|
-
const retrievedValue = frontmatter[key];
|
|
61
|
-
return {
|
|
62
|
-
success: true,
|
|
63
|
-
message: `Successfully retrieved key '${key}' from frontmatter.`,
|
|
64
|
-
value: retrievedValue,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
case "set": {
|
|
68
|
-
const patchOptions = {
|
|
69
|
-
operation: "replace",
|
|
70
|
-
targetType: "frontmatter",
|
|
71
|
-
target: key,
|
|
72
|
-
createTargetIfMissing: true,
|
|
73
|
-
contentType: typeof value === "object" ? "application/json" : "text/markdown",
|
|
74
|
-
};
|
|
75
|
-
const content = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
76
|
-
await retryWithDelay(() => obsidianService.patchFile(filePath, content, patchOptions, context), {
|
|
77
|
-
operationName: `patchFileForFrontmatterSet`,
|
|
78
|
-
context,
|
|
79
|
-
maxRetries: 3,
|
|
80
|
-
delayMs: 300,
|
|
81
|
-
shouldRetry: shouldRetryNotFound,
|
|
82
|
-
});
|
|
83
|
-
if (vaultCacheService) {
|
|
84
|
-
await vaultCacheService.updateCacheForFile(filePath, context);
|
|
85
|
-
}
|
|
86
|
-
return {
|
|
87
|
-
success: true,
|
|
88
|
-
message: `Successfully set key '${key}' in frontmatter.`,
|
|
89
|
-
value: { [key]: value },
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
case "delete": {
|
|
93
|
-
// Note on deletion strategy: The Obsidian REST API's PATCH endpoint for frontmatter
|
|
94
|
-
// supports adding/updating keys but does not have a dedicated "delete key" operation.
|
|
95
|
-
// Therefore, deletion is handled by reading the note content, parsing the frontmatter,
|
|
96
|
-
// removing the key from the JavaScript object, and then overwriting the entire note
|
|
97
|
-
// with the updated frontmatter block. This regex-based replacement is a workaround
|
|
98
|
-
// for the current API limitations.
|
|
99
|
-
const noteJson = (await getFileWithRetry(context, "json"));
|
|
100
|
-
const frontmatter = noteJson.frontmatter;
|
|
101
|
-
if (!frontmatter || frontmatter[key] === undefined) {
|
|
102
|
-
return {
|
|
103
|
-
success: true,
|
|
104
|
-
message: `Key '${key}' not found in frontmatter. No action taken.`,
|
|
105
|
-
value: {},
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
delete frontmatter[key];
|
|
109
|
-
const noteContent = (await getFileWithRetry(context, "markdown"));
|
|
110
|
-
const frontmatterRegex = /^---\n([\s\S]*?)\n---\n/;
|
|
111
|
-
const match = noteContent.match(frontmatterRegex);
|
|
112
|
-
let newContent;
|
|
113
|
-
const newFrontmatterString = Object.keys(frontmatter).length > 0 ? dump(frontmatter) : "";
|
|
114
|
-
if (match) {
|
|
115
|
-
// Frontmatter exists, replace it
|
|
116
|
-
if (newFrontmatterString) {
|
|
117
|
-
newContent = noteContent.replace(frontmatterRegex, `---\n${newFrontmatterString}---\n`);
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
// If frontmatter is now empty, remove the block entirely
|
|
121
|
-
newContent = noteContent.replace(frontmatterRegex, "");
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
// This case should be rare given the initial check, but handle it defensively
|
|
126
|
-
logger.warning("Frontmatter key existed in JSON but block not found in markdown. No action taken.", context);
|
|
127
|
-
return {
|
|
128
|
-
success: false,
|
|
129
|
-
message: `Could not find frontmatter block to update, though key '${key}' was detected.`,
|
|
130
|
-
value: {},
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
await retryWithDelay(() => obsidianService.updateFileContent(filePath, newContent, context), {
|
|
134
|
-
operationName: `updateFileForFrontmatterDelete`,
|
|
135
|
-
context,
|
|
136
|
-
maxRetries: 3,
|
|
137
|
-
delayMs: 300,
|
|
138
|
-
shouldRetry: shouldRetryNotFound,
|
|
139
|
-
});
|
|
140
|
-
if (vaultCacheService) {
|
|
141
|
-
await vaultCacheService.updateCacheForFile(filePath, context);
|
|
142
|
-
}
|
|
143
|
-
return {
|
|
144
|
-
success: true,
|
|
145
|
-
message: `Successfully deleted key '${key}' from frontmatter.`,
|
|
146
|
-
value: {},
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
default:
|
|
150
|
-
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid operation: ${operation}`, context);
|
|
151
|
-
}
|
|
152
|
-
};
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import { ObsidianRestApiService, VaultCacheService } from "../../../services/obsidianRestAPI/index.js";
|
|
3
|
-
export declare const registerObsidianManageFrontmatterTool: (server: McpServer, obsidianService: ObsidianRestApiService, vaultCacheService: VaultCacheService | undefined) => Promise<void>;
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
|
|
2
|
-
import { ErrorHandler, logger, requestContextService, } from "../../../utils/index.js";
|
|
3
|
-
import { ManageFrontmatterInputSchema, ObsidianManageFrontmatterInputSchemaShape, processObsidianManageFrontmatter, } from "./logic.js";
|
|
4
|
-
export const registerObsidianManageFrontmatterTool = async (server, obsidianService, vaultCacheService) => {
|
|
5
|
-
const toolName = "obsidian_manage_frontmatter";
|
|
6
|
-
const toolDescription = "Atomically manages a note's YAML frontmatter. Supports getting, setting (creating/updating), and deleting specific keys without rewriting the entire file. Ideal for efficient metadata operations on primitive or structured Obsidian frontmatter data.";
|
|
7
|
-
const registrationContext = requestContextService.createRequestContext({
|
|
8
|
-
operation: "RegisterObsidianManageFrontmatterTool",
|
|
9
|
-
toolName: toolName,
|
|
10
|
-
module: "ObsidianManageFrontmatterRegistration",
|
|
11
|
-
});
|
|
12
|
-
logger.info(`Attempting to register tool: ${toolName}`, registrationContext);
|
|
13
|
-
await ErrorHandler.tryCatch(async () => {
|
|
14
|
-
server.tool(toolName, toolDescription, ObsidianManageFrontmatterInputSchemaShape, async (params) => {
|
|
15
|
-
const handlerContext = requestContextService.createRequestContext({
|
|
16
|
-
parentContext: registrationContext,
|
|
17
|
-
operation: "HandleObsidianManageFrontmatterRequest",
|
|
18
|
-
toolName: toolName,
|
|
19
|
-
params: params,
|
|
20
|
-
});
|
|
21
|
-
logger.debug(`Handling '${toolName}' request`, handlerContext);
|
|
22
|
-
return await ErrorHandler.tryCatch(async () => {
|
|
23
|
-
const validatedParams = ManageFrontmatterInputSchema.parse(params);
|
|
24
|
-
const response = await processObsidianManageFrontmatter(validatedParams, handlerContext, obsidianService, vaultCacheService);
|
|
25
|
-
logger.debug(`'${toolName}' processed successfully`, handlerContext);
|
|
26
|
-
return {
|
|
27
|
-
content: [
|
|
28
|
-
{
|
|
29
|
-
type: "text",
|
|
30
|
-
text: JSON.stringify(response, null, 2),
|
|
31
|
-
},
|
|
32
|
-
],
|
|
33
|
-
isError: false,
|
|
34
|
-
};
|
|
35
|
-
}, {
|
|
36
|
-
operation: `processing ${toolName} handler`,
|
|
37
|
-
context: handlerContext,
|
|
38
|
-
input: params,
|
|
39
|
-
errorMapper: (error) => new McpError(error instanceof McpError
|
|
40
|
-
? error.code
|
|
41
|
-
: BaseErrorCode.INTERNAL_ERROR, `Error processing ${toolName} tool: ${error instanceof Error ? error.message : "Unknown error"}`, { ...handlerContext }),
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
logger.info(`Tool registered successfully: ${toolName}`, registrationContext);
|
|
45
|
-
}, {
|
|
46
|
-
operation: `registering tool ${toolName}`,
|
|
47
|
-
context: registrationContext,
|
|
48
|
-
errorCode: BaseErrorCode.INTERNAL_ERROR,
|
|
49
|
-
errorMapper: (error) => new McpError(error instanceof McpError ? error.code : BaseErrorCode.INTERNAL_ERROR, `Failed to register tool '${toolName}': ${error instanceof Error ? error.message : "Unknown error"}`, { ...registrationContext }),
|
|
50
|
-
critical: true,
|
|
51
|
-
});
|
|
52
|
-
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { ObsidianRestApiService, VaultCacheService } from "../../../services/obsidianRestAPI/index.js";
|
|
3
|
-
import { RequestContext } from "../../../utils/index.js";
|
|
4
|
-
export declare const ObsidianManageTagsInputSchemaShape: {
|
|
5
|
-
filePath: z.ZodString;
|
|
6
|
-
operation: z.ZodEnum<["add", "remove", "list"]>;
|
|
7
|
-
tags: z.ZodArray<z.ZodString, "many">;
|
|
8
|
-
};
|
|
9
|
-
export declare const ManageTagsInputSchema: z.ZodObject<{
|
|
10
|
-
filePath: z.ZodString;
|
|
11
|
-
operation: z.ZodEnum<["add", "remove", "list"]>;
|
|
12
|
-
tags: z.ZodArray<z.ZodString, "many">;
|
|
13
|
-
}, "strip", z.ZodTypeAny, {
|
|
14
|
-
operation: "add" | "remove" | "list";
|
|
15
|
-
filePath: string;
|
|
16
|
-
tags: string[];
|
|
17
|
-
}, {
|
|
18
|
-
operation: "add" | "remove" | "list";
|
|
19
|
-
filePath: string;
|
|
20
|
-
tags: string[];
|
|
21
|
-
}>;
|
|
22
|
-
export type ObsidianManageTagsInput = z.infer<typeof ManageTagsInputSchema>;
|
|
23
|
-
export interface ObsidianManageTagsResponse {
|
|
24
|
-
success: boolean;
|
|
25
|
-
message: string;
|
|
26
|
-
currentTags: string[];
|
|
27
|
-
}
|
|
28
|
-
export declare const processObsidianManageTags: (params: ObsidianManageTagsInput, context: RequestContext, obsidianService: ObsidianRestApiService, vaultCacheService: VaultCacheService | undefined) => Promise<ObsidianManageTagsResponse>;
|