obsidian-mcp-server 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/build/index.js +12 -3
  2. package/build/mcp/handlers.js +138 -0
  3. package/build/mcp/index.js +7 -0
  4. package/build/mcp/server.js +131 -0
  5. package/build/mcp/types.js +7 -0
  6. package/build/{obsidian.js → obsidian/client.js} +116 -96
  7. package/build/obsidian/errors.js +75 -0
  8. package/build/obsidian/index.js +7 -0
  9. package/build/obsidian/types.js +12 -0
  10. package/build/resources/index.js +15 -0
  11. package/build/{resources.js → resources/tags.js} +31 -5
  12. package/build/resources/types.js +5 -0
  13. package/build/tools/base.js +78 -0
  14. package/build/tools/files/content.js +171 -0
  15. package/build/tools/files/index.js +22 -0
  16. package/build/tools/files/list.js +133 -0
  17. package/build/tools/index.js +31 -0
  18. package/build/tools/properties/index.js +19 -0
  19. package/build/{properties.js → tools/properties/manager.js} +40 -8
  20. package/build/{propertyTools.js → tools/properties/tools.js} +22 -6
  21. package/build/{propertyTypes.js → tools/properties/types.js} +17 -5
  22. package/build/tools/search/complex.js +203 -0
  23. package/build/tools/search/index.js +20 -0
  24. package/build/tools/search/simple.js +127 -0
  25. package/build/utils/errors.js +59 -0
  26. package/build/utils/index.js +9 -0
  27. package/build/utils/logging.js +119 -0
  28. package/build/utils/rate-limiting.js +94 -0
  29. package/build/utils/tokenization.js +62 -0
  30. package/build/utils/validation.js +88 -0
  31. package/examples/README.md +48 -0
  32. package/examples/append-content.md +63 -0
  33. package/examples/complex-search.md +117 -0
  34. package/examples/find-in-file.md +94 -0
  35. package/examples/get-file-contents.md +72 -0
  36. package/examples/get-properties.md +89 -0
  37. package/examples/list-files-in-dir.md +55 -0
  38. package/examples/list-files-in-vault.md +53 -0
  39. package/examples/patch-content.md +60 -0
  40. package/examples/update-properties.md +126 -0
  41. package/package.json +1 -1
  42. package/src/index.ts +13 -3
  43. package/src/mcp/handlers.ts +183 -0
  44. package/src/mcp/index.ts +6 -0
  45. package/src/mcp/server.ts +162 -0
  46. package/src/mcp/types.ts +46 -0
  47. package/src/{obsidian.ts → obsidian/client.ts} +132 -125
  48. package/src/obsidian/errors.ts +105 -0
  49. package/src/obsidian/index.ts +6 -0
  50. package/src/obsidian/types.ts +124 -0
  51. package/src/resources/index.ts +17 -0
  52. package/src/{resources.ts → resources/tags.ts} +38 -6
  53. package/src/resources/types.ts +30 -0
  54. package/src/tools/base.ts +112 -0
  55. package/src/tools/files/content.ts +206 -0
  56. package/src/tools/files/index.ts +31 -0
  57. package/src/tools/files/list.ts +150 -0
  58. package/src/tools/index.ts +38 -0
  59. package/src/tools/properties/index.ts +21 -0
  60. package/src/{properties.ts → tools/properties/manager.ts} +41 -8
  61. package/src/{propertyTools.ts → tools/properties/tools.ts} +33 -7
  62. package/src/{propertyTypes.ts → tools/properties/types.ts} +29 -5
  63. package/src/tools/search/complex.ts +231 -0
  64. package/src/tools/search/index.ts +22 -0
  65. package/src/tools/search/simple.ts +147 -0
  66. package/src/utils/errors.ts +69 -0
  67. package/src/utils/index.ts +8 -0
  68. package/src/utils/logging.ts +146 -0
  69. package/src/utils/rate-limiting.ts +114 -0
  70. package/src/utils/tokenization.ts +71 -0
  71. package/src/utils/validation.ts +95 -0
  72. package/build/server.js +0 -263
  73. package/build/tools.js +0 -845
  74. package/build/types.js +0 -37
  75. package/src/server.ts +0 -335
  76. package/src/tools.ts +0 -926
  77. package/src/types.ts +0 -184
