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,191 @@
1
+ import dotenv from "dotenv";
2
+ import { existsSync, mkdirSync, readFileSync, statSync } from "fs";
3
+ import path, { dirname, join } from "path";
4
+ import { fileURLToPath } from "url";
5
+ import { z } from "zod";
6
+ dotenv.config();
7
+ // --- Determine Project Root ---
8
+ /**
9
+ * Finds the project root directory by searching upwards for package.json.
10
+ * @param startDir The directory to start searching from.
11
+ * @returns The absolute path to the project root, or throws an error if not found.
12
+ */
13
+ const findProjectRoot = (startDir) => {
14
+ let currentDir = startDir;
15
+ while (true) {
16
+ const packageJsonPath = join(currentDir, "package.json");
17
+ if (existsSync(packageJsonPath)) {
18
+ return currentDir;
19
+ }
20
+ const parentDir = dirname(currentDir);
21
+ if (parentDir === currentDir) {
22
+ // Reached the root of the filesystem without finding package.json
23
+ throw new Error(`Could not find project root (package.json) starting from ${startDir}`);
24
+ }
25
+ currentDir = parentDir;
26
+ }
27
+ };
28
+ let projectRoot;
29
+ try {
30
+ // For ESM, __dirname is not available directly.
31
+ const currentModuleDir = dirname(fileURLToPath(import.meta.url));
32
+ projectRoot = findProjectRoot(currentModuleDir);
33
+ }
34
+ catch (error) {
35
+ console.error(`FATAL: Error determining project root: ${error.message}`);
36
+ projectRoot = process.cwd();
37
+ console.warn(`Warning: Using process.cwd() (${projectRoot}) as fallback project root.`);
38
+ }
39
+ // --- End Determine Project Root ---
40
+ const pkgPath = join(projectRoot, "package.json");
41
+ let pkg = { name: "obsidian-mcp-server", version: "0.0.0" };
42
+ try {
43
+ pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
44
+ }
45
+ catch (error) {
46
+ if (process.stderr.isTTY) {
47
+ console.error("Warning: Could not read package.json for default config values. Using hardcoded defaults.", error);
48
+ }
49
+ }
50
+ /**
51
+ * Zod schema for validating environment variables.
52
+ * @private
53
+ */
54
+ const EnvSchema = z.object({
55
+ MCP_SERVER_NAME: z.string().optional(),
56
+ MCP_SERVER_VERSION: z.string().optional(),
57
+ MCP_LOG_LEVEL: z.string().default("info"),
58
+ LOGS_DIR: z.string().default(path.join(projectRoot, "logs")),
59
+ NODE_ENV: z.string().default("development"),
60
+ MCP_TRANSPORT_TYPE: z.enum(["stdio", "http"]).default("stdio"),
61
+ MCP_HTTP_PORT: z.coerce.number().int().positive().default(3010),
62
+ MCP_HTTP_HOST: z.string().default("127.0.0.1"),
63
+ MCP_ALLOWED_ORIGINS: z.string().optional(),
64
+ MCP_AUTH_MODE: z.enum(["jwt", "oauth"]).optional(),
65
+ MCP_AUTH_SECRET_KEY: z
66
+ .string()
67
+ .min(32, "MCP_AUTH_SECRET_KEY must be at least 32 characters long for security")
68
+ .optional(),
69
+ OAUTH_ISSUER_URL: z.string().url().optional(),
70
+ OAUTH_AUDIENCE: z.string().optional(),
71
+ OAUTH_JWKS_URI: z.string().url().optional(),
72
+ // --- Obsidian Specific Config ---
73
+ OBSIDIAN_API_KEY: z.string().min(1, "OBSIDIAN_API_KEY cannot be empty"),
74
+ OBSIDIAN_BASE_URL: z.string().url().default("http://127.0.0.1:27123"),
75
+ OBSIDIAN_VERIFY_SSL: z
76
+ .string()
77
+ .transform((val) => val.toLowerCase() === "true")
78
+ .default("false"),
79
+ OBSIDIAN_CACHE_REFRESH_INTERVAL_MIN: z.coerce
80
+ .number()
81
+ .int()
82
+ .positive()
83
+ .default(10),
84
+ OBSIDIAN_ENABLE_CACHE: z
85
+ .string()
86
+ .transform((val) => val.toLowerCase() === "true")
87
+ .default("true"),
88
+ OBSIDIAN_API_SEARCH_TIMEOUT_MS: z.coerce
89
+ .number()
90
+ .int()
91
+ .positive()
92
+ .default(30000),
93
+ });
94
+ const parsedEnv = EnvSchema.safeParse(process.env);
95
+ if (!parsedEnv.success) {
96
+ const errorDetails = parsedEnv.error.flatten().fieldErrors;
97
+ if (process.stderr.isTTY) {
98
+ console.error("❌ Invalid environment variables:", errorDetails);
99
+ }
100
+ throw new Error(`Invalid environment configuration. Please check your .env file or environment variables. Details: ${JSON.stringify(errorDetails)}`);
101
+ }
102
+ const env = parsedEnv.data;
103
+ // --- Directory Ensurance Function ---
104
+ const ensureDirectory = (dirPath, rootDir, dirName) => {
105
+ const resolvedDirPath = path.isAbsolute(dirPath)
106
+ ? dirPath
107
+ : path.resolve(rootDir, dirPath);
108
+ if (!resolvedDirPath.startsWith(rootDir + path.sep) &&
109
+ resolvedDirPath !== rootDir) {
110
+ if (process.stderr.isTTY) {
111
+ console.error(`Error: ${dirName} path "${dirPath}" resolves to "${resolvedDirPath}", which is outside the project boundary "${rootDir}".`);
112
+ }
113
+ return null;
114
+ }
115
+ if (!existsSync(resolvedDirPath)) {
116
+ try {
117
+ mkdirSync(resolvedDirPath, { recursive: true });
118
+ }
119
+ catch (err) {
120
+ if (process.stderr.isTTY) {
121
+ console.error(`Error creating ${dirName} directory at ${resolvedDirPath}: ${err instanceof Error ? err.message : String(err)}`);
122
+ }
123
+ return null;
124
+ }
125
+ }
126
+ else {
127
+ try {
128
+ if (!statSync(resolvedDirPath).isDirectory()) {
129
+ if (process.stderr.isTTY) {
130
+ console.error(`Error: ${dirName} path ${resolvedDirPath} exists but is not a directory.`);
131
+ }
132
+ return null;
133
+ }
134
+ }
135
+ catch (statError) {
136
+ if (process.stderr.isTTY) {
137
+ console.error(`Error accessing ${dirName} path ${resolvedDirPath}: ${statError.message}`);
138
+ }
139
+ return null;
140
+ }
141
+ }
142
+ return resolvedDirPath;
143
+ };
144
+ // --- End Directory Ensurance Function ---
145
+ const validatedLogsPath = ensureDirectory(env.LOGS_DIR, projectRoot, "logs");
146
+ if (!validatedLogsPath) {
147
+ if (process.stderr.isTTY) {
148
+ console.error("FATAL: Logs directory configuration is invalid or could not be created. Please check permissions and path. Exiting.");
149
+ }
150
+ process.exit(1);
151
+ }
152
+ /**
153
+ * Main application configuration object.
154
+ */
155
+ export const config = {
156
+ pkg,
157
+ mcpServerName: env.MCP_SERVER_NAME || pkg.name,
158
+ mcpServerVersion: env.MCP_SERVER_VERSION || pkg.version,
159
+ logLevel: env.MCP_LOG_LEVEL,
160
+ logsPath: validatedLogsPath,
161
+ environment: env.NODE_ENV,
162
+ mcpTransportType: env.MCP_TRANSPORT_TYPE,
163
+ mcpHttpPort: env.MCP_HTTP_PORT,
164
+ mcpHttpHost: env.MCP_HTTP_HOST,
165
+ mcpAllowedOrigins: env.MCP_ALLOWED_ORIGINS?.split(",")
166
+ .map((origin) => origin.trim())
167
+ .filter(Boolean),
168
+ mcpAuthMode: env.MCP_AUTH_MODE,
169
+ mcpAuthSecretKey: env.MCP_AUTH_SECRET_KEY,
170
+ oauthIssuerUrl: env.OAUTH_ISSUER_URL,
171
+ oauthAudience: env.OAUTH_AUDIENCE,
172
+ oauthJwksUri: env.OAUTH_JWKS_URI,
173
+ obsidianApiKey: env.OBSIDIAN_API_KEY,
174
+ obsidianBaseUrl: env.OBSIDIAN_BASE_URL,
175
+ obsidianVerifySsl: env.OBSIDIAN_VERIFY_SSL,
176
+ obsidianCacheRefreshIntervalMin: env.OBSIDIAN_CACHE_REFRESH_INTERVAL_MIN,
177
+ obsidianEnableCache: env.OBSIDIAN_ENABLE_CACHE,
178
+ obsidianApiSearchTimeoutMs: env.OBSIDIAN_API_SEARCH_TIMEOUT_MS,
179
+ };
180
+ /**
181
+ * The configured logging level for the application.
182
+ * Exported separately for convenience (e.g., logger initialization).
183
+ * @type {string}
184
+ */
185
+ export const logLevel = config.logLevel;
186
+ /**
187
+ * The configured runtime environment for the application.
188
+ * Exported separately for convenience.
189
+ * @type {string}
190
+ */
191
+ export const environment = config.environment;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,2 @@
1
1
  #!/usr/bin/env node
