obsidian-mcp-server 1.5.7 → 2.0.1

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 (204) hide show
  1. package/README.md +249 -101
  2. package/dist/config/index.d.ts +41 -0
  3. package/dist/config/index.js +191 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +298 -0
  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 -0
  97. package/dist/utils/index.js +13 -0
  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 +72 -40
  133. package/.github/workflows/publish.yml +0 -35
  134. package/Dockerfile +0 -63
  135. package/build/index.js +0 -20
  136. package/build/mcp/handlers.js +0 -305
  137. package/build/mcp/index.js +0 -7
  138. package/build/mcp/server.js +0 -240
  139. package/build/mcp/types.js +0 -49
  140. package/build/obsidian/client.js +0 -403
  141. package/build/obsidian/errors.js +0 -75
  142. package/build/obsidian/index.js +0 -7
  143. package/build/obsidian/types.js +0 -12
  144. package/build/resources/index.js +0 -15
  145. package/build/resources/tags.js +0 -257
  146. package/build/resources/types.js +0 -5
  147. package/build/tools/base.js +0 -88
  148. package/build/tools/files/content.js +0 -171
  149. package/build/tools/files/index.js +0 -22
  150. package/build/tools/files/list.js +0 -133
  151. package/build/tools/index.js +0 -31
  152. package/build/tools/properties/index.js +0 -19
  153. package/build/tools/properties/manager.js +0 -302
  154. package/build/tools/properties/tools.js +0 -239
  155. package/build/tools/properties/types.js +0 -70
  156. package/build/tools/search/complex.js +0 -270
  157. package/build/tools/search/index.js +0 -20
  158. package/build/tools/search/simple.js +0 -127
  159. package/build/utils/errors.js +0 -59
  160. package/build/utils/idGenerator.js +0 -21
  161. package/build/utils/index.js +0 -9
  162. package/build/utils/logging.js +0 -417
  163. package/build/utils/rate-limiting.js +0 -94
  164. package/build/utils/tokenization.js +0 -75
  165. package/build/utils/validation.js +0 -92
  166. package/debug.js +0 -98
  167. package/docs/tree.md +0 -67
  168. package/env.json +0 -42
  169. package/mcp-client-config.example.json +0 -23
  170. package/repomix.config.json +0 -20
  171. package/scripts/clean.ts +0 -103
  172. package/scripts/tree.ts +0 -599
  173. package/src/index.ts +0 -23
  174. package/src/mcp/handlers.ts +0 -410
  175. package/src/mcp/index.ts +0 -7
  176. package/src/mcp/server.ts +0 -288
  177. package/src/mcp/types.ts +0 -112
  178. package/src/obsidian/client.ts +0 -505
  179. package/src/obsidian/errors.ts +0 -105
  180. package/src/obsidian/index.ts +0 -7
  181. package/src/obsidian/types.ts +0 -124
  182. package/src/resources/index.ts +0 -17
  183. package/src/resources/tags.ts +0 -283
  184. package/src/resources/types.ts +0 -30
  185. package/src/tools/base.ts +0 -123
  186. package/src/tools/files/content.ts +0 -206
  187. package/src/tools/files/index.ts +0 -31
  188. package/src/tools/files/list.ts +0 -150
  189. package/src/tools/index.ts +0 -38
  190. package/src/tools/properties/index.ts +0 -21
  191. package/src/tools/properties/manager.ts +0 -370
  192. package/src/tools/properties/tools.ts +0 -280
  193. package/src/tools/properties/types.ts +0 -117
  194. package/src/tools/search/complex.ts +0 -303
  195. package/src/tools/search/index.ts +0 -22
  196. package/src/tools/search/simple.ts +0 -147
  197. package/src/utils/errors.ts +0 -69
  198. package/src/utils/idGenerator.ts +0 -22
  199. package/src/utils/index.ts +0 -8
  200. package/src/utils/logging.ts +0 -558
  201. package/src/utils/rate-limiting.ts +0 -114
  202. package/src/utils/tokenization.ts +0 -85
  203. package/src/utils/validation.ts +0 -105
  204. package/tsconfig.json +0 -17
