obsidian-mcp-server 1.5.8 → 2.0.2

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 (221) hide show
  1. package/README.md +248 -104
  2. package/dist/config/index.d.ts +41 -0
  3. package/dist/config/index.js +191 -0
  4. package/dist/index.d.ts +1 -5
  5. package/dist/index.js +296 -18
  6. package/dist/mcp-server/server.d.ts +33 -0
  7. package/dist/mcp-server/server.js +211 -0
  8. package/dist/mcp-server/tools/obsidianDeleteFileTool/index.d.ts +12 -0
  9. package/dist/mcp-server/tools/obsidianDeleteFileTool/index.js +12 -0
  10. package/dist/mcp-server/tools/obsidianDeleteFileTool/logic.d.ts +51 -0
  11. package/dist/mcp-server/tools/obsidianDeleteFileTool/logic.js +168 -0
  12. package/dist/mcp-server/tools/obsidianDeleteFileTool/registration.d.ts +19 -0
  13. package/dist/mcp-server/tools/obsidianDeleteFileTool/registration.js +91 -0
  14. package/dist/mcp-server/tools/obsidianGlobalSearchTool/index.d.ts +12 -0
  15. package/dist/mcp-server/tools/obsidianGlobalSearchTool/index.js +12 -0
  16. package/dist/mcp-server/tools/obsidianGlobalSearchTool/logic.d.ts +77 -0
  17. package/dist/mcp-server/tools/obsidianGlobalSearchTool/logic.js +341 -0
  18. package/dist/mcp-server/tools/obsidianGlobalSearchTool/registration.d.ts +18 -0
  19. package/dist/mcp-server/tools/obsidianGlobalSearchTool/registration.js +69 -0
  20. package/dist/mcp-server/tools/obsidianListFilesTool/index.d.ts +12 -0
  21. package/dist/mcp-server/tools/obsidianListFilesTool/index.js +12 -0
  22. package/dist/mcp-server/tools/obsidianListFilesTool/logic.d.ts +64 -0
  23. package/dist/mcp-server/tools/obsidianListFilesTool/logic.js +179 -0
  24. package/dist/mcp-server/tools/obsidianListFilesTool/registration.d.ts +19 -0
  25. package/dist/mcp-server/tools/obsidianListFilesTool/registration.js +96 -0
  26. package/dist/mcp-server/tools/obsidianManageFrontmatterTool/index.d.ts +3 -0
  27. package/dist/mcp-server/tools/obsidianManageFrontmatterTool/index.js +2 -0
  28. package/dist/mcp-server/tools/obsidianManageFrontmatterTool/logic.d.ts +42 -0
  29. package/dist/mcp-server/tools/obsidianManageFrontmatterTool/logic.js +152 -0
  30. package/dist/mcp-server/tools/obsidianManageFrontmatterTool/registration.d.ts +3 -0
  31. package/dist/mcp-server/tools/obsidianManageFrontmatterTool/registration.js +52 -0
  32. package/dist/mcp-server/tools/obsidianManageTagsTool/index.d.ts +3 -0
  33. package/dist/mcp-server/tools/obsidianManageTagsTool/index.js +2 -0
  34. package/dist/mcp-server/tools/obsidianManageTagsTool/logic.d.ts +28 -0
  35. package/dist/mcp-server/tools/obsidianManageTagsTool/logic.js +161 -0
  36. package/dist/mcp-server/tools/obsidianManageTagsTool/registration.d.ts +3 -0
  37. package/dist/mcp-server/tools/obsidianManageTagsTool/registration.js +52 -0
  38. package/dist/mcp-server/tools/obsidianReadFileTool/index.d.ts +12 -0
  39. package/dist/mcp-server/tools/obsidianReadFileTool/index.js +12 -0
  40. package/dist/mcp-server/tools/obsidianReadFileTool/logic.d.ts +87 -0
  41. package/dist/mcp-server/tools/obsidianReadFileTool/logic.js +216 -0
  42. package/dist/mcp-server/tools/obsidianReadFileTool/registration.d.ts +20 -0
  43. package/dist/mcp-server/tools/obsidianReadFileTool/registration.js +101 -0
  44. package/dist/mcp-server/tools/obsidianSearchReplaceTool/index.d.ts +12 -0
  45. package/dist/mcp-server/tools/obsidianSearchReplaceTool/index.js +12 -0
  46. package/dist/mcp-server/tools/obsidianSearchReplaceTool/logic.d.ts +255 -0
  47. package/dist/mcp-server/tools/obsidianSearchReplaceTool/logic.js +583 -0
  48. package/dist/mcp-server/tools/obsidianSearchReplaceTool/registration.d.ts +22 -0
  49. package/dist/mcp-server/tools/obsidianSearchReplaceTool/registration.js +111 -0
  50. package/dist/mcp-server/tools/obsidianUpdateFileTool/index.d.ts +12 -0
  51. package/dist/mcp-server/tools/obsidianUpdateFileTool/index.js +12 -0
  52. package/dist/mcp-server/tools/obsidianUpdateFileTool/logic.d.ts +183 -0
  53. package/dist/mcp-server/tools/obsidianUpdateFileTool/logic.js +490 -0
  54. package/dist/mcp-server/tools/obsidianUpdateFileTool/registration.d.ts +21 -0
  55. package/dist/mcp-server/tools/obsidianUpdateFileTool/registration.js +108 -0
  56. package/dist/mcp-server/transports/authentication/authContext.d.ts +33 -0
  57. package/dist/mcp-server/transports/authentication/authContext.js +24 -0
  58. package/dist/mcp-server/transports/authentication/authMiddleware.d.ts +30 -0
  59. package/dist/mcp-server/transports/authentication/authMiddleware.js +145 -0
  60. package/dist/mcp-server/transports/authentication/authUtils.d.ts +18 -0
  61. package/dist/mcp-server/transports/authentication/authUtils.js +45 -0
  62. package/dist/mcp-server/transports/authentication/oauthMiddleware.d.ts +24 -0
  63. package/dist/mcp-server/transports/authentication/oauthMiddleware.js +109 -0
  64. package/dist/mcp-server/transports/authentication/types.d.ts +17 -0
  65. package/dist/mcp-server/transports/authentication/types.js +5 -0
  66. package/dist/mcp-server/transports/httpTransport.d.ts +24 -0
  67. package/dist/mcp-server/transports/httpTransport.js +496 -0
  68. package/dist/mcp-server/transports/stdioTransport.d.ts +42 -0
  69. package/dist/mcp-server/transports/stdioTransport.js +63 -0
  70. package/dist/services/obsidianRestAPI/index.d.ts +15 -0
  71. package/dist/services/obsidianRestAPI/index.js +17 -0
  72. package/dist/services/obsidianRestAPI/methods/activeFileMethods.d.ts +38 -0
  73. package/dist/services/obsidianRestAPI/methods/activeFileMethods.js +62 -0
  74. package/dist/services/obsidianRestAPI/methods/commandMethods.d.ts +22 -0
  75. package/dist/services/obsidianRestAPI/methods/commandMethods.js +31 -0
  76. package/dist/services/obsidianRestAPI/methods/openMethods.d.ts +16 -0
  77. package/dist/services/obsidianRestAPI/methods/openMethods.js +21 -0
  78. package/dist/services/obsidianRestAPI/methods/patchMethods.d.ts +37 -0
  79. package/dist/services/obsidianRestAPI/methods/patchMethods.js +94 -0
  80. package/dist/services/obsidianRestAPI/methods/periodicNoteMethods.d.ts +42 -0
  81. package/dist/services/obsidianRestAPI/methods/periodicNoteMethods.js +66 -0
  82. package/dist/services/obsidianRestAPI/methods/searchMethods.d.ts +25 -0
  83. package/dist/services/obsidianRestAPI/methods/searchMethods.js +36 -0
  84. package/dist/services/obsidianRestAPI/methods/vaultMethods.d.ts +58 -0
  85. package/dist/services/obsidianRestAPI/methods/vaultMethods.js +144 -0
  86. package/dist/services/obsidianRestAPI/service.d.ts +195 -0
  87. package/dist/services/obsidianRestAPI/service.js +379 -0
  88. package/dist/services/obsidianRestAPI/types.d.ts +127 -0
  89. package/dist/services/obsidianRestAPI/types.js +7 -0
  90. package/dist/services/obsidianRestAPI/vaultCache/index.d.ts +4 -0
  91. package/dist/services/obsidianRestAPI/vaultCache/index.js +4 -0
  92. package/dist/services/obsidianRestAPI/vaultCache/service.d.ts +88 -0
  93. package/dist/services/obsidianRestAPI/vaultCache/service.js +299 -0
  94. package/dist/types-global/errors.d.ts +73 -0
  95. package/dist/types-global/errors.js +71 -0
  96. package/dist/utils/index.d.ts +5 -8
  97. package/dist/utils/index.js +13 -9
  98. package/dist/utils/internal/asyncUtils.d.ts +54 -0
  99. package/dist/utils/internal/asyncUtils.js +101 -0
  100. package/dist/utils/internal/errorHandler.d.ts +176 -0
  101. package/dist/utils/internal/errorHandler.js +351 -0
  102. package/dist/utils/internal/index.d.ts +4 -0
  103. package/dist/utils/internal/index.js +4 -0
  104. package/dist/utils/internal/logger.d.ts +141 -0
  105. package/dist/utils/internal/logger.js +406 -0
  106. package/dist/utils/internal/requestContext.d.ts +83 -0
  107. package/dist/utils/internal/requestContext.js +72 -0
  108. package/dist/utils/metrics/index.d.ts +1 -0
  109. package/dist/utils/metrics/index.js +1 -0
  110. package/dist/utils/metrics/tokenCounter.d.ts +27 -0
  111. package/dist/utils/metrics/tokenCounter.js +128 -0
  112. package/dist/utils/obsidian/index.d.ts +5 -0
  113. package/dist/utils/obsidian/index.js +5 -0
  114. package/dist/utils/obsidian/obsidianApiUtils.d.ts +14 -0
  115. package/dist/utils/obsidian/obsidianApiUtils.js +29 -0
  116. package/dist/utils/obsidian/obsidianStatUtils.d.ts +68 -0
  117. package/dist/utils/obsidian/obsidianStatUtils.js +143 -0
  118. package/dist/utils/parsing/dateParser.d.ts +56 -0
  119. package/dist/utils/parsing/dateParser.js +104 -0
  120. package/dist/utils/parsing/index.d.ts +2 -0
  121. package/dist/utils/parsing/index.js +3 -0
  122. package/dist/utils/parsing/jsonParser.d.ts +80 -0
  123. package/dist/utils/parsing/jsonParser.js +133 -0
  124. package/dist/utils/security/idGenerator.d.ts +140 -0
  125. package/dist/utils/security/idGenerator.js +194 -0
  126. package/dist/utils/security/index.d.ts +3 -0
  127. package/dist/utils/security/index.js +3 -0
  128. package/dist/utils/security/rateLimiter.d.ts +156 -0
  129. package/dist/utils/security/rateLimiter.js +235 -0
  130. package/dist/utils/security/sanitization.d.ts +244 -0
  131. package/dist/utils/security/sanitization.js +599 -0
  132. package/package.json +58 -37
  133. package/dist/index.js.map +0 -1
  134. package/dist/mcp/handlers.d.ts +0 -29
  135. package/dist/mcp/handlers.js +0 -305
  136. package/dist/mcp/handlers.js.map +0 -1
  137. package/dist/mcp/index.d.ts +0 -6
  138. package/dist/mcp/index.js +0 -7
  139. package/dist/mcp/index.js.map +0 -1
  140. package/dist/mcp/server.d.ts +0 -18
  141. package/dist/mcp/server.js +0 -240
  142. package/dist/mcp/server.js.map +0 -1
  143. package/dist/mcp/types.d.ts +0 -70
  144. package/dist/mcp/types.js +0 -49
  145. package/dist/mcp/types.js.map +0 -1
  146. package/dist/obsidian/client.d.ts +0 -109
  147. package/dist/obsidian/client.js +0 -403
  148. package/dist/obsidian/client.js.map +0 -1
  149. package/dist/obsidian/errors.d.ts +0 -28
  150. package/dist/obsidian/errors.js +0 -75
  151. package/dist/obsidian/errors.js.map +0 -1
  152. package/dist/obsidian/index.d.ts +0 -6
  153. package/dist/obsidian/index.js +0 -7
  154. package/dist/obsidian/index.js.map +0 -1
  155. package/dist/obsidian/types.d.ts +0 -107
  156. package/dist/obsidian/types.js +0 -12
  157. package/dist/obsidian/types.js.map +0 -1
  158. package/dist/resources/index.d.ts +0 -13
  159. package/dist/resources/index.js +0 -15
  160. package/dist/resources/index.js.map +0 -1
  161. package/dist/resources/tags.d.ts +0 -39
  162. package/dist/resources/tags.js +0 -257
  163. package/dist/resources/tags.js.map +0 -1
  164. package/dist/resources/types.d.ts +0 -27
  165. package/dist/resources/types.js +0 -5
  166. package/dist/resources/types.js.map +0 -1
  167. package/dist/tools/base.d.ts +0 -46
  168. package/dist/tools/base.js +0 -88
  169. package/dist/tools/base.js.map +0 -1
  170. package/dist/tools/files/content.d.ts +0 -58
  171. package/dist/tools/files/content.js +0 -171
  172. package/dist/tools/files/content.js.map +0 -1
  173. package/dist/tools/files/index.d.ts +0 -14
  174. package/dist/tools/files/index.js +0 -22
  175. package/dist/tools/files/index.js.map +0 -1
  176. package/dist/tools/files/list.d.ts +0 -35
  177. package/dist/tools/files/list.js +0 -133
  178. package/dist/tools/files/list.js.map +0 -1
  179. package/dist/tools/index.d.ts +0 -21
  180. package/dist/tools/index.js +0 -31
  181. package/dist/tools/index.js.map +0 -1
  182. package/dist/tools/properties/index.d.ts +0 -14
  183. package/dist/tools/properties/index.js +0 -19
  184. package/dist/tools/properties/index.js.map +0 -1
  185. package/dist/tools/properties/manager.d.ts +0 -62
  186. package/dist/tools/properties/manager.js +0 -302
  187. package/dist/tools/properties/manager.js.map +0 -1
  188. package/dist/tools/properties/tools.d.ts +0 -47
  189. package/dist/tools/properties/tools.js +0 -239
  190. package/dist/tools/properties/tools.js.map +0 -1
  191. package/dist/tools/properties/types.d.ts +0 -141
  192. package/dist/tools/properties/types.js +0 -70
  193. package/dist/tools/properties/types.js.map +0 -1
  194. package/dist/tools/search/complex.d.ts +0 -38
  195. package/dist/tools/search/complex.js +0 -270
  196. package/dist/tools/search/complex.js.map +0 -1
  197. package/dist/tools/search/index.d.ts +0 -14
  198. package/dist/tools/search/index.js +0 -20
  199. package/dist/tools/search/index.js.map +0 -1
  200. package/dist/tools/search/simple.d.ts +0 -25
  201. package/dist/tools/search/simple.js +0 -127
  202. package/dist/tools/search/simple.js.map +0 -1
  203. package/dist/utils/errors.d.ts +0 -24
  204. package/dist/utils/errors.js +0 -59
  205. package/dist/utils/errors.js.map +0 -1
  206. package/dist/utils/idGenerator.d.ts +0 -15
  207. package/dist/utils/idGenerator.js +0 -21
  208. package/dist/utils/idGenerator.js.map +0 -1
  209. package/dist/utils/index.js.map +0 -1
  210. package/dist/utils/logging.d.ts +0 -245
  211. package/dist/utils/logging.js +0 -417
  212. package/dist/utils/logging.js.map +0 -1
  213. package/dist/utils/rate-limiting.d.ts +0 -50
  214. package/dist/utils/rate-limiting.js +0 -94
  215. package/dist/utils/rate-limiting.js.map +0 -1
  216. package/dist/utils/tokenization.d.ts +0 -28
  217. package/dist/utils/tokenization.js +0 -75
  218. package/dist/utils/tokenization.js.map +0 -1
  219. package/dist/utils/validation.d.ts +0 -22
  220. package/dist/utils/validation.js +0 -92
  221. package/dist/utils/validation.js.map +0 -1