2
- export { run } from "./mcp/server.js";
3
- export * from "./obsidian/index.js";
4
- export * from "./resources/index.js";
5
- export * from "./tools/index.js";
6
- export * from "./utils/index.js";
2
+ export {};
package/dist/index.js CHANGED
@@ -1,20 +1,298 @@
1
1
  #!/usr/bin/env node
2
- import { run } from "./mcp/server.js";
3
- import { createLogger } from "./utils/logging.js";
4
- // Create a logger for the main entry point
5
- const logger = createLogger('Main');
6
- // Main entry point
7
- if (import.meta.url === `file://${process.argv[1]}`) {
8
- logger.info("Starting Obsidian MCP Server");
9
- run().catch((error) => {
10
- logger.error("Failed to start server:", error);
11
- process.exit(1);
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { config, environment } from "./config/index.js"; // This loads .env via dotenv.config()
4
+ import { initializeAndStartServer } from "./mcp-server/server.js";
5
+ import { requestContextService, retryWithDelay } from "./utils/index.js";
6
+ import { logger } from "./utils/internal/logger.js"; // Import logger instance early
7
+ // Import Services
8
+ import { ObsidianRestApiService } from "./services/obsidianRestAPI/index.js";
9
+ import { VaultCacheService } from "./services/obsidianRestAPI/vaultCache/index.js"; // Import VaultCacheService
10
+ /**
11
+ * The main MCP server instance (only stored globally for stdio shutdown).
12
+ * @type {McpServer | undefined}
13
+ */
14
+ let server;
15
+ /**
16
+ * The main HTTP server instance (only stored globally for http shutdown).
17
+ * @type {ServerType | undefined}
18
+ */
19
+ let httpServerInstance;
20
+ /**
21
+ * Shared Obsidian REST API service instance.
22
+ * @type {ObsidianRestApiService | undefined}
23
+ */
24
+ let obsidianService;
25
+ /**
26
+ * Shared Vault Cache service instance.
27
+ * @type {VaultCacheService | undefined}
28
+ */
29
+ let vaultCacheService;
30
+ /**
31
+ * Gracefully shuts down the main MCP server.
32
+ * Handles process termination signals (SIGTERM, SIGINT) and critical errors.
33
+ *
34
+ * @param signal - The signal or event name that triggered the shutdown (e.g., "SIGTERM", "uncaughtException").
35
+ */
36
+ const shutdown = async (signal) => {
37
+ // Define context for the shutdown operation
38
+ const shutdownContext = requestContextService.createRequestContext({
39
+ operation: "Shutdown",
40
+ signal,
41
+ });
42
+ logger.info(`Received ${signal}. Starting graceful shutdown...`, shutdownContext);
43
+ try {
44
+ // Stop cache refresh timer first
45
+ if (config.obsidianEnableCache && vaultCacheService) {
46
+ vaultCacheService.stopPeriodicRefresh();
47
+ }
48
+ // Close the main MCP server (only relevant for stdio)
49
+ if (server) {
50
+ logger.info("Closing main MCP server (stdio)...", shutdownContext);
51
+ await server.close();
52
+ logger.info("Main MCP server (stdio) closed successfully", shutdownContext);
53
+ }
54
+ // Close the main HTTP server instance (if it exists)
55
+ if (httpServerInstance) {
56
+ logger.info("Closing main HTTP server...", shutdownContext);
57
+ await new Promise((resolve, reject) => {
58
+ httpServerInstance.close((err) => {
59
+ if (err) {
60
+ logger.error("Error closing HTTP server", err, shutdownContext);
61
+ reject(err);
62
+ return;
63
+ }
64
+ logger.info("Main HTTP server closed successfully", shutdownContext);
65
+ resolve();
66
+ });
67
+ });
68
+ }
69
+ if (!server && !httpServerInstance) {
70
+ logger.warning("No server instance (Stdio or HTTP) found to close during shutdown.", shutdownContext);
71
+ }
72
+ // Add any other necessary cleanup here (e.g., closing database connections if added later)
73
+ logger.info("Graceful shutdown completed successfully", shutdownContext);
74
+ process.exit(0);
75
+ }
76
+ catch (error) {
77
+ // Handle any errors during shutdown
78
+ logger.error("Critical error during shutdown", error instanceof Error ? error : undefined, {
79
+ ...shutdownContext, // Spread the existing RequestContext
80
+ // error field is handled by logger.error's second argument
81
+ });
82
+ process.exit(1); // Exit with error code if shutdown fails
83
+ }
84
+ };
85
+ /**
86
+ * Initializes and starts the main MCP server.
87
+ * Sets up request context, initializes the server instance, starts the transport,
88
+ * and registers signal handlers for graceful shutdown and error handling.
89
+ */
90
+ const start = async () => {
91
+ // --- Logger Initialization (Moved here AFTER config/dotenv is loaded) ---
92
+ const validMcpLogLevels = [
93
+ "debug",
94
+ "info",
95
+ "notice",
96
+ "warning",
97
+ "error",
98
+ "crit",
99
+ "alert",
100
+ "emerg",
101
+ ];
102
+ // Read level from config (which read from env var or default)
103
+ const initialLogLevelConfig = config.logLevel;
104
+ // Validate the configured log level
105
+ let validatedMcpLogLevel = "info"; // Default to 'info'
106
+ if (validMcpLogLevels.includes(initialLogLevelConfig)) {
107
+ validatedMcpLogLevel = initialLogLevelConfig;
108
+ }
109
+ else {
110
+ // Use console.warn here as logger isn't initialized yet
111
+ console.warn(`Invalid MCP_LOG_LEVEL "${initialLogLevelConfig}" provided via config/env. Defaulting to "info".`);
112
+ }
113
+ // Initialize the logger with the validated MCP level and wait for it to complete.
114
+ await logger.initialize(validatedMcpLogLevel);
115
+ // Log initialization message using the logger itself (will go to file/console)
116
+ logger.info(`Logger initialized by start(). MCP logging level: ${validatedMcpLogLevel}`);
117
+ // --- End Logger Initialization ---
118
+ // Log that config is loaded (this was previously done earlier)
119
+ logger.debug("Configuration loaded successfully", requestContextService.createRequestContext({
120
+ configLoaded: true,
121
+ configSummary: {
122
+ serverName: config.mcpServerName,
123
+ transport: config.mcpTransportType,
124
+ logLevel: config.logLevel,
125
+ },
126
+ }));
127
+ // Create application-level request context using the service instance
128
+ // Use the validated transport type from the config object
129
+ const transportType = config.mcpTransportType;
130
+ const startupContext = requestContextService.createRequestContext({
131
+ operation: `ServerStartup_${transportType}`, // Include transport in operation name
132
+ appName: config.mcpServerName,
133
+ appVersion: config.mcpServerVersion,
134
+ environment: environment,
12
135
  });
13
- }
14
- // Export the run function and other important modules
15
- export { run } from "./mcp/server.js";
16
- export * from "./obsidian/index.js";
17
- export * from "./resources/index.js";
18
- export * from "./tools/index.js";
19
- export * from "./utils/index.js";
20
- //# sourceMappingURL=index.js.map
136
+ logger.info(`Starting ${config.mcpServerName} v${config.mcpServerVersion} (Transport: ${transportType})...`, startupContext);
137
+ try {
138
+ // --- Instantiate Shared Services ---
139
+ logger.debug("Instantiating shared services...", startupContext);
140
+ obsidianService = new ObsidianRestApiService(); // Instantiate Obsidian Service
141
+ // --- Perform Initial Obsidian API Status Check ---
142
+ try {
143
+ logger.info("Performing initial Obsidian API status check with retries...", startupContext);
144
+ const status = await retryWithDelay(async () => {
145
+ if (!obsidianService) {
146
+ // This case should not happen in practice, but it satisfies the type checker.
147
+ throw new Error("Obsidian service not initialized.");
148
+ }
149
+ const checkStatusContext = {
150
+ ...startupContext,
151
+ operation: "checkStatusAttempt",
152
+ };
153
+ const currentStatus = await obsidianService.checkStatus(checkStatusContext);
154
+ if (currentStatus?.service !== "Obsidian Local REST API" ||
155
+ !currentStatus?.authenticated) {
156
+ // Throw an error to trigger a retry
157
+ throw new Error(`Obsidian API status check failed or indicates authentication issue. Status: ${JSON.stringify(currentStatus)}`);
158
+ }
159
+ return currentStatus;
160
+ }, {
161
+ operationName: "initialObsidianApiCheck",
162
+ context: startupContext,
163
+ maxRetries: 5, // Retry up to 5 times
164
+ delayMs: 3000, // Wait 3 seconds between retries
165
+ });
166
+ logger.info("Obsidian API status check successful.", {
167
+ ...startupContext,
168
+ obsidianVersion: status.versions.obsidian,
169
+ pluginVersion: status.versions.self,
170
+ });
171
+ }
172
+ catch (statusError) {
173
+ logger.error("Critical error during initial Obsidian API status check after multiple retries. Check OBSIDIAN_BASE_URL, OBSIDIAN_API_KEY, and plugin status.", {
174
+ ...startupContext,
175
+ error: statusError instanceof Error
176
+ ? statusError.message
177
+ : String(statusError),
178
+ stack: statusError instanceof Error ? statusError.stack : undefined,
179
+ });
180
+ // Re-throw the final error to be caught by the main startup catch block, which will exit the process.
181
+ throw statusError;
182
+ }
183
+ // --- End Status Check ---
184
+ if (config.obsidianEnableCache) {
185
+ vaultCacheService = new VaultCacheService(obsidianService); // Instantiate Cache Service, passing Obsidian Service
186
+ logger.info("Vault cache is enabled and service is instantiated.", startupContext);
187
+ }
188
+ else {
189
+ logger.info("Vault cache is disabled by configuration.", startupContext);
190
+ }
191
+ logger.info("Shared services instantiated.", startupContext);
192
+ // --- End Service Instantiation ---
193
+ // Initialize the server instance and start the selected transport
194
+ logger.debug("Initializing and starting MCP server transport", startupContext);
195
+ // Start the server transport. Services are instantiated here and passed down.
196
+ // For stdio, this returns the McpServer instance.
197
+ // For http, it returns the http.Server instance.
198
+ const serverOrHttpInstance = await initializeAndStartServer(obsidianService, vaultCacheService);
199
+ if (transportType === "stdio" &&
200
+ serverOrHttpInstance instanceof McpServer) {
201
+ server = serverOrHttpInstance; // Store McpServer for stdio
202
+ logger.debug("Stored McpServer instance for stdio transport.", startupContext);
203
+ }
204
+ else if (transportType === "http" && serverOrHttpInstance) {
205
+ // The instance is of ServerType (http.Server or https.Server)
206
+ httpServerInstance = serverOrHttpInstance; // Store ServerType for http transport
207
+ logger.debug("Stored http.Server instance for http transport.", startupContext);
208
+ }
209
+ else if (transportType === "http") {
210
+ // This case should ideally not be reached if startHttpTransport always returns an http.Server
211
+ logger.warning("HTTP transport selected, but initializeAndStartServer did not return an http.Server instance.", startupContext);
212
+ }
213
+ // If initializeAndStartServer failed, it would have thrown an error,
214
+ // and execution would jump to the outer catch block.
215
+ logger.info(`${config.mcpServerName} is running with ${transportType} transport`, {
216
+ ...startupContext,
217
+ startTime: new Date().toISOString(),
218
+ });
219
+ // --- Trigger Background Cache Build ---
220
+ if (config.obsidianEnableCache && vaultCacheService) {
221
+ // Start building the cache, but don't wait for it to finish.
222
+ // The server will be operational while the cache builds.
223
+ // Tools needing the cache should check its readiness state.
224
+ logger.info("Triggering background vault cache build...", startupContext);
225
+ // No 'await' here - run in background
226
+ vaultCacheService
227
+ .buildVaultCache()
228
+ .then(() => {
229
+ // Once the initial build is done, start the periodic refresh
230
+ vaultCacheService?.startPeriodicRefresh();
231
+ })
232
+ .catch((cacheBuildError) => {
233
+ // Log errors during the background build process
234
+ logger.error("Error occurred during background vault cache build", {
235
+ ...startupContext, // Use startup context for correlation
236
+ operation: "BackgroundCacheBuild",
237
+ error: cacheBuildError instanceof Error
238
+ ? cacheBuildError.message
239
+ : String(cacheBuildError),
240
+ stack: cacheBuildError instanceof Error
241
+ ? cacheBuildError.stack
242
+ : undefined,
243
+ });
244
+ });
245
+ }
246
+ // --- End Cache Build Trigger ---
247
+ // --- Signal and Error Handling Setup ---
248
+ // Handle process signals for graceful shutdown
249
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
250
+ process.on("SIGINT", () => shutdown("SIGINT"));
251
+ // Handle uncaught exceptions
252
+ process.on("uncaughtException", async (error) => {
253
+ const errorContext = {
254
+ ...startupContext, // Include base context for correlation
255
+ event: "uncaughtException",
256
+ error: error instanceof Error ? error.message : String(error),
257
+ stack: error instanceof Error ? error.stack : undefined,
258
+ };
259
+ logger.error("Uncaught exception detected. Initiating shutdown...", errorContext);
260
+ // Attempt graceful shutdown; shutdown() handles its own errors.
261
+ await shutdown("uncaughtException");
262
+ // If shutdown fails internally, it will call process.exit(1).
263
+ // If shutdown succeeds, it calls process.exit(0).
264
+ // If shutdown itself throws unexpectedly *before* exiting, this process might terminate abruptly,
265
+ // but the core shutdown logic is handled within shutdown().
266
+ });
267
+ // Handle unhandled promise rejections
268
+ process.on("unhandledRejection", async (reason) => {
269
+ const rejectionContext = {
270
+ ...startupContext, // Include base context for correlation
271
+ event: "unhandledRejection",
272
+ reason: reason instanceof Error ? reason.message : String(reason),
273
+ stack: reason instanceof Error ? reason.stack : undefined,
274
+ };
275
+ logger.error("Unhandled promise rejection detected. Initiating shutdown...", rejectionContext);
276
+ // Attempt graceful shutdown; shutdown() handles its own errors.
277
+ await shutdown("unhandledRejection");
278
+ // Similar logic as uncaughtException: shutdown handles its exit codes.
279
+ });
280
+ }
281
+ catch (error) {
282
+ // Handle critical startup errors (already logged by ErrorHandler or caught above)
283
+ // Log the final failure context, including error details, before exiting
284
+ logger.error("Critical error during startup, exiting.", {
285
+ ...startupContext,
286
+ finalErrorContext: "Startup Failure",
287
+ error: error instanceof Error ? error.message : String(error),
288
+ stack: error instanceof Error ? error.stack : undefined,
289
+ });
290
+ process.exit(1);
291
+ }
292
+ };
293
+ // --- Async IIFE to allow top-level await ---
294
+ // This remains necessary because start() is async
295
+ (async () => {
296
+ // Start the application
297
+ await start();
298
+ })(); // End async IIFE
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @fileoverview Main entry point for the MCP (Model Context Protocol) server.
3
+ * This file orchestrates the server's lifecycle:
4
+ * 1. Initializes the core `McpServer` instance (from `@modelcontextprotocol/sdk`) with its identity and capabilities.
5
+ * 2. Registers available resources and tools, making them discoverable and usable by clients.
6
+ * 3. Selects and starts the appropriate communication transport (stdio or Streamable HTTP)
7
+ * based on configuration.
8
+ * 4. Handles top-level error management during startup.
9
+ *
10
+ * MCP Specification References:
11
+ * - Lifecycle: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/lifecycle.mdx
12
+ * - Overview (Capabilities): https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/index.mdx
13
+ * - Transports: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/transports.mdx
14
+ * @module src/mcp-server/server
15
+ */
16
+ import { ServerType } from "@hono/node-server";
17
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
18
+ import { ObsidianRestApiService } from "../services/obsidianRestAPI/index.js";
19
+ import { VaultCacheService } from "../services/obsidianRestAPI/vaultCache/index.js";
20
+ /**
21
+ * Main application entry point. Initializes services and starts the MCP server.
22
+ * Orchestrates server startup, transport selection, and top-level error handling.
23
+ *
24
+ * MCP Spec Relevance:
25
+ * - Manages server startup, leading to a server ready for MCP messages.
26
+ * - Handles critical startup failures, ensuring appropriate process exit.
27
+ *
28
+ * @param {ObsidianRestApiService} obsidianService - The shared Obsidian REST API service instance, instantiated by the caller (e.g., index.ts).
29
+ * @param {VaultCacheService | undefined} vaultCacheService - The shared Vault Cache service instance, instantiated by the caller (e.g., index.ts).
30
+ * @returns {Promise<void | McpServer>} For 'stdio', resolves with `McpServer`. For 'http', runs indefinitely.
31
+ * Rejects on critical failure, leading to process exit.
32
+ */
33
+ export declare function initializeAndStartServer(obsidianService: ObsidianRestApiService, vaultCacheService: VaultCacheService | undefined): Promise<void | McpServer | ServerType>;