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.
Files changed (254) hide show
  1. package/CLAUDE.md +364 -0
  2. package/Dockerfile +99 -0
  3. package/LICENSE +4 -6
  4. package/README.md +246 -206
  5. package/changelog/3.0.x/3.0.0.md +102 -0
  6. package/changelog/3.1.x/3.1.0.md +26 -0
  7. package/changelog/template.md +51 -0
  8. package/dist/config/server-config.d.ts +19 -0
  9. package/dist/config/server-config.d.ts.map +1 -0
  10. package/dist/config/server-config.js +55 -0
  11. package/dist/config/server-config.js.map +1 -0
  12. package/dist/index.d.ts +7 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +23 -295
  15. package/dist/index.js.map +1 -0
  16. package/dist/mcp-server/prompts/definitions/index.d.ts +8 -0
  17. package/dist/mcp-server/prompts/definitions/index.d.ts.map +1 -0
  18. package/dist/mcp-server/prompts/definitions/index.js +8 -0
  19. package/dist/mcp-server/prompts/definitions/index.js.map +1 -0
  20. package/dist/mcp-server/resources/definitions/index.d.ts +36 -0
  21. package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -0
  22. package/dist/mcp-server/resources/definitions/index.js +9 -0
  23. package/dist/mcp-server/resources/definitions/index.js.map +1 -0
  24. package/dist/mcp-server/resources/definitions/obsidian-status.resource.d.ts +23 -0
  25. package/dist/mcp-server/resources/definitions/obsidian-status.resource.d.ts.map +1 -0
  26. package/dist/mcp-server/resources/definitions/obsidian-status.resource.js +47 -0
  27. package/dist/mcp-server/resources/definitions/obsidian-status.resource.js.map +1 -0
  28. package/dist/mcp-server/resources/definitions/obsidian-tags.resource.d.ts +13 -0
  29. package/dist/mcp-server/resources/definitions/obsidian-tags.resource.d.ts.map +1 -0
  30. package/dist/mcp-server/resources/definitions/obsidian-tags.resource.js +30 -0
  31. package/dist/mcp-server/resources/definitions/obsidian-tags.resource.js.map +1 -0
  32. package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.d.ts +21 -0
  33. package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.d.ts.map +1 -0
  34. package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.js +38 -0
  35. package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.js.map +1 -0
  36. package/dist/mcp-server/tools/definitions/_shared/schemas.d.ts +45 -0
  37. package/dist/mcp-server/tools/definitions/_shared/schemas.d.ts.map +1 -0
  38. package/dist/mcp-server/tools/definitions/_shared/schemas.js +66 -0
  39. package/dist/mcp-server/tools/definitions/_shared/schemas.js.map +1 -0
  40. package/dist/mcp-server/tools/definitions/_shared/suggest-paths.d.ts +51 -0
  41. package/dist/mcp-server/tools/definitions/_shared/suggest-paths.d.ts.map +1 -0
  42. package/dist/mcp-server/tools/definitions/_shared/suggest-paths.js +120 -0
  43. package/dist/mcp-server/tools/definitions/_shared/suggest-paths.js.map +1 -0
  44. package/dist/mcp-server/tools/definitions/index.d.ts +582 -0
  45. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -0
  46. package/dist/mcp-server/tools/definitions/index.js +40 -0
  47. package/dist/mcp-server/tools/definitions/index.js.map +1 -0
  48. package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts +42 -0
  49. package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts.map +1 -0
  50. package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js +58 -0
  51. package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js.map +1 -0
  52. package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts +46 -0
  53. package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts.map +1 -0
  54. package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js +66 -0
  55. package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js.map +1 -0
  56. package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.d.ts +19 -0
  57. package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.d.ts.map +1 -0
  58. package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.js +43 -0
  59. package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.js.map +1 -0
  60. package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts +92 -0
  61. package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts.map +1 -0
  62. package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js +245 -0
  63. package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js.map +1 -0
  64. package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.d.ts +13 -0
  65. package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.d.ts.map +1 -0
  66. package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.js +38 -0
  67. package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.js.map +1 -0
  68. package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts +59 -0
  69. package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts.map +1 -0
  70. package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js +273 -0
  71. package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js.map +1 -0
  72. package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.d.ts +13 -0
  73. package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.d.ts.map +1 -0
  74. package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js +38 -0
  75. package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js.map +1 -0
  76. package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts +68 -0
  77. package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts.map +1 -0
  78. package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js +178 -0
  79. package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js.map +1 -0
  80. package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts +77 -0
  81. package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts.map +1 -0
  82. package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js +172 -0
  83. package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js.map +1 -0
  84. package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.d.ts +22 -0
  85. package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.d.ts.map +1 -0
  86. package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.js +88 -0
  87. package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.js.map +1 -0
  88. package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts +81 -0
  89. package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts.map +1 -0
  90. package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js +83 -0
  91. package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js.map +1 -0
  92. package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.d.ts +59 -0
  93. package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.d.ts.map +1 -0
  94. package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js +167 -0
  95. package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js.map +1 -0
  96. package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts +76 -0
  97. package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts.map +1 -0
  98. package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js +244 -0
  99. package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js.map +1 -0
  100. package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts +48 -0
  101. package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts.map +1 -0
  102. package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js +100 -0
  103. package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js.map +1 -0
  104. package/dist/services/obsidian/frontmatter-ops.d.ts +34 -0
  105. package/dist/services/obsidian/frontmatter-ops.d.ts.map +1 -0
  106. package/dist/services/obsidian/frontmatter-ops.js +230 -0
  107. package/dist/services/obsidian/frontmatter-ops.js.map +1 -0
  108. package/dist/services/obsidian/obsidian-service.d.ts +83 -0
  109. package/dist/services/obsidian/obsidian-service.d.ts.map +1 -0
  110. package/dist/services/obsidian/obsidian-service.js +435 -0
  111. package/dist/services/obsidian/obsidian-service.js.map +1 -0
  112. package/dist/services/obsidian/section-extractor.d.ts +13 -0
  113. package/dist/services/obsidian/section-extractor.d.ts.map +1 -0
  114. package/dist/services/obsidian/section-extractor.js +124 -0
  115. package/dist/services/obsidian/section-extractor.js.map +1 -0
  116. package/dist/services/obsidian/types.d.ts +91 -0
  117. package/dist/services/obsidian/types.d.ts.map +1 -0
  118. package/dist/services/obsidian/types.js +7 -0
  119. package/dist/services/obsidian/types.js.map +1 -0
  120. package/package.json +63 -69
  121. package/server.json +167 -0
  122. package/CHANGELOG.md +0 -124
  123. package/dist/config/index.d.ts +0 -41
  124. package/dist/config/index.js +0 -191
  125. package/dist/mcp-server/server.d.ts +0 -33
  126. package/dist/mcp-server/server.js +0 -211
  127. package/dist/mcp-server/tools/obsidianDeleteNoteTool/index.d.ts +0 -12
  128. package/dist/mcp-server/tools/obsidianDeleteNoteTool/index.js +0 -12
  129. package/dist/mcp-server/tools/obsidianDeleteNoteTool/logic.d.ts +0 -51
  130. package/dist/mcp-server/tools/obsidianDeleteNoteTool/logic.js +0 -168
  131. package/dist/mcp-server/tools/obsidianDeleteNoteTool/registration.d.ts +0 -19
  132. package/dist/mcp-server/tools/obsidianDeleteNoteTool/registration.js +0 -91
  133. package/dist/mcp-server/tools/obsidianGlobalSearchTool/index.d.ts +0 -12
  134. package/dist/mcp-server/tools/obsidianGlobalSearchTool/index.js +0 -12
  135. package/dist/mcp-server/tools/obsidianGlobalSearchTool/logic.d.ts +0 -77
  136. package/dist/mcp-server/tools/obsidianGlobalSearchTool/logic.js +0 -341
  137. package/dist/mcp-server/tools/obsidianGlobalSearchTool/registration.d.ts +0 -18
  138. package/dist/mcp-server/tools/obsidianGlobalSearchTool/registration.js +0 -69
  139. package/dist/mcp-server/tools/obsidianListNotesTool/index.d.ts +0 -12
  140. package/dist/mcp-server/tools/obsidianListNotesTool/index.js +0 -12
  141. package/dist/mcp-server/tools/obsidianListNotesTool/logic.d.ts +0 -68
  142. package/dist/mcp-server/tools/obsidianListNotesTool/logic.js +0 -215
  143. package/dist/mcp-server/tools/obsidianListNotesTool/registration.d.ts +0 -23
  144. package/dist/mcp-server/tools/obsidianListNotesTool/registration.js +0 -98
  145. package/dist/mcp-server/tools/obsidianManageFrontmatterTool/index.d.ts +0 -3
  146. package/dist/mcp-server/tools/obsidianManageFrontmatterTool/index.js +0 -2
  147. package/dist/mcp-server/tools/obsidianManageFrontmatterTool/logic.d.ts +0 -42
  148. package/dist/mcp-server/tools/obsidianManageFrontmatterTool/logic.js +0 -152
  149. package/dist/mcp-server/tools/obsidianManageFrontmatterTool/registration.d.ts +0 -3
  150. package/dist/mcp-server/tools/obsidianManageFrontmatterTool/registration.js +0 -52
  151. package/dist/mcp-server/tools/obsidianManageTagsTool/index.d.ts +0 -3
  152. package/dist/mcp-server/tools/obsidianManageTagsTool/index.js +0 -2
  153. package/dist/mcp-server/tools/obsidianManageTagsTool/logic.d.ts +0 -28
  154. package/dist/mcp-server/tools/obsidianManageTagsTool/logic.js +0 -161
  155. package/dist/mcp-server/tools/obsidianManageTagsTool/registration.d.ts +0 -3
  156. package/dist/mcp-server/tools/obsidianManageTagsTool/registration.js +0 -52
  157. package/dist/mcp-server/tools/obsidianReadNoteTool/index.d.ts +0 -12
  158. package/dist/mcp-server/tools/obsidianReadNoteTool/index.js +0 -12
  159. package/dist/mcp-server/tools/obsidianReadNoteTool/logic.d.ts +0 -87
  160. package/dist/mcp-server/tools/obsidianReadNoteTool/logic.js +0 -216
  161. package/dist/mcp-server/tools/obsidianReadNoteTool/registration.d.ts +0 -20
  162. package/dist/mcp-server/tools/obsidianReadNoteTool/registration.js +0 -101
  163. package/dist/mcp-server/tools/obsidianSearchReplaceTool/index.d.ts +0 -12
  164. package/dist/mcp-server/tools/obsidianSearchReplaceTool/index.js +0 -12
  165. package/dist/mcp-server/tools/obsidianSearchReplaceTool/logic.d.ts +0 -255
  166. package/dist/mcp-server/tools/obsidianSearchReplaceTool/logic.js +0 -583
  167. package/dist/mcp-server/tools/obsidianSearchReplaceTool/registration.d.ts +0 -22
  168. package/dist/mcp-server/tools/obsidianSearchReplaceTool/registration.js +0 -111
  169. package/dist/mcp-server/tools/obsidianUpdateNoteTool/index.d.ts +0 -12
  170. package/dist/mcp-server/tools/obsidianUpdateNoteTool/index.js +0 -12
  171. package/dist/mcp-server/tools/obsidianUpdateNoteTool/logic.d.ts +0 -183
  172. package/dist/mcp-server/tools/obsidianUpdateNoteTool/logic.js +0 -490
  173. package/dist/mcp-server/tools/obsidianUpdateNoteTool/registration.d.ts +0 -21
  174. package/dist/mcp-server/tools/obsidianUpdateNoteTool/registration.js +0 -108
  175. package/dist/mcp-server/transports/auth/core/authContext.d.ts +0 -33
  176. package/dist/mcp-server/transports/auth/core/authContext.js +0 -24
  177. package/dist/mcp-server/transports/auth/core/authTypes.d.ts +0 -17
  178. package/dist/mcp-server/transports/auth/core/authTypes.js +0 -5
  179. package/dist/mcp-server/transports/auth/core/authUtils.d.ts +0 -18
  180. package/dist/mcp-server/transports/auth/core/authUtils.js +0 -45
  181. package/dist/mcp-server/transports/auth/index.d.ts +0 -10
  182. package/dist/mcp-server/transports/auth/index.js +0 -9
  183. package/dist/mcp-server/transports/auth/strategies/jwt/jwtMiddleware.d.ts +0 -27
  184. package/dist/mcp-server/transports/auth/strategies/jwt/jwtMiddleware.js +0 -149
  185. package/dist/mcp-server/transports/auth/strategies/oauth/oauthMiddleware.d.ts +0 -20
  186. package/dist/mcp-server/transports/auth/strategies/oauth/oauthMiddleware.js +0 -124
  187. package/dist/mcp-server/transports/httpErrorHandler.d.ts +0 -26
  188. package/dist/mcp-server/transports/httpErrorHandler.js +0 -73
  189. package/dist/mcp-server/transports/httpTransport.d.ts +0 -21
  190. package/dist/mcp-server/transports/httpTransport.js +0 -208
  191. package/dist/mcp-server/transports/stdioTransport.d.ts +0 -42
  192. package/dist/mcp-server/transports/stdioTransport.js +0 -63
  193. package/dist/services/obsidianRestAPI/index.d.ts +0 -15
  194. package/dist/services/obsidianRestAPI/index.js +0 -17
  195. package/dist/services/obsidianRestAPI/methods/activeFileMethods.d.ts +0 -38
  196. package/dist/services/obsidianRestAPI/methods/activeFileMethods.js +0 -62
  197. package/dist/services/obsidianRestAPI/methods/commandMethods.d.ts +0 -22
  198. package/dist/services/obsidianRestAPI/methods/commandMethods.js +0 -31
  199. package/dist/services/obsidianRestAPI/methods/openMethods.d.ts +0 -16
  200. package/dist/services/obsidianRestAPI/methods/openMethods.js +0 -21
  201. package/dist/services/obsidianRestAPI/methods/patchMethods.d.ts +0 -37
  202. package/dist/services/obsidianRestAPI/methods/patchMethods.js +0 -94
  203. package/dist/services/obsidianRestAPI/methods/periodicNoteMethods.d.ts +0 -42
  204. package/dist/services/obsidianRestAPI/methods/periodicNoteMethods.js +0 -66
  205. package/dist/services/obsidianRestAPI/methods/searchMethods.d.ts +0 -25
  206. package/dist/services/obsidianRestAPI/methods/searchMethods.js +0 -36
  207. package/dist/services/obsidianRestAPI/methods/vaultMethods.d.ts +0 -58
  208. package/dist/services/obsidianRestAPI/methods/vaultMethods.js +0 -144
  209. package/dist/services/obsidianRestAPI/service.d.ts +0 -195
  210. package/dist/services/obsidianRestAPI/service.js +0 -379
  211. package/dist/services/obsidianRestAPI/types.d.ts +0 -127
  212. package/dist/services/obsidianRestAPI/types.js +0 -7
  213. package/dist/services/obsidianRestAPI/vaultCache/index.d.ts +0 -4
  214. package/dist/services/obsidianRestAPI/vaultCache/index.js +0 -4
  215. package/dist/services/obsidianRestAPI/vaultCache/service.d.ts +0 -88
  216. package/dist/services/obsidianRestAPI/vaultCache/service.js +0 -299
  217. package/dist/types-global/errors.d.ts +0 -73
  218. package/dist/types-global/errors.js +0 -71
  219. package/dist/utils/index.d.ts +0 -5
  220. package/dist/utils/index.js +0 -13
  221. package/dist/utils/internal/asyncUtils.d.ts +0 -54
  222. package/dist/utils/internal/asyncUtils.js +0 -101
  223. package/dist/utils/internal/errorHandler.d.ts +0 -176
  224. package/dist/utils/internal/errorHandler.js +0 -351
  225. package/dist/utils/internal/index.d.ts +0 -4
  226. package/dist/utils/internal/index.js +0 -4
  227. package/dist/utils/internal/logger.d.ts +0 -141
  228. package/dist/utils/internal/logger.js +0 -406
  229. package/dist/utils/internal/requestContext.d.ts +0 -83
  230. package/dist/utils/internal/requestContext.js +0 -72
  231. package/dist/utils/metrics/index.d.ts +0 -1
  232. package/dist/utils/metrics/index.js +0 -1
  233. package/dist/utils/metrics/tokenCounter.d.ts +0 -27
  234. package/dist/utils/metrics/tokenCounter.js +0 -128
  235. package/dist/utils/obsidian/index.d.ts +0 -5
  236. package/dist/utils/obsidian/index.js +0 -5
  237. package/dist/utils/obsidian/obsidianApiUtils.d.ts +0 -14
  238. package/dist/utils/obsidian/obsidianApiUtils.js +0 -29
  239. package/dist/utils/obsidian/obsidianStatUtils.d.ts +0 -68
  240. package/dist/utils/obsidian/obsidianStatUtils.js +0 -143
  241. package/dist/utils/parsing/dateParser.d.ts +0 -56
  242. package/dist/utils/parsing/dateParser.js +0 -104
  243. package/dist/utils/parsing/index.d.ts +0 -2
  244. package/dist/utils/parsing/index.js +0 -3
  245. package/dist/utils/parsing/jsonParser.d.ts +0 -80
  246. package/dist/utils/parsing/jsonParser.js +0 -133
  247. package/dist/utils/security/idGenerator.d.ts +0 -140
  248. package/dist/utils/security/idGenerator.js +0 -194
  249. package/dist/utils/security/index.d.ts +0 -3
  250. package/dist/utils/security/index.js +0 -3
  251. package/dist/utils/security/rateLimiter.d.ts +0 -156
  252. package/dist/utils/security/rateLimiter.js +0 -235
  253. package/dist/utils/security/sanitization.d.ts +0 -244
  254. 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.");
@@ -1,5 +0,0 @@
1
- export * from "./internal/index.js";
2
- export * from "./parsing/index.js";
3
- export * from "./security/index.js";
4
- export * from "./metrics/index.js";
5
- export * from "./obsidian/index.js";
@@ -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
- }