@@ -0,0 +1,53 @@
1
+ # List Files in Vault Tool Example
2
+
3
+ ## Request
4
+ ```json
5
+ {
6
+ "type": "object",
7
+ "properties": {}
8
+ }
9
+ ```
10
+
11
+ ## Response
12
+ ```
13
+ Areas/
14
+ Areas/Work/
15
+ Areas/Personal/
16
+ Areas/Projects/
17
+ Resources/
18
+ Resources/Templates/
19
+ Resources/Attachments/
20
+ Daily Notes/
21
+ Daily Notes/2024-01-25.md
22
+ Projects/
23
+ Projects/Project Alpha/
24
+ Projects/Project Beta/
25
+ Archive/
26
+ README.md
27
+ index.md
28
+ ```
29
+
30
+ ## Example Use Cases
31
+
32
+ 1. **Vault Organization**
33
+ - Get a complete overview of your vault structure
34
+ - Identify main areas and categories
35
+ - Plan content organization
36
+
37
+ 2. **Content Management**
38
+ - Find important directories and files
39
+ - Review vault hierarchy
40
+ - Locate specific resources
41
+
42
+ 3. **Navigation**
43
+ - Quick access to key areas
44
+ - Browse through project directories
45
+ - Find templates and resources
46
+
47
+ ## Notes
48
+ - The tool returns both files and directories at the root level
49
+ - Directory paths end with a forward slash (/)
50
+ - File extensions are preserved in the output
51
+ - Results are alphabetically sorted
52
+ - Special characters in filenames are preserved
53
+ - Provides a hierarchical view of your vault structure
@@ -0,0 +1,60 @@
1
+ # Patch Content Tool Example
2
+
3
+ ## Request 1 (Update Existing Note)
4
+ ```json
5
+ {
6
+ "filepath": "Projects/Project Alpha/Documentation/Architecture.md",
7
+ "content": "---\ntitle: System Architecture\ntags: [architecture, documentation]\nstatus: [complete]\nmodified: 2024-01-25\n---\n\n# System Architecture\n\n## Overview\nThis document describes the high-level architecture of Project Alpha.\n\n## Components\n\n### Frontend\n- React-based web application\n- Material UI components\n- Redux state management\n\n### Backend\n- Node.js REST API\n- PostgreSQL database\n- Redis caching layer\n\n### Infrastructure\n- AWS cloud hosting\n- Docker containerization\n- CI/CD pipeline\n\n## System Diagram\n![[architecture-diagram.png]]\n\n## Integration Points\n- Authentication service\n- Payment processing\n- Email service\n- Analytics platform\n\n#project-alpha #architecture #technical"
8
+ }
9
+ ```
10
+
11
+ ## Response 1
12
+ ```json
13
+ {
14
+ "success": true,
15
+ "message": "File content updated successfully"
16
+ }
17
+ ```
18
+
19
+ ## Request 2 (Create New Note)
20
+ ```json
21
+ {
22
+ "filepath": "Templates/Meeting Note.md",
23
+ "content": "---\ntitle: {{title}}\ndate: {{date}}\nattendees: \ntags: [meeting]\nstatus: [draft]\n---\n\n# {{title}}\n\n## Agenda\n1. \n2. \n3. \n\n## Discussion Points\n- \n\n## Decisions\n- \n\n## Action Items\n- [ ] \n- [ ] \n- [ ] \n\n## Next Steps\n- \n\n## Notes\n- "
24
+ }
25
+ ```
26
+
27
+ ## Response 2
28
+ ```json
29
+ {
30
+ "success": true,
31
+ "message": "File created successfully"
32
+ }
33
+ ```
34
+
35
+ ## Example Use Cases
36
+
37
+ 1. **Document Updates**
38
+ - Complete revision of documentation
39
+ - Update outdated content
40
+ - Replace temporary content with final version
41
+
42
+ 2. **Template Creation**
43
+ - Create note templates
44
+ - Define standardized formats
45
+ - Set up reusable structures
46
+
47
+ 3. **Content Migration**
48
+ - Move and reformat content
49
+ - Update note structure
50
+ - Implement new organization systems
51
+
52
+ ## Notes
53
+ - Replaces entire file content or creates new file
54
+ - Supports YAML frontmatter
55
+ - Handles template variables
56
+ - Preserves markdown formatting
57
+ - Creates parent directories if needed
58
+ - Validates content structure
59
+ - File path is relative to vault root
60
+ - Useful for major content revisions
@@ -0,0 +1,126 @@
1
+ # Update Properties Tool Example
2
+
3
+ ## Request 1 (Update Multiple Properties)
4
+ ```json
5
+ {
6
+ "filepath": "Projects/Project Alpha/Documentation/Requirements.md",
7
+ "properties": {
8
+ "status": ["in-review"],
9
+ "tags": ["project", "requirements", "documentation", "technical"],
10
+ "modified": "2024-01-25",
11
+ "reviewers": ["Tech Lead", "Product Manager", "Security Team"],
12
+ "version": "1.1"
13
+ }
14
+ }
15
+ ```
16
+
17
+ ## Response 1
18
+ ```json
19
+ {
20
+ "success": true,
21
+ "message": "Properties updated successfully"
22
+ }
23
+ ```
24
+
25
+ ## Request 2 (Merge Array Properties)
26
+ ```json
27
+ {
28
+ "filepath": "Projects/Project Beta/Specifications.md",
29
+ "properties": {
30
+ "tags": ["api", "backend"],
31
+ "status": ["draft"]
32
+ },
33
+ "replace": false
34
+ }
35
+ ```
36
+
37
+ ## Response 2
38
+ ```json
39
+ {
40
+ "success": true,
41
+ "message": "Properties merged successfully"
42
+ }
43
+ ```
44
+
45
+ ## Example Use Cases
46
+
47
+ 1. **Document Management**
48
+ - Update document status
49
+ - Add or remove tags
50
+ - Track document versions
51
+ - Manage review process
52
+
53
+ 2. **Project Tracking**
54
+ - Update task status
55
+ - Assign reviewers
56
+ - Track modifications
57
+ - Version control
58
+
59
+ 3. **Content Organization**
60
+ - Categorize content
61
+ - Update metadata
62
+ - Manage document lifecycle
63
+ - Track document history
64
+
65
+ ## Notes
66
+ - Updates YAML frontmatter properties
67
+ - Supports property merging or replacement
68
+ - Handles various property types:
69
+ * Strings
70
+ * Arrays
71
+ * Dates
72
+ * Numbers
73
+ * Boolean values
74
+ - Preserves existing properties not specified in update
75
+ - Automatically updates modified timestamp
76
+ - Creates properties if they don't exist
77
+
78
+ ## Property Handling
79
+
80
+ 1. **Array Merging (replace: false)**
81
+ ```yaml
82
+ # Original
83
+ tags: [project, draft]
84
+ status: [review]
85
+
86
+ # Update
87
+ tags: [technical, draft]
88
+ status: [complete]
89
+
90
+ # Result
91
+ tags: [project, draft, technical]
92
+ status: [review, complete]
93
+ ```
94
+
95
+ 2. **Array Replacement (replace: true)**
96
+ ```yaml
97
+ # Original
98
+ reviewers: [Alice, Bob]
99
+ tags: [draft]
100
+
101
+ # Update (replace: true)
102
+ reviewers: [Charlie]
103
+ tags: [final]
104
+
105
+ # Result
106
+ reviewers: [Charlie]
107
+ tags: [final]
108
+ ```
109
+
110
+ 3. **Property Types**
111
+ ```yaml
112
+ # String properties
113
+ title: "Updated Title"
114
+ author: "New Author"
115
+
116
+ # Date properties
117
+ modified: 2024-01-25
118
+ due: 2024-02-01
119
+
120
+ # Number properties
121
+ version: 2.0
122
+ priority: 1
123
+
124
+ # Boolean properties
125
+ published: true
126
+ confidential: false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "obsidian-mcp-server",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Model Context Protocol server for Obsidian integration with token-aware response handling",
5
5
  "main": "build/index.js",
6
6
  "type": "module",
package/src/index.ts CHANGED
@@ -1,12 +1,22 @@
1
1
  #!/usr/bin/env node
2
- import { run } from "./server.js";
2
+ import { run } from "./mcp/server.js";
3
+ import { createLogger } from "./utils/logging.js";
4
+
5
+ // Create a logger for the main entry point
6
+ const logger = createLogger('Main');
3
7
 
4
8
  // Main entry point
5
9
  if (import.meta.url === `file://${process.argv[1]}`) {
