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,299 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module VaultCacheService
|
|
3
|
-
* @description Service for building and managing an in-memory cache of Obsidian vault content.
|
|
4
|
-
*/
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import { config } from "../../../config/index.js";
|
|
7
|
-
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
|
|
8
|
-
import { logger, requestContextService, retryWithDelay, } from "../../../utils/index.js";
|
|
9
|
-
/**
|
|
10
|
-
* Manages an in-memory cache of the Obsidian vault's file structure and metadata.
|
|
11
|
-
*
|
|
12
|
-
* __Is the cache safe and secure?__
|
|
13
|
-
* Yes, the cache is safe and secure for its purpose within this application. Here's why:
|
|
14
|
-
* 1. __In-Memory Storage:__ The cache exists only in the server's memory. It is not written to disk or transmitted over the network, so its attack surface is limited to the server process itself.
|
|
15
|
-
* 2. __Local Data Source:__ The data populating the cache comes directly from your own Obsidian vault via the local REST API. It is not fetching data from external, untrusted sources.
|
|
16
|
-
*
|
|
17
|
-
* __Warning: High Memory Usage__
|
|
18
|
-
* This service stores the entire content of every markdown file in the vault in memory. For users with very large vaults (e.g., many gigabytes of markdown files), this can lead to significant RAM consumption. If you experience high memory usage, consider disabling the cache via the `OBSIDIAN_ENABLE_CACHE` environment variable.
|
|
19
|
-
*/
|
|
20
|
-
export class VaultCacheService {
|
|
21
|
-
constructor(obsidianService) {
|
|
22
|
-
this.vaultContentCache = new Map();
|
|
23
|
-
this.isCacheReady = false;
|
|
24
|
-
this.isBuilding = false;
|
|
25
|
-
this.refreshIntervalId = null;
|
|
26
|
-
this.obsidianService = obsidianService;
|
|
27
|
-
logger.info("VaultCacheService initialized.", requestContextService.createRequestContext({
|
|
28
|
-
operation: "VaultCacheServiceInit",
|
|
29
|
-
}));
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Starts the periodic cache refresh mechanism.
|
|
33
|
-
* The interval is controlled by the `OBSIDIAN_CACHE_REFRESH_INTERVAL_MIN` config setting.
|
|
34
|
-
*/
|
|
35
|
-
startPeriodicRefresh() {
|
|
36
|
-
const refreshIntervalMs = config.obsidianCacheRefreshIntervalMin * 60 * 1000;
|
|
37
|
-
if (this.refreshIntervalId) {
|
|
38
|
-
logger.warning("Periodic refresh is already running.", requestContextService.createRequestContext({
|
|
39
|
-
operation: "startPeriodicRefresh",
|
|
40
|
-
}));
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
this.refreshIntervalId = setInterval(() => this.refreshCache(), refreshIntervalMs);
|
|
44
|
-
logger.info(`Vault cache periodic refresh scheduled every ${config.obsidianCacheRefreshIntervalMin} minutes.`, requestContextService.createRequestContext({
|
|
45
|
-
operation: "startPeriodicRefresh",
|
|
46
|
-
}));
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Stops the periodic cache refresh mechanism.
|
|
50
|
-
* Should be called during graceful shutdown.
|
|
51
|
-
*/
|
|
52
|
-
stopPeriodicRefresh() {
|
|
53
|
-
const context = requestContextService.createRequestContext({
|
|
54
|
-
operation: "stopPeriodicRefresh",
|
|
55
|
-
});
|
|
56
|
-
if (this.refreshIntervalId) {
|
|
57
|
-
clearInterval(this.refreshIntervalId);
|
|
58
|
-
this.refreshIntervalId = null;
|
|
59
|
-
logger.info("Stopped periodic cache refresh.", context);
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
logger.info("Periodic cache refresh was not running.", context);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Checks if the cache has been successfully built.
|
|
67
|
-
* @returns {boolean} True if the cache is ready, false otherwise.
|
|
68
|
-
*/
|
|
69
|
-
isReady() {
|
|
70
|
-
return this.isCacheReady;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Checks if the cache is currently being built.
|
|
74
|
-
* @returns {boolean} True if the cache build is in progress, false otherwise.
|
|
75
|
-
*/
|
|
76
|
-
getIsBuilding() {
|
|
77
|
-
return this.isBuilding;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Returns the entire vault content cache.
|
|
81
|
-
* Use with caution for large vaults due to potential memory usage.
|
|
82
|
-
* @returns {ReadonlyMap<string, CacheEntry>} The cache map.
|
|
83
|
-
*/
|
|
84
|
-
getCache() {
|
|
85
|
-
// Return a readonly view or copy if mutation is a concern
|
|
86
|
-
return this.vaultContentCache;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Retrieves a specific entry from the cache.
|
|
90
|
-
* @param {string} filePath - The vault-relative path of the file.
|
|
91
|
-
* @returns {CacheEntry | undefined} The cache entry or undefined if not found.
|
|
92
|
-
*/
|
|
93
|
-
getEntry(filePath) {
|
|
94
|
-
return this.vaultContentCache.get(filePath);
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Immediately fetches the latest data for a single file and updates its entry in the cache.
|
|
98
|
-
* This is useful for ensuring cache consistency immediately after a file modification.
|
|
99
|
-
* @param {string} filePath - The vault-relative path of the file to update.
|
|
100
|
-
* @param {RequestContext} context - The request context for logging.
|
|
101
|
-
*/
|
|
102
|
-
async updateCacheForFile(filePath, context) {
|
|
103
|
-
const opContext = { ...context, operation: "updateCacheForFile", filePath };
|
|
104
|
-
logger.debug(`Proactively updating cache for file: ${filePath}`, opContext);
|
|
105
|
-
try {
|
|
106
|
-
const noteJson = await retryWithDelay(() => this.obsidianService.getFileContent(filePath, "json", opContext), {
|
|
107
|
-
operationName: "proactiveCacheUpdate",
|
|
108
|
-
context: opContext,
|
|
109
|
-
maxRetries: 3,
|
|
110
|
-
delayMs: 300,
|
|
111
|
-
shouldRetry: (err) => err instanceof McpError &&
|
|
112
|
-
(err.code === BaseErrorCode.NOT_FOUND ||
|
|
113
|
-
err.code === BaseErrorCode.SERVICE_UNAVAILABLE),
|
|
114
|
-
});
|
|
115
|
-
if (noteJson && noteJson.content && noteJson.stat) {
|
|
116
|
-
this.vaultContentCache.set(filePath, {
|
|
117
|
-
content: noteJson.content,
|
|
118
|
-
mtime: noteJson.stat.mtime,
|
|
119
|
-
});
|
|
120
|
-
logger.info(`Proactively updated cache for: ${filePath}`, opContext);
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
logger.warning(`Proactive cache update for ${filePath} received invalid data, skipping update.`, opContext);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
catch (error) {
|
|
127
|
-
// If the file was deleted, a NOT_FOUND error is expected. We should remove it from the cache.
|
|
128
|
-
if (error instanceof McpError && error.code === BaseErrorCode.NOT_FOUND) {
|
|
129
|
-
if (this.vaultContentCache.has(filePath)) {
|
|
130
|
-
this.vaultContentCache.delete(filePath);
|
|
131
|
-
logger.info(`Proactively removed deleted file from cache: ${filePath}`, opContext);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
logger.error(`Failed to proactively update cache for ${filePath}. Error: ${error instanceof Error ? error.message : String(error)}`, opContext);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Builds the in-memory cache by fetching all markdown files and their content.
|
|
141
|
-
* This is intended to be run once at startup. Subsequent updates are handled by `refreshCache`.
|
|
142
|
-
*/
|
|
143
|
-
async buildVaultCache() {
|
|
144
|
-
const initialBuildContext = requestContextService.createRequestContext({
|
|
145
|
-
operation: "buildVaultCache.initialCheck",
|
|
146
|
-
});
|
|
147
|
-
if (this.isBuilding) {
|
|
148
|
-
logger.warning("Cache build already in progress. Skipping.", initialBuildContext);
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
if (this.isCacheReady) {
|
|
152
|
-
logger.info("Cache already built. Skipping.", initialBuildContext);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
await this.refreshCache(true); // Perform an initial, full build
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Refreshes the cache by comparing remote file modification times with cached ones.
|
|
159
|
-
* Only fetches content for new or updated files.
|
|
160
|
-
* @param isInitialBuild - If true, forces a full build and sets the cache readiness flag.
|
|
161
|
-
*/
|
|
162
|
-
async refreshCache(isInitialBuild = false) {
|
|
163
|
-
const context = requestContextService.createRequestContext({
|
|
164
|
-
operation: "refreshCache",
|
|
165
|
-
isInitialBuild,
|
|
166
|
-
});
|
|
167
|
-
if (this.isBuilding) {
|
|
168
|
-
logger.warning("Cache refresh already in progress. Skipping.", context);
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
this.isBuilding = true;
|
|
172
|
-
if (isInitialBuild) {
|
|
173
|
-
this.isCacheReady = false;
|
|
174
|
-
}
|
|
175
|
-
logger.info("Starting vault cache refresh process...", context);
|
|
176
|
-
try {
|
|
177
|
-
const startTime = Date.now();
|
|
178
|
-
const remoteFiles = await this.listAllMarkdownFiles("/", context);
|
|
179
|
-
const remoteFileSet = new Set(remoteFiles);
|
|
180
|
-
const cachedFileSet = new Set(this.vaultContentCache.keys());
|
|
181
|
-
let filesAdded = 0;
|
|
182
|
-
let filesUpdated = 0;
|
|
183
|
-
let filesRemoved = 0;
|
|
184
|
-
// 1. Remove deleted files from cache
|
|
185
|
-
for (const cachedFile of cachedFileSet) {
|
|
186
|
-
if (!remoteFileSet.has(cachedFile)) {
|
|
187
|
-
this.vaultContentCache.delete(cachedFile);
|
|
188
|
-
filesRemoved++;
|
|
189
|
-
logger.debug(`Removed deleted file from cache: ${cachedFile}`, {
|
|
190
|
-
...context,
|
|
191
|
-
filePath: cachedFile,
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
// 2. Check for new or updated files
|
|
196
|
-
for (const filePath of remoteFiles) {
|
|
197
|
-
try {
|
|
198
|
-
const fileMetadata = await this.obsidianService.getFileMetadata(filePath, context);
|
|
199
|
-
if (!fileMetadata) {
|
|
200
|
-
logger.warning(`Skipping file during cache refresh due to missing or invalid metadata: ${filePath}`, { ...context, filePath });
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
const remoteMtime = fileMetadata.mtime;
|
|
204
|
-
const cachedEntry = this.vaultContentCache.get(filePath);
|
|
205
|
-
if (!cachedEntry || cachedEntry.mtime < remoteMtime) {
|
|
206
|
-
const noteJson = (await this.obsidianService.getFileContent(filePath, "json", context));
|
|
207
|
-
this.vaultContentCache.set(filePath, {
|
|
208
|
-
content: noteJson.content,
|
|
209
|
-
mtime: noteJson.stat.mtime,
|
|
210
|
-
});
|
|
211
|
-
if (!cachedEntry) {
|
|
212
|
-
filesAdded++;
|
|
213
|
-
logger.debug(`Added new file to cache: ${filePath}`, {
|
|
214
|
-
...context,
|
|
215
|
-
filePath,
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
filesUpdated++;
|
|
220
|
-
logger.debug(`Updated modified file in cache: ${filePath}`, {
|
|
221
|
-
...context,
|
|
222
|
-
filePath,
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
catch (error) {
|
|
228
|
-
logger.error(`Failed to process file during cache refresh: ${filePath}. Skipping. Error: ${error instanceof Error ? error.message : String(error)}`, { ...context, filePath });
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
const duration = (Date.now() - startTime) / 1000;
|
|
232
|
-
if (isInitialBuild) {
|
|
233
|
-
this.isCacheReady = true;
|
|
234
|
-
logger.info(`Initial vault cache build completed in ${duration.toFixed(2)}s. Cached ${this.vaultContentCache.size} files.`, context);
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
logger.info(`Vault cache refresh completed in ${duration.toFixed(2)}s. Added: ${filesAdded}, Updated: ${filesUpdated}, Removed: ${filesRemoved}. Total cached: ${this.vaultContentCache.size}.`, context);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
catch (error) {
|
|
241
|
-
logger.error(`Critical error during vault cache refresh. Cache may be incomplete. Error: ${error instanceof Error ? error.message : String(error)}`, context);
|
|
242
|
-
if (isInitialBuild) {
|
|
243
|
-
this.isCacheReady = false;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
finally {
|
|
247
|
-
this.isBuilding = false;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Helper to recursively list all markdown files. Similar to the one in search logic.
|
|
252
|
-
* @param dirPath - Starting directory path.
|
|
253
|
-
* @param context - Request context.
|
|
254
|
-
* @param visitedDirs - Set to track visited directories.
|
|
255
|
-
* @returns Array of file paths.
|
|
256
|
-
*/
|
|
257
|
-
async listAllMarkdownFiles(dirPath, context, visitedDirs = new Set()) {
|
|
258
|
-
const operation = "listAllMarkdownFiles";
|
|
259
|
-
const opContext = { ...context, operation, dirPath };
|
|
260
|
-
const normalizedPath = path.posix.normalize(dirPath === "" ? "/" : dirPath);
|
|
261
|
-
if (visitedDirs.has(normalizedPath)) {
|
|
262
|
-
logger.warning(`Cycle detected or directory already visited during cache build: ${normalizedPath}. Skipping.`, opContext);
|
|
263
|
-
return [];
|
|
264
|
-
}
|
|
265
|
-
visitedDirs.add(normalizedPath);
|
|
266
|
-
let markdownFiles = [];
|
|
267
|
-
try {
|
|
268
|
-
const entries = await this.obsidianService.listFiles(normalizedPath, opContext);
|
|
269
|
-
for (const entry of entries) {
|
|
270
|
-
const fullPath = path.posix.join(normalizedPath, entry);
|
|
271
|
-
if (entry.endsWith("/")) {
|
|
272
|
-
const subDirFiles = await this.listAllMarkdownFiles(fullPath, opContext, visitedDirs);
|
|
273
|
-
markdownFiles = markdownFiles.concat(subDirFiles);
|
|
274
|
-
}
|
|
275
|
-
else if (entry.toLowerCase().endsWith(".md")) {
|
|
276
|
-
markdownFiles.push(fullPath);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
return markdownFiles;
|
|
280
|
-
}
|
|
281
|
-
catch (error) {
|
|
282
|
-
const errMsg = `Failed to list directory during cache build scan: ${normalizedPath}`;
|
|
283
|
-
const err = error; // Type assertion
|
|
284
|
-
if (err instanceof McpError && err.code === BaseErrorCode.NOT_FOUND) {
|
|
285
|
-
logger.warning(`${errMsg} - Directory not found, skipping.`, opContext);
|
|
286
|
-
return [];
|
|
287
|
-
}
|
|
288
|
-
// Log and re-throw critical listing errors
|
|
289
|
-
if (err instanceof Error) {
|
|
290
|
-
logger.error(errMsg, err, opContext);
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
logger.error(errMsg, opContext);
|
|
294
|
-
}
|
|
295
|
-
const errorCode = err instanceof McpError ? err.code : BaseErrorCode.INTERNAL_ERROR;
|
|
296
|
-
throw new McpError(errorCode, `${errMsg}: ${err instanceof Error ? err.message : String(err)}`, opContext);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
/**
|
|
3
|
-
* Defines a set of standardized error codes for common issues within MCP servers or tools.
|
|
4
|
-
* These codes help clients understand the nature of an error programmatically.
|
|
5
|
-
*/
|
|
6
|
-
export declare enum BaseErrorCode {
|
|
7
|
-
/** Access denied due to invalid credentials or lack of authentication. */
|
|
8
|
-
UNAUTHORIZED = "UNAUTHORIZED",
|
|
9
|
-
/** Access denied despite valid authentication, due to insufficient permissions. */
|
|
10
|
-
FORBIDDEN = "FORBIDDEN",
|
|
11
|
-
/** The requested resource or entity could not be found. */
|
|
12
|
-
NOT_FOUND = "NOT_FOUND",
|
|
13
|
-
/** The request could not be completed due to a conflict with the current state of the resource. */
|
|
14
|
-
CONFLICT = "CONFLICT",
|
|
15
|
-
/** The request failed due to invalid input parameters or data. */
|
|
16
|
-
VALIDATION_ERROR = "VALIDATION_ERROR",
|
|
17
|
-
/** An error occurred while parsing input data (e.g., date string, JSON). */
|
|
18
|
-
PARSING_ERROR = "PARSING_ERROR",
|
|
19
|
-
/** The request was rejected because the client has exceeded rate limits. */
|
|
20
|
-
RATE_LIMITED = "RATE_LIMITED",
|
|
21
|
-
/** The request timed out before a response could be generated. */
|
|
22
|
-
TIMEOUT = "TIMEOUT",
|
|
23
|
-
/** The service is temporarily unavailable, possibly due to maintenance or overload. */
|
|
24
|
-
SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE",
|
|
25
|
-
/** An unexpected error occurred on the server side. */
|
|
26
|
-
INTERNAL_ERROR = "INTERNAL_ERROR",
|
|
27
|
-
/** An error occurred, but the specific cause is unknown or cannot be categorized. */
|
|
28
|
-
UNKNOWN_ERROR = "UNKNOWN_ERROR",
|
|
29
|
-
/** An error occurred during the loading or validation of configuration data. */
|
|
30
|
-
CONFIGURATION_ERROR = "CONFIGURATION_ERROR"
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Custom error class for MCP-specific errors.
|
|
34
|
-
* Encapsulates a `BaseErrorCode`, a descriptive message, and optional details.
|
|
35
|
-
* Provides a method to format the error into a standard MCP tool response.
|
|
36
|
-
*/
|
|
37
|
-
export declare class McpError extends Error {
|
|
38
|
-
code: BaseErrorCode;
|
|
39
|
-
details?: Record<string, unknown> | undefined;
|
|
40
|
-
/**
|
|
41
|
-
* Creates an instance of McpError.
|
|
42
|
-
* @param {BaseErrorCode} code - The standardized error code.
|
|
43
|
-
* @param {string} message - A human-readable description of the error.
|
|
44
|
-
* @param {Record<string, unknown>} [details] - Optional additional details about the error.
|
|
45
|
-
*/
|
|
46
|
-
constructor(code: BaseErrorCode, message: string, details?: Record<string, unknown> | undefined);
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Zod schema for validating error objects, potentially used for parsing
|
|
50
|
-
* error responses or validating error structures internally.
|
|
51
|
-
*/
|
|
52
|
-
export declare const ErrorSchema: z.ZodObject<{
|
|
53
|
-
/** The error code, corresponding to BaseErrorCode enum values. */
|
|
54
|
-
code: z.ZodNativeEnum<typeof BaseErrorCode>;
|
|
55
|
-
/** A human-readable description of the error. */
|
|
56
|
-
message: z.ZodString;
|
|
57
|
-
/** Optional additional details or context about the error. */
|
|
58
|
-
details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
59
|
-
}, "strip", z.ZodTypeAny, {
|
|
60
|
-
code: BaseErrorCode;
|
|
61
|
-
message: string;
|
|
62
|
-
details?: Record<string, unknown> | undefined;
|
|
63
|
-
}, {
|
|
64
|
-
code: BaseErrorCode;
|
|
65
|
-
message: string;
|
|
66
|
-
details?: Record<string, unknown> | undefined;
|
|
67
|
-
}>;
|
|
68
|
-
/**
|
|
69
|
-
* TypeScript type inferred from `ErrorSchema`.
|
|
70
|
-
* Represents a validated error object structure.
|
|
71
|
-
* @typedef {z.infer<typeof ErrorSchema>} ErrorResponse
|
|
72
|
-
*/
|
|
73
|
-
export type ErrorResponse = z.infer<typeof ErrorSchema>;
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
/**
|
|
3
|
-
* Defines a set of standardized error codes for common issues within MCP servers or tools.
|
|
4
|
-
* These codes help clients understand the nature of an error programmatically.
|
|
5
|
-
*/
|
|
6
|
-
export var BaseErrorCode;
|
|
7
|
-
(function (BaseErrorCode) {
|
|
8
|
-
/** Access denied due to invalid credentials or lack of authentication. */
|
|
9
|
-
BaseErrorCode["UNAUTHORIZED"] = "UNAUTHORIZED";
|
|
10
|
-
/** Access denied despite valid authentication, due to insufficient permissions. */
|
|
11
|
-
BaseErrorCode["FORBIDDEN"] = "FORBIDDEN";
|
|
12
|
-
/** The requested resource or entity could not be found. */
|
|
13
|
-
BaseErrorCode["NOT_FOUND"] = "NOT_FOUND";
|
|
14
|
-
/** The request could not be completed due to a conflict with the current state of the resource. */
|
|
15
|
-
BaseErrorCode["CONFLICT"] = "CONFLICT";
|
|
16
|
-
/** The request failed due to invalid input parameters or data. */
|
|
17
|
-
BaseErrorCode["VALIDATION_ERROR"] = "VALIDATION_ERROR";
|
|
18
|
-
/** An error occurred while parsing input data (e.g., date string, JSON). */
|
|
19
|
-
BaseErrorCode["PARSING_ERROR"] = "PARSING_ERROR";
|
|
20
|
-
/** The request was rejected because the client has exceeded rate limits. */
|
|
21
|
-
BaseErrorCode["RATE_LIMITED"] = "RATE_LIMITED";
|
|
22
|
-
/** The request timed out before a response could be generated. */
|
|
23
|
-
BaseErrorCode["TIMEOUT"] = "TIMEOUT";
|
|
24
|
-
/** The service is temporarily unavailable, possibly due to maintenance or overload. */
|
|
25
|
-
BaseErrorCode["SERVICE_UNAVAILABLE"] = "SERVICE_UNAVAILABLE";
|
|
26
|
-
/** An unexpected error occurred on the server side. */
|
|
27
|
-
BaseErrorCode["INTERNAL_ERROR"] = "INTERNAL_ERROR";
|
|
28
|
-
/** An error occurred, but the specific cause is unknown or cannot be categorized. */
|
|
29
|
-
BaseErrorCode["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
30
|
-
/** An error occurred during the loading or validation of configuration data. */
|
|
31
|
-
BaseErrorCode["CONFIGURATION_ERROR"] = "CONFIGURATION_ERROR";
|
|
32
|
-
})(BaseErrorCode || (BaseErrorCode = {}));
|
|
33
|
-
/**
|
|
34
|
-
* Custom error class for MCP-specific errors.
|
|
35
|
-
* Encapsulates a `BaseErrorCode`, a descriptive message, and optional details.
|
|
36
|
-
* Provides a method to format the error into a standard MCP tool response.
|
|
37
|
-
*/
|
|
38
|
-
export class McpError extends Error {
|
|
39
|
-
/**
|
|
40
|
-
* Creates an instance of McpError.
|
|
41
|
-
* @param {BaseErrorCode} code - The standardized error code.
|
|
42
|
-
* @param {string} message - A human-readable description of the error.
|
|
43
|
-
* @param {Record<string, unknown>} [details] - Optional additional details about the error.
|
|
44
|
-
*/
|
|
45
|
-
constructor(code, message, details) {
|
|
46
|
-
super(message);
|
|
47
|
-
this.code = code;
|
|
48
|
-
this.details = details;
|
|
49
|
-
// Set the error name for identification
|
|
50
|
-
this.name = "McpError";
|
|
51
|
-
// Ensure the prototype chain is correct
|
|
52
|
-
Object.setPrototypeOf(this, McpError.prototype);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Zod schema for validating error objects, potentially used for parsing
|
|
57
|
-
* error responses or validating error structures internally.
|
|
58
|
-
*/
|
|
59
|
-
export const ErrorSchema = z
|
|
60
|
-
.object({
|
|
61
|
-
/** The error code, corresponding to BaseErrorCode enum values. */
|
|
62
|
-
code: z.nativeEnum(BaseErrorCode).describe("Standardized error code"),
|
|
63
|
-
/** A human-readable description of the error. */
|
|
64
|
-
message: z.string().describe("Detailed error message"),
|
|
65
|
-
/** Optional additional details or context about the error. */
|
|
66
|
-
details: z
|
|
67
|
-
.record(z.unknown())
|
|
68
|
-
.optional()
|
|
69
|
-
.describe("Optional structured error details"),
|
|
70
|
-
})
|
|
71
|
-
.describe("Schema for validating structured error objects.");
|
package/dist/utils/index.d.ts
DELETED
package/dist/utils/index.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// Re-export all utilities from their categorized subdirectories
|
|
2
|
-
export * from "./internal/index.js";
|
|
3
|
-
export * from "./parsing/index.js";
|
|
4
|
-
export * from "./security/index.js";
|
|
5
|
-
export * from "./metrics/index.js";
|
|
6
|
-
export * from "./obsidian/index.js"; // Added export for obsidian utils
|
|
7
|
-
// It's good practice to have index.ts files in each subdirectory
|
|
8
|
-
// that export the contents of that directory.
|
|
9
|
-
// Assuming those will be created or already exist.
|
|
10
|
-
// If not, this might need adjustment to export specific files, e.g.:
|
|
11
|
-
// export * from './internal/errorHandler.js';
|
|
12
|
-
// export * from './internal/logger.js';
|
|
13
|
-
// ... etc.
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { RequestContext } from "./requestContext.js";
|
|
2
|
-
/**
|
|
3
|
-
* Configuration for the {@link retryWithDelay} function, defining how retries are handled.
|
|
4
|
-
*/
|
|
5
|
-
export interface RetryConfig<T> {
|
|
6
|
-
/**
|
|
7
|
-
* A descriptive name for the operation being retried. Used in logging.
|
|
8
|
-
* Example: "FetchUserData", "ProcessPayment".
|
|
9
|
-
*/
|
|
10
|
-
operationName: string;
|
|
11
|
-
/**
|
|
12
|
-
* The request context associated with the operation, for logging and tracing.
|
|
13
|
-
*/
|
|
14
|
-
context: RequestContext;
|
|
15
|
-
/**
|
|
16
|
-
* The maximum number of retry attempts before failing.
|
|
17
|
-
*/
|
|
18
|
-
maxRetries: number;
|
|
19
|
-
/**
|
|
20
|
-
* The delay in milliseconds between retry attempts.
|
|
21
|
-
*/
|
|
22
|
-
delayMs: number;
|
|
23
|
-
/**
|
|
24
|
-
* An optional function to determine if a retry should be attempted based on the error.
|
|
25
|
-
* If not provided, retries will be attempted for any error.
|
|
26
|
-
* @param error - The error that occurred during the operation.
|
|
27
|
-
* @returns `true` if a retry should be attempted, `false` otherwise.
|
|
28
|
-
*/
|
|
29
|
-
shouldRetry?: (error: unknown) => boolean;
|
|
30
|
-
/**
|
|
31
|
-
* An optional function to execute before each retry attempt.
|
|
32
|
-
* Useful for custom logging or cleanup actions.
|
|
33
|
-
* @param attempt - The current retry attempt number.
|
|
34
|
-
* @param error - The error that triggered the retry.
|
|
35
|
-
*/
|
|
36
|
-
onRetry?: (attempt: number, error: unknown) => void;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Executes an asynchronous operation with a configurable retry mechanism.
|
|
40
|
-
* This function will attempt the operation up to `maxRetries` times, with a specified
|
|
41
|
-
* `delayMs` between attempts. It allows for custom logic to decide if an error
|
|
42
|
-
* warrants a retry and for actions to be taken before each retry.
|
|
43
|
-
*
|
|
44
|
-
* @template T The expected return type of the asynchronous operation.
|
|
45
|
-
* @param {() => Promise<T>} operation - The asynchronous function to execute.
|
|
46
|
-
* This function should return a Promise resolving to type `T`.
|
|
47
|
-
* @param {RetryConfig<T>} config - Configuration options for the retry behavior,
|
|
48
|
-
* including operation name, context, retry limits, delay, and custom handlers.
|
|
49
|
-
* @returns {Promise<T>} A promise that resolves with the result of the operation if successful.
|
|
50
|
-
* @throws {McpError} Throws an `McpError` if the operation fails after all retry attempts,
|
|
51
|
-
* or if an unexpected error occurs during the retry logic. The error will contain details
|
|
52
|
-
* about the operation name, context, and the last encountered error.
|
|
53
|
-
*/
|
|
54
|
-
export declare function retryWithDelay<T>(operation: () => Promise<T>, config: RetryConfig<T>): Promise<T>;
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Provides utilities for handling asynchronous operations,
|
|
3
|
-
* such as retrying operations with delays.
|
|
4
|
-
* @module src/utils/internal/asyncUtils
|
|
5
|
-
*/
|
|
6
|
-
import { McpError, BaseErrorCode } from "../../types-global/errors.js";
|
|
7
|
-
import { logger } from "./logger.js";
|
|
8
|
-
/**
|
|
9
|
-
* Executes an asynchronous operation with a configurable retry mechanism.
|
|
10
|
-
* This function will attempt the operation up to `maxRetries` times, with a specified
|
|
11
|
-
* `delayMs` between attempts. It allows for custom logic to decide if an error
|
|
12
|
-
* warrants a retry and for actions to be taken before each retry.
|
|
13
|
-
*
|
|
14
|
-
* @template T The expected return type of the asynchronous operation.
|
|
15
|
-
* @param {() => Promise<T>} operation - The asynchronous function to execute.
|
|
16
|
-
* This function should return a Promise resolving to type `T`.
|
|
17
|
-
* @param {RetryConfig<T>} config - Configuration options for the retry behavior,
|
|
18
|
-
* including operation name, context, retry limits, delay, and custom handlers.
|
|
19
|
-
* @returns {Promise<T>} A promise that resolves with the result of the operation if successful.
|
|
20
|
-
* @throws {McpError} Throws an `McpError` if the operation fails after all retry attempts,
|
|
21
|
-
* or if an unexpected error occurs during the retry logic. The error will contain details
|
|
22
|
-
* about the operation name, context, and the last encountered error.
|
|
23
|
-
*/
|
|
24
|
-
export async function retryWithDelay(operation, config) {
|
|
25
|
-
const { operationName, context, maxRetries, delayMs, shouldRetry = () => true, // Default: retry on any error
|
|
26
|
-
onRetry, } = config;
|
|
27
|
-
let lastError;
|
|
28
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
29
|
-
try {
|
|
30
|
-
return await operation();
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
lastError = error;
|
|
34
|
-
// Ensure the context for logging includes attempt details
|
|
35
|
-
const retryAttemptContext = {
|
|
36
|
-
...context, // Spread existing context
|
|
37
|
-
operation: operationName, // Ensure operationName is part of the context for logger
|
|
38
|
-
attempt,
|
|
39
|
-
maxRetries,
|
|
40
|
-
lastError: error instanceof Error ? error.message : String(error),
|
|
41
|
-
};
|
|
42
|
-
if (attempt < maxRetries && shouldRetry(error)) {
|
|
43
|
-
if (onRetry) {
|
|
44
|
-
onRetry(attempt, error); // Custom onRetry logic
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
// Default logging for retry attempt
|
|
48
|
-
logger.warning(`Operation '${operationName}' failed on attempt ${attempt} of ${maxRetries}. Retrying in ${delayMs}ms...`, retryAttemptContext);
|
|
49
|
-
}
|
|
50
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
// Max retries reached or shouldRetry returned false
|
|
54
|
-
const finalErrorMsg = `Operation '${operationName}' failed definitively after ${attempt} attempt(s).`;
|
|
55
|
-
// Log the final failure with the enriched context
|
|
56
|
-
logger.error(finalErrorMsg, error instanceof Error ? error : undefined, retryAttemptContext);
|
|
57
|
-
if (error instanceof McpError) {
|
|
58
|
-
// If the last error was already an McpError, re-throw it but ensure its details are preserved/updated.
|
|
59
|
-
error.details = {
|
|
60
|
-
...(typeof error.details === "object" && error.details !== null
|
|
61
|
-
? error.details
|
|
62
|
-
: {}),
|
|
63
|
-
...retryAttemptContext, // Add retry context to existing details
|
|
64
|
-
finalAttempt: true,
|
|
65
|
-
};
|
|
66
|
-
throw error;
|
|
67
|
-
}
|
|
68
|
-
// For other errors, wrap in a new McpError
|
|
69
|
-
throw new McpError(BaseErrorCode.SERVICE_UNAVAILABLE, // Default to SERVICE_UNAVAILABLE, consider making this configurable or smarter
|
|
70
|
-
`${finalErrorMsg} Last error: ${error instanceof Error ? error.message : String(error)}`, {
|
|
71
|
-
...retryAttemptContext, // Include all retry context
|
|
72
|
-
originalErrorName: error instanceof Error ? error.name : typeof error,
|
|
73
|
-
originalErrorStack: error instanceof Error ? error.stack : undefined,
|
|
74
|
-
finalAttempt: true,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
// Fallback: This part should ideally not be reached if the loop logic is correct.
|
|
80
|
-
// If it is, it implies an issue with the loop or maxRetries logic.
|
|
81
|
-
const fallbackErrorContext = {
|
|
82
|
-
...context,
|
|
83
|
-
operation: operationName,
|
|
84
|
-
maxRetries,
|
|
85
|
-
reason: "Fallback_Error_Path_Reached_In_Retry_Logic",
|
|
86
|
-
};
|
|
87
|
-
logger.crit(
|
|
88
|
-
// Log as critical because this path indicates a logic flaw
|
|
89
|
-
`Operation '${operationName}' failed unexpectedly after all retries (fallback path). This may indicate a logic error in retryWithDelay.`, lastError instanceof Error ? lastError : undefined, fallbackErrorContext);
|
|
90
|
-
throw new McpError(BaseErrorCode.INTERNAL_ERROR, // Indicates an issue with the retry utility itself
|
|
91
|
-
`Operation '${operationName}' failed unexpectedly after all retries (fallback path). Last error: ${lastError instanceof Error ? lastError.message : String(lastError)}`, {
|
|
92
|
-
...fallbackErrorContext,
|
|
93
|
-
originalError: lastError instanceof Error
|
|
94
|
-
? {
|
|
95
|
-
message: lastError.message,
|
|
96
|
-
name: lastError.name,
|
|
97
|
-
stack: lastError.stack,
|
|
98
|
-
}
|
|
99
|
-
: String(lastError),
|
|
100
|
-
});
|
|
101
|
-
}
|