package/src/mcp/types.ts DELETED
@@ -1,112 +0,0 @@
1
- /**
2
- * MCP server type definitions
3
- */
4
- import { TextContent, Tool } from "@modelcontextprotocol/sdk/types.js";
5
- import { ErrorCategoryType, LogLevel, StandardizedErrorObject } from "../utils/logging.js";
6
-
7
- /**
8
- * MCP server configuration
9
- */
10
- export interface McpServerConfig extends Record<string, unknown> {
11
- name: string;
12
- version: string;
13
- }
14
-
15
- /**
16
- * Timeout configuration for tool execution
17
- */
18
- export interface TimeoutConfig {
19
- toolExecutionMs: number; // Default: 60000 (60 seconds)
20
- }
21
-
22
- /**
23
- * Map of resources by URI
24
- */
25
- export interface ResourceMap {
26
- [uri: string]: {
27
- getContent(): Promise<TextContent[]>;
28
- getResourceDescription(): any;
29
- };
30
- }
31
-
32
- /**
33
- * Server capabilities configuration
34
- */
35
- export interface ServerCapabilities {
36
- tools: Record<string, Tool>;
37
- resources: ResourceMap;
38
- }
39
-
40
- /**
41
- * Operation result interface following MCP best practices
42
- */
43
- export interface OperationResultSuccess<DataType> {
44
- resultSuccessful: true;
45
- resultData: DataType;
46
- }
47
-
48
- export interface OperationResultFailure {
49
- resultSuccessful: false;
50
- resultError: StandardizedErrorObject;
51
- }
52
-
53
- export type OperationResult<DataType> =
54
- | OperationResultSuccess<DataType>
55
- | OperationResultFailure;
56
-
57
- /**
58
- * Error codes used in the MCP server
59
- */
60
- export enum McpErrorCode {
61
- // Server errors (5xxxx)
62
- INTERNAL_SERVER_ERROR = 50000,
63
- SERVICE_UNAVAILABLE = 50300,
64
- TIMEOUT = 50800,
65
-
66
- // Client errors (4xxxx)
67
- BAD_REQUEST = 40000,
68
- UNAUTHORIZED = 40100,
69
- FORBIDDEN = 40300,
70
- NOT_FOUND = 40400,
71
- METHOD_NOT_ALLOWED = 40500,
72
- RATE_LIMIT_EXCEEDED = 40900,
73
-
74
- // Success with special handling
75
- SUCCESS_NO_CONTENT = 20400
76
- }
77
-
78
- /**
79
- * Default timeout configuration
80
- */
81
- export const DEFAULT_TIMEOUT_CONFIG: TimeoutConfig = {
82
- toolExecutionMs: parseInt(process.env.TOOL_TIMEOUT_MS ?? '60000') // 60 second default timeout
83
- };
84
-
85
- /**
86
- * Helper functions for creating operation results
87
- */
88
- export function createSuccessResult<DataType>(data: DataType): OperationResultSuccess<DataType> {
89
- return {
90
- resultSuccessful: true,
91
- resultData: data
92
- };
93
- }
94
-
95
- export function createFailureResult(
96
- message: string,
97
- code: string | McpErrorCode,
98
- category: ErrorCategoryType = ErrorCategoryType.CATEGORY_UNKNOWN,
99
- context?: Record<string, unknown>
100
- ): OperationResultFailure {
101
- return {
102
- resultSuccessful: false,
103
- resultError: {
104
- errorMessage: message,
105
- errorCode: typeof code === 'string' ? code : String(code),
106
- errorCategory: category,
107
- errorSeverity: LogLevel.ERROR,
108
- errorTimestamp: new Date().toISOString(),
109
- errorContext: context
110
- }
111
- };
112
- }
@@ -1,505 +0,0 @@
1
- /**
2
- * Obsidian REST API client implementation
3
- */
4
- import type { AxiosInstance, AxiosRequestConfig } from "axios";
5
- import axios from "axios";
6
- import { readFileSync } from "fs";
7
- import { Agent } from "node:https";
8
- import { dirname, join } from "path";
9
- import { fileURLToPath } from 'url';
10
-
11
- import { ObsidianError } from '../utils/errors.js';
12
- import { createLogger } from '../utils/logging.js';
13
- import { sanitizeHeader, validateFilePath } from '../utils/validation.js';
14
- import {
15
- createMissingAPIKeyMessage,
16
- handleAxiosError
17
- } from './errors.js';
18
- import {
19
- DEFAULT_OBSIDIAN_CONFIG,
20
- JsonLogicQuery,
21
- NoteJson,
22
- ObsidianCommand,
23
- ObsidianConfig,
24
- ObsidianFile,
25
- ObsidianServerConfig,
26
- ObsidianStatus,
27
- PeriodType,
28
- SearchResponse,
29
- SimpleSearchResult
30
- } from './types.js';
31
-
32
- // Logger for the ObsidianClient
33
- const logger = createLogger('ObsidianClient');
34
-
35
- // Get package version for user agent
36
- const __filename = fileURLToPath(import.meta.url);
37
- const __dirname = dirname(__filename);
38
- const VERSION = (() => {
39
- try {
40
- // Look for package.json in the same directory as the built files
41
- const packagePath = join(__dirname, '..', '..', 'package.json');
42
- const pkg = JSON.parse(readFileSync(packagePath, 'utf-8'));
43
- return pkg.version;
44
- } catch (error) {
45
- // Try alternative location for development
46
- try {
47
- const devPackagePath = join(__dirname, '..', '..', '..', 'package.json');
48
- const pkg = JSON.parse(readFileSync(devPackagePath, 'utf-8'));
49
- return pkg.version;
50
- } catch (devError) {
51
- logger.warn('Could not read package.json version, using fallback');
52
- return '1.1.0'; // Fallback version
53
- }
54
- }
55
- })();
56
-
57
- /**
58
- * Client for interacting with the Obsidian Local REST API
59
- */
60
- export class ObsidianClient {
61
- private client: AxiosInstance;
62
- private config: Required<ObsidianConfig> & ObsidianServerConfig;
63
-
64
- /**
65
- * Create a new ObsidianClient
66
- * @param config Configuration for the client
67
- */
68
- constructor(config: ObsidianConfig) {
69
- if (!config.apiKey) {
70
- throw new ObsidianError(
71
- createMissingAPIKeyMessage(),
72
- 40100 // Unauthorized
73
- );
74
- }
75
-
76
- // Determine if we're in a development environment
77
- const isDev = process.env.NODE_ENV === 'development' || !process.env.NODE_ENV;
78
-
79
- // Read environment variables with fallbacks
80
- const envConfig = {
81
- protocol: process.env.OBSIDIAN_PROTOCOL as "http" | "https" || DEFAULT_OBSIDIAN_CONFIG.protocol,
82
- host: process.env.OBSIDIAN_HOST || DEFAULT_OBSIDIAN_CONFIG.host,
83
- port: parseInt(process.env.OBSIDIAN_PORT || String(DEFAULT_OBSIDIAN_CONFIG.port)),
84
- verifySSL: process.env.VERIFY_SSL ? process.env.VERIFY_SSL === 'true' : (isDev ? false : true),
85
- timeout: parseInt(process.env.REQUEST_TIMEOUT || '5000'),
86
- maxContentLength: parseInt(process.env.MAX_CONTENT_LENGTH || String(50 * 1024 * 1024)),
87
- maxBodyLength: parseInt(process.env.MAX_BODY_LENGTH || String(50 * 1024 * 1024))
88
- };
89
-
90
- // Combine defaults with provided config and environment variables
91
- this.config = {
92
- protocol: envConfig.protocol,
93
- host: envConfig.host,
94
- port: envConfig.port,
95
- verifySSL: config.verifySSL ?? envConfig.verifySSL,
96
- apiKey: config.apiKey,
97
- timeout: config.timeout ?? envConfig.timeout,
98
- maxContentLength: config.maxContentLength ?? envConfig.maxContentLength,
99
- maxBodyLength: config.maxBodyLength ?? envConfig.maxBodyLength
100
- };
101
-
102
- // Configure HTTPS agent
103
- const httpsAgent = new Agent({
104
- rejectUnauthorized: this.config.verifySSL
105
- });
106
-
107
- const axiosConfig: AxiosRequestConfig = {
108
- baseURL: this.getBaseUrl(),
109
- headers: {
110
- ...this.getHeaders(),
111
- // Security headers
112
- 'X-Content-Type-Options': 'nosniff',
113
- 'X-Frame-Options': 'DENY',
114
- 'X-XSS-Protection': '1; mode=block',
115
- 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains'
116
- },
117
- validateStatus: (status) => status >= 200 && status < 300,
118
- timeout: this.config.timeout,
119
- maxRedirects: 5,
120
- maxContentLength: this.config.maxContentLength,
121
- maxBodyLength: this.config.maxBodyLength,
122
- httpsAgent,
123
- // Additional security configurations
124
- xsrfCookieName: 'XSRF-TOKEN',
125
- xsrfHeaderName: 'X-XSRF-TOKEN',
126
- withCredentials: true,
127
- decompress: true
128
- };
129
-
130
- if (!this.config.verifySSL) {
131
- logger.warn(
132
- "WARNING: SSL verification is disabled. While this works for development, it's not recommended for production.\n" +
133
- "To properly configure SSL certificates:\n" +
134
- "1. Go to Obsidian Settings > Local REST API\n" +
135
- "2. Under 'How to Access', copy the certificate\n" +
136
- "3. For Windows users:\n" +
137
- " - Open 'certmgr.msc' (Windows Certificate Manager)\n" +
138
- " - Go to 'Trusted Root Certification Authorities' > 'Certificates'\n" +
139
- " - Right-click > 'All Tasks' > 'Import' and follow the wizard\n" +
140
- " - Select the certificate file you copied from Obsidian\n" +
141
- "4. For other systems:\n" +
142
- " - macOS: Add to Keychain Access\n" +
143
- " - Linux: Add to ca-certificates"
144
- );
145
- }
146
-
147
- this.client = axios.create(axiosConfig);
148
- }
149
-
150
- /**
151
- * Get the base URL for the Obsidian API
152
- */
153
- private getBaseUrl(): string {
154
- return `${this.config.protocol}://${this.config.host}:${this.config.port}`;
155
- }
156
-
157
- /**
158
- * Get headers for requests to the Obsidian API
159
- */
160
- private getHeaders(): Record<string, string> {
161
- const headers: Record<string, string> = {
162
- Authorization: `Bearer ${this.config.apiKey}`,
163
- 'Accept': 'application/json',
164
- 'User-Agent': `obsidian-mcp-server/${VERSION}`
165
- };
166
-
167
- // Sanitize headers
168
- return Object.fromEntries(
169
- Object.entries(headers).map(([key, value]) => [
170
- key,
171
- sanitizeHeader(value)
172
- ])
173
- );
174
- }
175
-
176
- /**
177
- * Safely execute an API request with error handling
178
- */
179
- private async safeRequest<T>(operation: () => Promise<T>): Promise<T> {
180
- try {
181
- return await operation();
182
- } catch (error) {
183
- if (axios.isAxiosError(error)) {
184
- throw handleAxiosError(error, this.config.host, this.config.port);
185
- }
186
-
187
- if (error instanceof Error) {
188
- throw new ObsidianError(error.message, 50000, error);
189
- }
190
-
191
- throw new ObsidianError("Unknown error occurred", 50000, error);
192
- }
193
- }
194
-
195
- /**
196
- * List all files in the vault
197
- */
198
- async listFilesInVault(): Promise<ObsidianFile[]> {
199
- return this.safeRequest(async () => {
200
- logger.debug('Listing all files in vault');
201
- const response = await this.client.get<{ files: ObsidianFile[] }>("/vault/");
202
- return response.data.files;
203
- });
204
- }
205
-
206
- /**
207
- * List files in a specific directory
208
- */
209
- async listFilesInDir(dirpath: string): Promise<ObsidianFile[]> {
210
- validateFilePath(dirpath);
211
- return this.safeRequest(async () => {
212
- logger.debug(`Listing files in directory: ${dirpath}`);
213
- const response = await this.client.get<{ files: ObsidianFile[] }>(`/vault/${dirpath}/`);
214
- return response.data.files;
215
- });
216
- }
217
-
218
- /**
219
- * Get the contents of a file
220
- */
221
- async getFileContents(filepath: string): Promise<string> {
222
- validateFilePath(filepath);
223
- return this.safeRequest(async () => {
224
- logger.debug(`Getting contents of file: ${filepath}`);
225
- const response = await this.client.get<string>(`/vault/${filepath}`);
226
- return response.data;
227
- });
228
- }
229
-
230
- /**
231
- * Search for a string across all files
232
- */
233
- async search(query: string, contextLength: number = 100): Promise<SimpleSearchResult[]> {
234
- return this.safeRequest(async () => {
235
- logger.debug(`Searching for: ${query} with context length: ${contextLength}`);
236
- const response = await this.client.post<SimpleSearchResult[]>(
237
- "/search/simple/",
238
- null,
239
- { params: { query, contextLength } }
240
- );
241
- return response.data;
242
- });
243
- }
244
-
245
- /**
246
- * Append content to a file
247
- */
248
- async appendContent(filepath: string, content: string): Promise<void> {
249
- validateFilePath(filepath);
250
- if (!content || typeof content !== 'string') {
251
- throw new ObsidianError('Invalid content: Content must be a non-empty string', 40003);
252
- }
253
- return this.safeRequest(async () => {
254
- logger.debug(`Appending content to file: ${filepath}`);
255
- await this.client.post(
256
- `/vault/${filepath}`,
257
- content,
258
- {
259
- headers: {
260
- "Content-Type": "text/markdown"
261
- }
262
- }
263
- );
264
- });
265
- }
266
-
267
- /**
268
- * Update the entire content of a file
269
- */
270
- async updateContent(filepath: string, content: string): Promise<void> {
271
- validateFilePath(filepath);
272
- if (!content || typeof content !== 'string') {
273
- throw new ObsidianError('Invalid content: Content must be a non-empty string', 40003);
274
- }
275
-
276
- return this.safeRequest(async () => {
277
- logger.debug(`Updating content of file: ${filepath}`);
278
- await this.client.put(
279
- `/vault/${filepath}`,
280
- content,
281
- {
282
- headers: {
283
- "Content-Type": "text/markdown"
284
- }
285
- }
286
- );
287
- });
288
- }
289
-
290
- /**
291
- * Execute a complex search using JsonLogic query
292
- */
293
- async searchJson(query: JsonLogicQuery): Promise<SearchResponse[]> {
294
- return this.safeRequest(async () => {
295
- logger.debug(`Executing JSON search with query: ${JSON.stringify(query)}`);
296
- const isTagSearch = JSON.stringify(query).includes('"contains"') &&
297
- JSON.stringify(query).includes('"#"');
298
-
299
- const response = await this.client.post(
300
- "/search/",
301
- query,
302
- {
303
- headers: {
304
- "Content-Type": "application/vnd.olrapi.jsonlogic+json",
305
- "Accept": "application/vnd.olrapi.note+json"
306
- }
307
- }
308
- );
309
-
310
- return response.data as SearchResponse[];
311
- });
312
- }
313
-
314
- /**
315
- * Get server status
316
- */
317
- async getStatus(): Promise<ObsidianStatus> {
318
- return this.safeRequest(async () => {
319
- logger.debug('Getting server status');
320
- const response = await this.client.get<ObsidianStatus>("/");
321
- return response.data;
322
- });
323
- }
324
-
325
- /**
326
- * List available commands
327
- */
328
- async listCommands(): Promise<ObsidianCommand[]> {
329
- return this.safeRequest(async () => {
330
- logger.debug('Listing commands');
331
- const response = await this.client.get<{commands: ObsidianCommand[]}>("/commands/");
332
- return response.data.commands;
333
- });
334
- }
335
-
336
- /**
337
- * Execute a command by ID
338
- */
339
- async executeCommand(commandId: string): Promise<void> {
340
- return this.safeRequest(async () => {
341
- logger.debug(`Executing command: ${commandId}`);
342
- await this.client.post(`/commands/${commandId}/`);
343
- });
344
- }
345
-
346
- /**
347
- * Open a file in Obsidian
348
- */
349
- async openFile(filepath: string, newLeaf: boolean = false): Promise<void> {
350
- validateFilePath(filepath);
351
- return this.safeRequest(async () => {
352
- logger.debug(`Opening file: ${filepath}, newLeaf: ${newLeaf}`);
353
- await this.client.post(`/open/${filepath}`, null, {
354
- params: { newLeaf }
355
- });
356
- });
357
- }
358
-
359
- /**
360
- * Get the currently active file
361
- */
362
- async getActiveFile(): Promise<NoteJson> {
363
- return this.safeRequest(async () => {
364
- logger.debug('Getting active file');
365
- const response = await this.client.get<NoteJson>("/active/", {
366
- headers: {
367
- "Accept": "application/vnd.olrapi.note+json"
368
- }
369
- });
370
- return response.data;
371
- });
372
- }
373
-
374
- /**
375
- * Update the active file
376
- */
377
- async updateActiveFile(content: string): Promise<void> {
378
- return this.safeRequest(async () => {
379
- logger.debug('Updating active file');
380
- await this.client.put("/active/", content, {
381
- headers: {
382
- "Content-Type": "text/markdown"
383
- }
384
- });
385
- });
386
- }
387
-
388
- /**
389
- * Delete the active file
390
- */
391
- async deleteActiveFile(): Promise<void> {
392
- return this.safeRequest(async () => {
393
- logger.debug('Deleting active file');
394
- await this.client.delete("/active/");
395
- });
396
- }
397
-
398
- /**
399
- * Patch the active file
400
- */
401
- async patchActiveFile(
402
- operation: "append" | "prepend" | "replace",
403
- targetType: "heading" | "block" | "frontmatter",
404
- target: string,
405
- content: string,
406
- options?: {
407
- delimiter?: string;
408
- trimWhitespace?: boolean;
409
- contentType?: "text/markdown" | "application/json";
410
- }
411
- ): Promise<void> {
412
- return this.safeRequest(async () => {
413
- logger.debug(`Patching active file: ${operation} ${targetType} "${target}"`);
414
- const headers: Record<string, string> = {
415
- "Operation": operation,
416
- "Target-Type": targetType,
417
- "Target": target,
418
- "Content-Type": options?.contentType || "text/markdown"
419
- };
420
-
421
- if (options?.delimiter) {
422
- headers["Target-Delimiter"] = options.delimiter;
423
- }
424
- if (options?.trimWhitespace !== undefined) {
425
- headers["Trim-Target-Whitespace"] = options.trimWhitespace.toString();
426
- }
427
-
428
- await this.client.patch("/active/", content, { headers });
429
- });
430
- }
431
-
432
- /**
433
- * Get a periodic note (e.g., daily, weekly)
434
- */
435
- async getPeriodicNote(period: PeriodType["type"]): Promise<NoteJson> {
436
- return this.safeRequest(async () => {
437
- logger.debug(`Getting ${period} periodic note`);
438
- const response = await this.client.get<NoteJson>(`/periodic/${period}/`, {
439
- headers: {
440
- "Accept": "application/vnd.olrapi.note+json"
441
- }
442
- });
443
- return response.data;
444
- });
445
- }
446
-
447
- /**
448
- * Update a periodic note
449
- */
450
- async updatePeriodicNote(period: PeriodType["type"], content: string): Promise<void> {
451
- return this.safeRequest(async () => {
452
- logger.debug(`Updating ${period} periodic note`);
453
- await this.client.put(`/periodic/${period}/`, content, {
454
- headers: {
455
- "Content-Type": "text/markdown"
456
- }
457
- });
458
- });
459
- }
460
-
461
- /**
462
- * Delete a periodic note
463
- */
464
- async deletePeriodicNote(period: PeriodType["type"]): Promise<void> {
465
- return this.safeRequest(async () => {
466
- logger.debug(`Deleting ${period} periodic note`);
467
- await this.client.delete(`/periodic/${period}/`);
468
- });
469
- }
470
-
471
- /**
472
- * Patch a periodic note
473
- */
474
- async patchPeriodicNote(
475
- period: PeriodType["type"],
476
- operation: "append" | "prepend" | "replace",
477
- targetType: "heading" | "block" | "frontmatter",
478
- target: string,
479
- content: string,
480
- options?: {
481
- delimiter?: string;
482
- trimWhitespace?: boolean;
483
- contentType?: "text/markdown" | "application/json";
484
- }
485
- ): Promise<void> {
486
- return this.safeRequest(async () => {
487
- logger.debug(`Patching ${period} periodic note: ${operation} ${targetType} "${target}"`);
488
- const headers: Record<string, string> = {
489
- "Operation": operation,
490
- "Target-Type": targetType,
491
- "Target": target,
492
- "Content-Type": options?.contentType || "text/markdown"
493
- };
494
-
495
- if (options?.delimiter) {
496
- headers["Target-Delimiter"] = options.delimiter;
497
- }
498
- if (options?.trimWhitespace !== undefined) {
499
- headers["Trim-Target-Whitespace"] = options.trimWhitespace.toString();
500
- }
501
-
502
- await this.client.patch(`/periodic/${period}/`, content, { headers });
503
- });
504
- }
505
- }