10
+ logger.info("Starting Obsidian MCP Server");
6
11
  run().catch((error) => {
7
- console.error("Failed to start server:", error);
12
+ logger.error("Failed to start server:", error);
8
13
  process.exit(1);
9
14
  });
10
15
  }
11
16
 
12
- export { run };
17
+ // Export the run function and other important modules
18
+ export { run } from "./mcp/server.js";
19
+ export * from "./obsidian/index.js";
20
+ export * from "./tools/index.js";
21
+ export * from "./resources/index.js";
22
+ export * from "./utils/index.js";
@@ -0,0 +1,183 @@
1
+ /**
2
+ * MCP server request handlers
3
+ */
4
+ import {
5
+ Tool,
6
+ TextContent,
7
+ ListToolsRequestSchema,
8
+ CallToolRequestSchema,
9
+ ListResourcesRequestSchema,
10
+ ReadResourceRequestSchema
11
+ } from "@modelcontextprotocol/sdk/types.js";
12
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
13
+ import { ObsidianError } from "../utils/errors.js";
14
+ import { validateToolArguments } from "../utils/validation.js";
15
+ import { rateLimiter } from "../utils/rate-limiting.js";
16
+ import { createLogger } from "../utils/logging.js";
17
+ import { DEFAULT_TIMEOUT_CONFIG } from "./types.js";
18
+ import { BaseToolHandler } from "../tools/base.js";
19
+
20
+ // Create a logger for request handlers
21
+ const logger = createLogger('McpHandlers');
22
+
23
+ /**
24
+ * Set up tool listing handler
25
+ * @param server The MCP server instance
26
+ * @param toolHandlers The tool handlers to register
27
+ */
28
+ export function setupToolListingHandler(
29
+ server: Server,
30
+ toolHandlers: Map<string, BaseToolHandler<any>>
31
+ ): void {
32
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
33
+ logger.debug('Handling ListToolsRequest');
34
+ const tools: Tool[] = [];
35
+ for (const handler of toolHandlers.values()) {
36
+ tools.push(handler.getToolDescription());
37
+ }
38
+ return { tools };
39
+ });
40
+ }
41
+
42
+ /**
43
+ * Set up tool calling handler
44
+ * @param server The MCP server instance
45
+ * @param toolHandlers The tool handlers to register
46
+ */
47
+ export function setupToolCallingHandler(
48
+ server: Server,
49
+ toolHandlers: Map<string, BaseToolHandler<any>>
50
+ ): void {
51
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
52
+ const { name, arguments: args } = request.params;
53
+ logger.debug(`Handling CallToolRequest for tool: ${name}`);
54
+
55
+ const handler = toolHandlers.get(name);
56
+ if (!handler) {
57
+ logger.error(`Unknown tool requested: ${name}`);
58
+ throw new ObsidianError(`Unknown tool: ${name}`, 40400); // 40400 = Not found
59
+ }
60
+
61
+ // Check rate limit
62
+ try {
63
+ rateLimiter.enforceRateLimit(name);
64
+ } catch (error) {
65
+ // If rate limit is exceeded, log and rethrow the error
66
+ logger.warn(`Rate limit exceeded for tool: ${name}`);
67
+ throw error;
68
+ }
69
+
70
+ // Add timeout handling
71
+ const timeoutMs = DEFAULT_TIMEOUT_CONFIG.toolExecutionMs;
72
+ const timeoutPromise = new Promise<never>((_, reject) => {
73
+ setTimeout(() => {
74
+ reject(new ObsidianError(`Tool execution timed out after ${timeoutMs}ms`, 40800)); // 40800 = Request timeout
75
+ }, timeoutMs);
76
+ });
77
+
78
+ try {
79
+ // Validate arguments against tool's schema
80
+ const toolDescription = handler.getToolDescription();
81
+ const validationResult = validateToolArguments(args, toolDescription.inputSchema);
82
+ if (!validationResult.valid) {
83
+ logger.error(`Invalid tool arguments for ${name}:`, validationResult.errors);
84
+ throw new ObsidianError(
85
+ `Invalid tool arguments: ${validationResult.errors.join(', ')}`,
86
+ 40000 // 40000 = Bad request
87
+ );
88
+ }
89
+
90
+ // Log the tool execution
91
+ logger.info(`Executing tool: ${name}`);
92
+
93
+ // Race between tool execution and timeout
94
+ const content = await Promise.race([
95
+ handler.runTool(args),
96
+ timeoutPromise
97
+ ]);
98
+
99
+ logger.debug(`Tool execution completed successfully: ${name}`);
100
+
101
+ return { content };
102
+ } catch (error) {
103
+ if (error instanceof ObsidianError) {
104
+ // Check if the operation actually succeeded despite the error
105
+ if (error.errorCode === 20400) { // 20400 = Success with no content
106
+ return {
107
+ content: [{
108
+ type: "text",
109
+ text: "Operation completed successfully"
110
+ }]
111
+ };
112
+ }
113
+ throw error;
114
+ }
115
+
116
+ // Enhanced error logging
117
+ logger.error("Tool execution error:", {
118
+ name: error instanceof Error ? error.name : 'Unknown',
119
+ message: error instanceof Error ? error.message : String(error),
120
+ stack: error instanceof Error ? error.stack : undefined,
121
+ toolName: name
122
+ });
123
+
124
+ if (error instanceof Error) {
125
+ throw new ObsidianError(
126
+ `Tool '${name}' execution failed: ${error.message}`,
127
+ 50000, // 50000 = Internal server error
128
+ { originalError: error.stack }
129
+ );
130
+ }
131
+
132
+ throw new ObsidianError(
133
+ "Tool execution failed with unknown error",
134
+ 50000, // 50000 = Internal server error
135
+ { error }
136
+ );
137
+ }
138
+ });
139
+ }
140
+
141
+ /**
142
+ * Set up resource listing handler
143
+ * @param server The MCP server instance
144
+ * @param resources The resources to register
145
+ */
146
+ export function setupResourceListingHandler(
147
+ server: Server,
148
+ resources: Record<string, any>
149
+ ): void {
150
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
151
+ logger.debug('Handling ListResourcesRequest');
152
+ return {
153
+ resources: Object.values(resources).map(resource =>
154
+ resource.getResourceDescription())
155
+ };
156
+ });
157
+ }
158
+
159
+ /**
160
+ * Set up resource reading handler
161
+ * @param server The MCP server instance
162
+ * @param resources The resources to register
163
+ */
164
+ export function setupResourceReadingHandler(
165
+ server: Server,
166
+ resources: Record<string, any>
167
+ ): void {
168
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
169
+ const uri = request.params.uri;
170
+ logger.debug(`Handling ReadResourceRequest for URI: ${uri}`);
171
+
172
+ const resource = resources[uri];
173
+ if (resource) {
174
+ logger.debug(`Found resource for URI: ${uri}`);
175
+ return {
176
+ contents: await resource.getContent()
177
+ };
178
+ }
179
+
180
+ logger.error(`Resource not found: ${uri}`);
181
+ throw new ObsidianError(`Resource not found: ${uri}`, 40400); // 40400 = Not found
182
+ });
183
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * MCP module exports
3
+ */
4
+ export * from './types.js';
5
+ export * from './handlers.js';
6
+ export * from './server.js';
@@ -0,0 +1,162 @@
1
+ /**
2
+ * MCP server implementation
3
+ */
4
+ import { config } from "dotenv";
5
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
+ import { createLogger } from "../utils/logging.js";
8
+ import { rateLimiter } from "../utils/rate-limiting.js";
9
+ import { createTagResource } from "../resources/index.js";
10
+ import { createToolHandlers, createToolHandlerMap } from "../tools/index.js";
11
+ import { ObsidianClient } from "../obsidian/client.js";
12
+ import {
13
+ setupToolListingHandler,
14
+ setupToolCallingHandler,
15
+ setupResourceListingHandler,
16
+ setupResourceReadingHandler
17
+ } from "./handlers.js";
18
+ import { McpServerConfig, ResourceMap } from "./types.js";
19
+
20
+ // Create a logger for the server
21
+ const logger = createLogger('McpServer');
22
+
23
+ // Load environment variables
24
+ config();
25
+
26
+ /**
27
+ * Initialize MCP server components
28
+ */
29
+ export async function initializeServer(): Promise<Server> {
30
+ // Verify API key exists
31
+ const API_KEY = process.env.OBSIDIAN_API_KEY;
32
+ if (!API_KEY) {
33
+ throw new Error("OBSIDIAN_API_KEY environment variable is required");
34
+ }
35
+
36
+ // Initialize Obsidian client with environment configuration
37
+ logger.info('Initializing Obsidian client');
38
+ const client = new ObsidianClient({
39
+ apiKey: API_KEY,
40
+ verifySSL: process.env.VERIFY_SSL === 'true',
41
+ timeout: parseInt(process.env.REQUEST_TIMEOUT || '5000'),
42
+ maxContentLength: parseInt(process.env.MAX_CONTENT_LENGTH || String(50 * 1024 * 1024)),
43
+ maxBodyLength: parseInt(process.env.MAX_BODY_LENGTH || String(50 * 1024 * 1024))
44
+ });
45
+
46
+ // Initialize tool handlers
47
+ logger.info('Initializing tool handlers');
48
+ const toolHandlers = createToolHandlers(client);
49
+ const toolHandlerMap = createToolHandlerMap(toolHandlers);
50
+
51
+ // Initialize resources
52
+ logger.info('Initializing resources');
53
+ const tagResource = createTagResource(client);
54
+ const resources: ResourceMap = {
55
+ [tagResource.getResourceDescription().uri]: tagResource
56
+ };
57
+
58
+ // Create MCP server
59
+ const serverConfig: McpServerConfig = {
60
+ name: "obsidian-mcp-server",
61
+ version: process.env.npm_package_version ?? "1.1.0" // Use version from package.json
62
+ };
63
+
64
+ logger.info(`Creating MCP server: ${serverConfig.name} v${serverConfig.version}`);
65
+ const server = new Server(
66
+ serverConfig,
67
+ {
68
+ capabilities: {
69
+ tools: {},
70
+ resources
71
+ }
72
+ }
73
+ );
74
+
75
+ // Set up request handlers
76
+ setupToolListingHandler(server, toolHandlerMap);
77
+ setupToolCallingHandler(server, toolHandlerMap);
78
+ setupResourceListingHandler(server, resources);
79
+ setupResourceReadingHandler(server, resources);
80
+
81
+ // Set up error handler
82
+ server.onerror = (error) => {
83
+ logger.error("[MCP Error]", error);
84
+ };
85
+
86
+ return server;
87
+ }
88
+
89
+ /**
90
+ * Set up graceful shutdown handling
91
+ * @param server The MCP server instance
92
+ * @param cleanupHandlers Additional cleanup handlers to run on shutdown
93
+ */
94
+ export function setupShutdownHandling(
95
+ server: Server,
96
+ cleanupHandlers: (() => Promise<void> | void)[] = []
97
+ ): void {
98
+ // Cleanup function for graceful shutdown
99
+ const cleanup = async () => {
100
+ logger.info('Shutting down server...');
101
+
102
+ // Run cleanup handlers
103
+ for (const handler of cleanupHandlers) {
104
+ try {
105
+ await handler();
106
+ } catch (error) {
107
+ logger.error('Error during cleanup:', error);
108
+ }
109
+ }
110
+
111
+ // Dispose rate limiter
112
+ rateLimiter.dispose();
113
+
114
+ // Close server
115
+ await server.close();
116
+
117
+ process.exit(0);
118
+ };
119
+
120
+ // Handle various termination signals
121
+ process.on('SIGINT', cleanup); // Ctrl+C on all platforms
122
+ process.on('SIGTERM', cleanup); // Termination request
123
+
124
+ if (process.platform === 'win32') {
125
+ // Windows-specific handling
126
+ process.on('SIGHUP', cleanup); // Terminal closed
127
+ } else {
128
+ // Unix-specific signals
129
+ process.on('SIGUSR1', cleanup);
130
+ process.on('SIGUSR2', cleanup);
131
+ }
132
+
133
+ // Handle uncaught errors
134
+ process.on('uncaughtException', async (error) => {
135
+ logger.error('Uncaught exception:', error);
136
+ await cleanup();
137
+ });
138
+
139
+ process.on('unhandledRejection', async (error) => {
140
+ logger.error('Unhandled rejection:', error);
141
+ await cleanup();
142
+ });
143
+ }
144
+
145
+ /**
146
+ * Run the MCP server
147
+ */
148
+ export async function run(): Promise<void> {
149
+ try {
150
+ const server = await initializeServer();
151
+ setupShutdownHandling(server, []);
152
+
153
+ // Connect to transport
154
+ const transport = new StdioServerTransport();
155
+ await server.connect(transport);
156
+
157
+ logger.info("Obsidian MCP server running on stdio");
158
+ } catch (error) {
159
+ logger.error("Failed to start server:", error);
160
+ process.exit(1);
161
+ }
162
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * MCP server type definitions
3
+ */
4
+ import { TextContent, Tool } from "@modelcontextprotocol/sdk/types.js";
5
+ import { BaseToolHandler } from "../tools/base.js";
6
+ import { TagResource } from "../resources/tags.js";
7
+
8
+ /**
9
+ * MCP server configuration
10
+ */
11
+ export interface McpServerConfig extends Record<string, unknown> {
12
+ name: string;
13
+ version: string;
14
+ }
15
+
16
+ /**
17
+ * Timeout configuration for tool execution
18
+ */
19
+ export interface TimeoutConfig {
20
+ toolExecutionMs: number; // Default: 60000 (60 seconds)
21
+ }
22
+
23
+ /**
24
+ * Map of resources by URI
25
+ */
26
+ export interface ResourceMap {
27
+ [uri: string]: {
28
+ getContent(): Promise<TextContent[]>;
29
+ getResourceDescription(): any;
30
+ };
31
+ }
32
+
33
+ /**
34
+ * Server capabilities configuration
35
+ */
36
+ export interface ServerCapabilities {
37
+ tools: Record<string, Tool>;
38
+ resources: ResourceMap;
39
+ }
40
+
41
+ /**
42
+ * Default timeout configuration
43
+ */
44
+ export const DEFAULT_TIMEOUT_CONFIG: TimeoutConfig = {
45
+ toolExecutionMs: parseInt(process.env.TOOL_TIMEOUT_MS ?? '60000') // 60 second default timeout
46
+ };