@@ -0,0 +1,195 @@
1
+ /**
2
+ * @module ObsidianRestApiService
3
+ * @description
4
+ * This module provides the core implementation for the Obsidian REST API service.
5
+ * It encapsulates the logic for making authenticated requests to the API endpoints.
6
+ */
7
+ import { RequestContext } from "../../utils/index.js";
8
+ import { ApiStatusResponse, // Import PatchOptions type
9
+ ComplexSearchResult, NoteJson, NoteStat, ObsidianCommand, PatchOptions, Period, SimpleSearchResult } from "./types.js";
10
+ export declare class ObsidianRestApiService {
11
+ private axiosInstance;
12
+ private apiKey;
13
+ constructor();
14
+ /**
15
+ * Private helper to make requests and handle common errors.
16
+ * @param config - Axios request configuration.
17
+ * @param context - Request context for logging.
18
+ * @param operationName - Name of the operation for logging context.
19
+ * @returns The response data.
20
+ * @throws {McpError} If the request fails.
21
+ */
22
+ private _request;
23
+ /**
24
+ * Checks the status and authentication of the Obsidian Local REST API.
25
+ * @param context - The request context for logging and correlation.
26
+ * @returns {Promise<ApiStatusResponse>} - The status object from the API.
27
+ */
28
+ checkStatus(context: RequestContext): Promise<ApiStatusResponse>;
29
+ /**
30
+ * Gets the content of a specific file in the vault.
31
+ * @param filePath - Vault-relative path to the file.
32
+ * @param format - 'markdown' or 'json' (for NoteJson).
33
+ * @param context - Request context.
34
+ * @returns The file content (string) or NoteJson object.
35
+ */
36
+ getFileContent(filePath: string, format: "markdown" | "json" | undefined, context: RequestContext): Promise<string | NoteJson>;
37
+ /**
38
+ * Updates (overwrites) the content of a file or creates it if it doesn't exist.
39
+ * @param filePath - Vault-relative path to the file.
40
+ * @param content - The new content for the file.
41
+ * @param context - Request context.
42
+ * @returns {Promise<void>} Resolves on success (204 No Content).
43
+ */
44
+ updateFileContent(filePath: string, content: string, context: RequestContext): Promise<void>;
45
+ /**
46
+ * Appends content to the end of a file. Creates the file if it doesn't exist.
47
+ * @param filePath - Vault-relative path to the file.
48
+ * @param content - The content to append.
49
+ * @param context - Request context.
50
+ * @returns {Promise<void>} Resolves on success (204 No Content).
51
+ */
52
+ appendFileContent(filePath: string, content: string, context: RequestContext): Promise<void>;
53
+ /**
54
+ * Deletes a specific file in the vault.
55
+ * @param filePath - Vault-relative path to the file.
56
+ * @param context - Request context.
57
+ * @returns {Promise<void>} Resolves on success (204 No Content).
58
+ */
59
+ deleteFile(filePath: string, context: RequestContext): Promise<void>;
60
+ /**
61
+ * Lists files within a specified directory in the vault.
62
+ * @param dirPath - Vault-relative path to the directory. Use empty string "" or "/" for the root.
63
+ * @param context - Request context.
64
+ * @returns A list of file and directory names.
65
+ */
66
+ listFiles(dirPath: string, context: RequestContext): Promise<string[]>;
67
+ /**
68
+ * Gets the metadata (stat) of a specific file using a lightweight HEAD request.
69
+ * @param filePath - Vault-relative path to the file.
70
+ * @param context - Request context.
71
+ * @returns The file's metadata.
72
+ */
73
+ getFileMetadata(filePath: string, context: RequestContext): Promise<NoteStat | null>;
74
+ /**
75
+ * Performs a simple text search across the vault.
76
+ * @param query - The text query string.
77
+ * @param contextLength - Number of characters surrounding each match (default 100).
78
+ * @param context - Request context.
79
+ * @returns An array of search results.
80
+ */
81
+ searchSimple(query: string, contextLength: number | undefined, context: RequestContext): Promise<SimpleSearchResult[]>;
82
+ /**
83
+ * Performs a complex search using Dataview DQL or JsonLogic.
84
+ * @param query - The query string (DQL) or JSON object (JsonLogic).
85
+ * @param contentType - The content type header indicating the query format.
86
+ * @param context - Request context.
87
+ * @returns An array of search results.
88
+ */
89
+ searchComplex(query: string | object, contentType: "application/vnd.olrapi.dataview.dql+txt" | "application/vnd.olrapi.jsonlogic+json", context: RequestContext): Promise<ComplexSearchResult[]>;
90
+ /**
91
+ * Executes a registered Obsidian command by its ID.
92
+ * @param commandId - The ID of the command (e.g., "app:go-back").
93
+ * @param context - Request context.
94
+ * @returns {Promise<void>} Resolves on success (204 No Content).
95
+ */
96
+ executeCommand(commandId: string, context: RequestContext): Promise<void>;
97
+ /**
98
+ * Lists all available Obsidian commands.
99
+ * @param context - Request context.
100
+ * @returns A list of available commands.
101
+ */
102
+ listCommands(context: RequestContext): Promise<ObsidianCommand[]>;
103
+ /**
104
+ * Opens a specific file in Obsidian. Creates the file if it doesn't exist.
105
+ * @param filePath - Vault-relative path to the file.
106
+ * @param newLeaf - Whether to open the file in a new editor tab (leaf).
107
+ * @param context - Request context.
108
+ * @returns {Promise<void>} Resolves on success (200 OK, but no body expected).
109
+ */
110
+ openFile(filePath: string, newLeaf: boolean | undefined, context: RequestContext): Promise<void>;
111
+ /**
112
+ * Gets the content of the currently active file in Obsidian.
113
+ * @param format - 'markdown' or 'json' (for NoteJson).
114
+ * @param context - Request context.
115
+ * @returns The file content (string) or NoteJson object.
116
+ */
117
+ getActiveFile(format: "markdown" | "json" | undefined, context: RequestContext): Promise<string | NoteJson>;
118
+ /**
119
+ * Updates (overwrites) the content of the currently active file.
120
+ * @param content - The new content.
121
+ * @param context - Request context.
122
+ * @returns {Promise<void>} Resolves on success (204 No Content).
123
+ */
124
+ updateActiveFile(content: string, context: RequestContext): Promise<void>;
125
+ /**
126
+ * Appends content to the end of the currently active file.
127
+ * @param content - The content to append.
128
+ * @param context - Request context.
129
+ * @returns {Promise<void>} Resolves on success (204 No Content).
130
+ */
131
+ appendActiveFile(content: string, context: RequestContext): Promise<void>;
132
+ /**
133
+ * Deletes the currently active file.
134
+ * @param context - Request context.
135
+ * @returns {Promise<void>} Resolves on success (204 No Content).
136
+ */
137
+ deleteActiveFile(context: RequestContext): Promise<void>;
138
+ /**
139
+ * Gets the content of a periodic note (daily, weekly, etc.).
140
+ * @param period - The period type ('daily', 'weekly', 'monthly', 'quarterly', 'yearly').
141
+ * @param format - 'markdown' or 'json'.
142
+ * @param context - Request context.
143
+ * @returns The note content or NoteJson.
144
+ */
145
+ getPeriodicNote(period: Period, format: "markdown" | "json" | undefined, context: RequestContext): Promise<string | NoteJson>;
146
+ /**
147
+ * Updates (overwrites) the content of a periodic note. Creates if needed.
148
+ * @param period - The period type.
149
+ * @param content - The new content.
150
+ * @param context - Request context.
151
+ * @returns {Promise<void>} Resolves on success (204 No Content).
152
+ */
153
+ updatePeriodicNote(period: Period, content: string, context: RequestContext): Promise<void>;
154
+ /**
155
+ * Appends content to a periodic note. Creates if needed.
156
+ * @param period - The period type.
157
+ * @param content - The content to append.
158
+ * @param context - Request context.
159
+ * @returns {Promise<void>} Resolves on success (204 No Content).
160
+ */
161
+ appendPeriodicNote(period: Period, content: string, context: RequestContext): Promise<void>;
162
+ /**
163
+ * Deletes a periodic note.
164
+ * @param period - The period type.
165
+ * @param context - Request context.
166
+ * @returns {Promise<void>} Resolves on success (204 No Content).
167
+ */
168
+ deletePeriodicNote(period: Period, context: RequestContext): Promise<void>;
169
+ /**
170
+ * Patches a specific file in the vault using granular controls.
171
+ * @param filePath - Vault-relative path to the file.
172
+ * @param content - The content to insert/replace (string or JSON for tables/frontmatter).
173
+ * @param options - Patch operation details (operation, targetType, target, etc.).
174
+ * @param context - Request context.
175
+ * @returns {Promise<void>} Resolves on success (200 OK).
176
+ */
177
+ patchFile(filePath: string, content: string | object, options: PatchOptions, context: RequestContext): Promise<void>;
178
+ /**
179
+ * Patches the currently active file in Obsidian using granular controls.
180
+ * @param content - The content to insert/replace.
181
+ * @param options - Patch operation details.
182
+ * @param context - Request context.
183
+ * @returns {Promise<void>} Resolves on success (200 OK).
184
+ */
185
+ patchActiveFile(content: string | object, options: PatchOptions, context: RequestContext): Promise<void>;
186
+ /**
187
+ * Patches a periodic note using granular controls.
188
+ * @param period - The period type ('daily', 'weekly', etc.).
189
+ * @param content - The content to insert/replace.
190
+ * @param options - Patch operation details.
191
+ * @param context - Request context.
192
+ * @returns {Promise<void>} Resolves on success (200 OK).
193
+ */
194
+ patchPeriodicNote(period: Period, content: string | object, options: PatchOptions, context: RequestContext): Promise<void>;
195
+ }
@@ -0,0 +1,379 @@
1
+ /**
2
+ * @module ObsidianRestApiService
3
+ * @description
4
+ * This module provides the core implementation for the Obsidian REST API service.
5
+ * It encapsulates the logic for making authenticated requests to the API endpoints.
6
+ */
7
+ import axios from "axios";
8
+ import https from "node:https"; // Import the https module for Agent configuration
9
+ import { config } from "../../config/index.js";
10
+ import { BaseErrorCode, McpError } from "../../types-global/errors.js";
11
+ import { ErrorHandler, logger, requestContextService, } from "../../utils/index.js"; // Added requestContextService
12
+ import * as activeFileMethods from "./methods/activeFileMethods.js";
13
+ import * as commandMethods from "./methods/commandMethods.js";
14
+ import * as openMethods from "./methods/openMethods.js";
15
+ import * as patchMethods from "./methods/patchMethods.js";
16
+ import * as periodicNoteMethods from "./methods/periodicNoteMethods.js";
17
+ import * as searchMethods from "./methods/searchMethods.js";
18
+ import * as vaultMethods from "./methods/vaultMethods.js";
19
+ export class ObsidianRestApiService {
20
+ constructor() {
21
+ this.apiKey = config.obsidianApiKey; // Get from central config
22
+ if (!this.apiKey) {
23
+ // Config validation should prevent this, but double-check
24
+ throw new McpError(BaseErrorCode.CONFIGURATION_ERROR, "Obsidian API Key is missing in configuration.", {});
25
+ }
26
+ const httpsAgent = new https.Agent({
27
+ rejectUnauthorized: config.obsidianVerifySsl,
28
+ });
29
+ this.axiosInstance = axios.create({
30
+ baseURL: config.obsidianBaseUrl.replace(/\/$/, ""), // Remove trailing slash
31
+ headers: {
32
+ Authorization: `Bearer ${this.apiKey}`,
33
+ Accept: "application/json", // Default accept type
34
+ },
35
+ timeout: 60000, // Increased timeout to 60 seconds (was 15000)
36
+ httpsAgent,
37
+ });
38
+ logger.info(`ObsidianRestApiService initialized with base URL: ${this.axiosInstance.defaults.baseURL}, Verify SSL: ${config.obsidianVerifySsl}`, requestContextService.createRequestContext({
39
+ operation: "ObsidianServiceInit",
40
+ }));
41
+ }
42
+ /**
43
+ * Private helper to make requests and handle common errors.
44
+ * @param config - Axios request configuration.
45
+ * @param context - Request context for logging.
46
+ * @param operationName - Name of the operation for logging context.
47
+ * @returns The response data.
48
+ * @throws {McpError} If the request fails.
49
+ */
50
+ async _request(requestConfig, context, operationName) {
51
+ const operationContext = {
52
+ ...context,
53
+ operation: `ObsidianAPI_${operationName}`,
54
+ };
55
+ logger.debug(`Making Obsidian API request: ${requestConfig.method} ${requestConfig.url}`, operationContext);
56
+ return await ErrorHandler.tryCatch(async () => {
57
+ try {
58
+ const response = await this.axiosInstance.request(requestConfig);
59
+ logger.debug(`Obsidian API request successful: ${requestConfig.method} ${requestConfig.url}`, { ...operationContext, status: response.status });
60
+ // For HEAD requests, we need the headers, so return the whole response.
61
+ // For other requests, returning response.data is fine.
62
+ if (requestConfig.method === "HEAD") {
63
+ return response;
64
+ }
65
+ return response.data;
66
+ }
67
+ catch (error) {
68
+ const axiosError = error;
69
+ let errorCode = BaseErrorCode.INTERNAL_ERROR;
70
+ let errorMessage = `Obsidian API request failed: ${axiosError.message}`;
71
+ const errorDetails = {
72
+ requestUrl: requestConfig.url,
73
+ requestMethod: requestConfig.method,
74
+ responseStatus: axiosError.response?.status,
75
+ responseData: axiosError.response?.data,
76
+ };
77
+ if (axiosError.response) {
78
+ // Handle specific HTTP status codes
79
+ switch (axiosError.response.status) {
80
+ case 400:
81
+ errorCode = BaseErrorCode.VALIDATION_ERROR;
82
+ errorMessage = `Obsidian API Bad Request: ${JSON.stringify(axiosError.response.data)}`;
83
+ break;
84
+ case 401:
85
+ errorCode = BaseErrorCode.UNAUTHORIZED;
86
+ errorMessage = "Obsidian API Unauthorized: Invalid API Key.";
87
+ break;
88
+ case 403:
89
+ errorCode = BaseErrorCode.FORBIDDEN;
90
+ errorMessage = "Obsidian API Forbidden: Check permissions.";
91
+ break;
92
+ case 404:
93
+ errorCode = BaseErrorCode.NOT_FOUND;
94
+ errorMessage = `Obsidian API Not Found: ${requestConfig.url}`;
95
+ // Log 404s at debug level, as they might be expected (e.g., checking existence)
96
+ logger.debug(errorMessage, {
97
+ ...operationContext,
98
+ ...errorDetails,
99
+ });
100
+ throw new McpError(errorCode, errorMessage, operationContext);
101
+ // NOTE: We throw immediately after logging debug for 404, skipping the general error log below.
102
+ case 405:
103
+ errorCode = BaseErrorCode.VALIDATION_ERROR; // Method not allowed often implies incorrect usage
104
+ errorMessage = `Obsidian API Method Not Allowed: ${requestConfig.method} on ${requestConfig.url}`;
105
+ break;
106
+ case 503:
107
+ errorCode = BaseErrorCode.SERVICE_UNAVAILABLE;
108
+ errorMessage = "Obsidian API Service Unavailable.";
109
+ break;
110
+ }
111
+ // General error logging for non-404 client/server errors handled above
112
+ logger.error(errorMessage, {
113
+ ...operationContext,
114
+ ...errorDetails,
115
+ });
116
+ throw new McpError(errorCode, errorMessage, operationContext);
117
+ }
118
+ else if (axiosError.request) {
119
+ // Network error (no response received)
120
+ errorCode = BaseErrorCode.SERVICE_UNAVAILABLE;
121
+ errorMessage = `Obsidian API Network Error: No response received from ${requestConfig.url}. This may be due to Obsidian not running, the Local REST API plugin being disabled, or a network issue.`;
122
+ logger.error(errorMessage, {
123
+ ...operationContext,
124
+ ...errorDetails,
125
+ });
126
+ throw new McpError(errorCode, errorMessage, operationContext);
127
+ }
128
+ else {
129
+ // Other errors (e.g., setup issues)
130
+ // Pass error object correctly if it's an Error instance
131
+ logger.error(errorMessage, error instanceof Error ? error : undefined, {
132
+ ...operationContext,
133
+ ...errorDetails,
134
+ originalError: String(error),
135
+ });
136
+ throw new McpError(errorCode, errorMessage, operationContext);
137
+ }
138
+ }
139
+ }, {
140
+ operation: `ObsidianAPI_${operationName}_Wrapper`,
141
+ context: context,
142
+ input: requestConfig, // Log request config (sanitized by ErrorHandler)
143
+ errorCode: BaseErrorCode.INTERNAL_ERROR, // Default if wrapper itself fails
144
+ });
145
+ }
146
+ // --- API Methods ---
147
+ /**
148
+ * Checks the status and authentication of the Obsidian Local REST API.
149
+ * @param context - The request context for logging and correlation.
150
+ * @returns {Promise<ApiStatusResponse>} - The status object from the API.
151
+ */
152
+ async checkStatus(context) {
153
+ // Note: This is the only endpoint that doesn't strictly require auth,
154
+ // but sending the key helps check if it's valid.
155
+ // This one is simple enough to keep inline or could be extracted too.
156
+ return this._request({
157
+ method: "GET",
158
+ url: "/",
159
+ }, context, "checkStatus");
160
+ }
161
+ // --- Vault Methods ---
162
+ /**
163
+ * Gets the content of a specific file in the vault.
164
+ * @param filePath - Vault-relative path to the file.
165
+ * @param format - 'markdown' or 'json' (for NoteJson).
166
+ * @param context - Request context.
167
+ * @returns The file content (string) or NoteJson object.
168
+ */
169
+ async getFileContent(filePath, format = "markdown", context) {
170
+ return vaultMethods.getFileContent(this._request.bind(this), filePath, format, context);
171
+ }
172
+ /**
173
+ * Updates (overwrites) the content of a file or creates it if it doesn't exist.
174
+ * @param filePath - Vault-relative path to the file.
175
+ * @param content - The new content for the file.
176
+ * @param context - Request context.
177
+ * @returns {Promise<void>} Resolves on success (204 No Content).
178
+ */
179
+ async updateFileContent(filePath, content, context) {
180
+ return vaultMethods.updateFileContent(this._request.bind(this), filePath, content, context);
181
+ }
182
+ /**
183
+ * Appends content to the end of a file. Creates the file if it doesn't exist.
184
+ * @param filePath - Vault-relative path to the file.
185
+ * @param content - The content to append.
186
+ * @param context - Request context.
187
+ * @returns {Promise<void>} Resolves on success (204 No Content).
188
+ */
189
+ async appendFileContent(filePath, content, context) {
190
+ return vaultMethods.appendFileContent(this._request.bind(this), filePath, content, context);
191
+ }
192
+ /**
193
+ * Deletes a specific file in the vault.
194
+ * @param filePath - Vault-relative path to the file.
195
+ * @param context - Request context.
196
+ * @returns {Promise<void>} Resolves on success (204 No Content).
197
+ */
198
+ async deleteFile(filePath, context) {
199
+ return vaultMethods.deleteFile(this._request.bind(this), filePath, context);
200
+ }
201
+ /**
202
+ * Lists files within a specified directory in the vault.
203
+ * @param dirPath - Vault-relative path to the directory. Use empty string "" or "/" for the root.
204
+ * @param context - Request context.
205
+ * @returns A list of file and directory names.
206
+ */
207
+ async listFiles(dirPath, context) {
208
+ return vaultMethods.listFiles(this._request.bind(this), dirPath, context);
209
+ }
210
+ /**
211
+ * Gets the metadata (stat) of a specific file using a lightweight HEAD request.
212
+ * @param filePath - Vault-relative path to the file.
213
+ * @param context - Request context.
214
+ * @returns The file's metadata.
215
+ */
216
+ async getFileMetadata(filePath, context) {
217
+ return vaultMethods.getFileMetadata(this._request.bind(this), filePath, context);
218
+ }
219
+ // --- Search Methods ---
220
+ /**
221
+ * Performs a simple text search across the vault.
222
+ * @param query - The text query string.
223
+ * @param contextLength - Number of characters surrounding each match (default 100).
224
+ * @param context - Request context.
225
+ * @returns An array of search results.
226
+ */
227
+ async searchSimple(query, contextLength = 100, context) {
228
+ return searchMethods.searchSimple(this._request.bind(this), query, contextLength, context);
229
+ }
230
+ /**
231
+ * Performs a complex search using Dataview DQL or JsonLogic.
232
+ * @param query - The query string (DQL) or JSON object (JsonLogic).
233
+ * @param contentType - The content type header indicating the query format.
234
+ * @param context - Request context.
235
+ * @returns An array of search results.
236
+ */
237
+ async searchComplex(query, contentType, context) {
238
+ return searchMethods.searchComplex(this._request.bind(this), query, contentType, context);
239
+ }
240
+ // --- Command Methods ---
241
+ /**
242
+ * Executes a registered Obsidian command by its ID.
243
+ * @param commandId - The ID of the command (e.g., "app:go-back").
244
+ * @param context - Request context.
245
+ * @returns {Promise<void>} Resolves on success (204 No Content).
246
+ */
247
+ async executeCommand(commandId, context) {
248
+ return commandMethods.executeCommand(this._request.bind(this), commandId, context);
249
+ }
250
+ /**
251
+ * Lists all available Obsidian commands.
252
+ * @param context - Request context.
253
+ * @returns A list of available commands.
254
+ */
255
+ async listCommands(context) {
256
+ return commandMethods.listCommands(this._request.bind(this), context);
257
+ }
258
+ // --- Open Methods ---
259
+ /**
260
+ * Opens a specific file in Obsidian. Creates the file if it doesn't exist.
261
+ * @param filePath - Vault-relative path to the file.
262
+ * @param newLeaf - Whether to open the file in a new editor tab (leaf).
263
+ * @param context - Request context.
264
+ * @returns {Promise<void>} Resolves on success (200 OK, but no body expected).
265
+ */
266
+ async openFile(filePath, newLeaf = false, context) {
267
+ return openMethods.openFile(this._request.bind(this), filePath, newLeaf, context);
268
+ }
269
+ // --- Active File Methods ---
270
+ /**
271
+ * Gets the content of the currently active file in Obsidian.
272
+ * @param format - 'markdown' or 'json' (for NoteJson).
273
+ * @param context - Request context.
274
+ * @returns The file content (string) or NoteJson object.
275
+ */
276
+ async getActiveFile(format = "markdown", context) {
277
+ return activeFileMethods.getActiveFile(this._request.bind(this), format, context);
278
+ }
279
+ /**
280
+ * Updates (overwrites) the content of the currently active file.
281
+ * @param content - The new content.
282
+ * @param context - Request context.
283
+ * @returns {Promise<void>} Resolves on success (204 No Content).
284
+ */
285
+ async updateActiveFile(content, context) {
286
+ return activeFileMethods.updateActiveFile(this._request.bind(this), content, context);
287
+ }
288
+ /**
289
+ * Appends content to the end of the currently active file.
290
+ * @param content - The content to append.
291
+ * @param context - Request context.
292
+ * @returns {Promise<void>} Resolves on success (204 No Content).
293
+ */
294
+ async appendActiveFile(content, context) {
295
+ return activeFileMethods.appendActiveFile(this._request.bind(this), content, context);
296
+ }
297
+ /**
298
+ * Deletes the currently active file.
299
+ * @param context - Request context.
300
+ * @returns {Promise<void>} Resolves on success (204 No Content).
301
+ */
302
+ async deleteActiveFile(context) {
303
+ return activeFileMethods.deleteActiveFile(this._request.bind(this), context);
304
+ }
305
+ // --- Periodic Notes Methods ---
306
+ // PATCH methods for periodic notes are complex and omitted for brevity
307
+ /**
308
+ * Gets the content of a periodic note (daily, weekly, etc.).
309
+ * @param period - The period type ('daily', 'weekly', 'monthly', 'quarterly', 'yearly').
310
+ * @param format - 'markdown' or 'json'.
311
+ * @param context - Request context.
312
+ * @returns The note content or NoteJson.
313
+ */
314
+ async getPeriodicNote(period, format = "markdown", context) {
315
+ return periodicNoteMethods.getPeriodicNote(this._request.bind(this), period, format, context);
316
+ }
317
+ /**
318
+ * Updates (overwrites) the content of a periodic note. Creates if needed.
319
+ * @param period - The period type.
320
+ * @param content - The new content.
321
+ * @param context - Request context.
322
+ * @returns {Promise<void>} Resolves on success (204 No Content).
323
+ */
324
+ async updatePeriodicNote(period, content, context) {
325
+ return periodicNoteMethods.updatePeriodicNote(this._request.bind(this), period, content, context);
326
+ }
327
+ /**
328
+ * Appends content to a periodic note. Creates if needed.
329
+ * @param period - The period type.
330
+ * @param content - The content to append.
331
+ * @param context - Request context.
332
+ * @returns {Promise<void>} Resolves on success (204 No Content).
333
+ */
334
+ async appendPeriodicNote(period, content, context) {
335
+ return periodicNoteMethods.appendPeriodicNote(this._request.bind(this), period, content, context);
336
+ }
337
+ /**
338
+ * Deletes a periodic note.
339
+ * @param period - The period type.
340
+ * @param context - Request context.
341
+ * @returns {Promise<void>} Resolves on success (204 No Content).
342
+ */
343
+ async deletePeriodicNote(period, context) {
344
+ return periodicNoteMethods.deletePeriodicNote(this._request.bind(this), period, context);
345
+ }
346
+ // --- Patch Methods ---
347
+ /**
348
+ * Patches a specific file in the vault using granular controls.
349
+ * @param filePath - Vault-relative path to the file.
350
+ * @param content - The content to insert/replace (string or JSON for tables/frontmatter).
351
+ * @param options - Patch operation details (operation, targetType, target, etc.).
352
+ * @param context - Request context.
353
+ * @returns {Promise<void>} Resolves on success (200 OK).
354
+ */
355
+ async patchFile(filePath, content, options, context) {
356
+ return patchMethods.patchFile(this._request.bind(this), filePath, content, options, context);
357
+ }
358
+ /**
359
+ * Patches the currently active file in Obsidian using granular controls.
360
+ * @param content - The content to insert/replace.
361
+ * @param options - Patch operation details.
362
+ * @param context - Request context.
363
+ * @returns {Promise<void>} Resolves on success (200 OK).
364
+ */
365
+ async patchActiveFile(content, options, context) {
366
+ return patchMethods.patchActiveFile(this._request.bind(this), content, options, context);
367
+ }
368
+ /**
369
+ * Patches a periodic note using granular controls.
370
+ * @param period - The period type ('daily', 'weekly', etc.).
371
+ * @param content - The content to insert/replace.
372
+ * @param options - Patch operation details.
373
+ * @param context - Request context.
374
+ * @returns {Promise<void>} Resolves on success (200 OK).
375
+ */
376
+ async patchPeriodicNote(period, content, options, context) {
377
+ return patchMethods.patchPeriodicNote(this._request.bind(this), period, content, options, context);
378
+ }
379
+ }