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.
- package/README.md +248 -104
- package/dist/config/index.d.ts +41 -0
- package/dist/config/index.js +191 -0
- package/dist/index.d.ts +1 -5
- package/dist/index.js +296 -18
- package/dist/mcp-server/server.d.ts +33 -0
- package/dist/mcp-server/server.js +211 -0
- package/dist/mcp-server/tools/obsidianDeleteFileTool/index.d.ts +12 -0
- package/dist/mcp-server/tools/obsidianDeleteFileTool/index.js +12 -0
- package/dist/mcp-server/tools/obsidianDeleteFileTool/logic.d.ts +51 -0
- package/dist/mcp-server/tools/obsidianDeleteFileTool/logic.js +168 -0
- package/dist/mcp-server/tools/obsidianDeleteFileTool/registration.d.ts +19 -0
- package/dist/mcp-server/tools/obsidianDeleteFileTool/registration.js +91 -0
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/index.d.ts +12 -0
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/index.js +12 -0
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/logic.d.ts +77 -0
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/logic.js +341 -0
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/registration.d.ts +18 -0
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/registration.js +69 -0
- package/dist/mcp-server/tools/obsidianListFilesTool/index.d.ts +12 -0
- package/dist/mcp-server/tools/obsidianListFilesTool/index.js +12 -0
- package/dist/mcp-server/tools/obsidianListFilesTool/logic.d.ts +64 -0
- package/dist/mcp-server/tools/obsidianListFilesTool/logic.js +179 -0
- package/dist/mcp-server/tools/obsidianListFilesTool/registration.d.ts +19 -0
- package/dist/mcp-server/tools/obsidianListFilesTool/registration.js +96 -0
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/index.d.ts +3 -0
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/index.js +2 -0
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/logic.d.ts +42 -0
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/logic.js +152 -0
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/registration.d.ts +3 -0
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/registration.js +52 -0
- package/dist/mcp-server/tools/obsidianManageTagsTool/index.d.ts +3 -0
- package/dist/mcp-server/tools/obsidianManageTagsTool/index.js +2 -0
- package/dist/mcp-server/tools/obsidianManageTagsTool/logic.d.ts +28 -0
- package/dist/mcp-server/tools/obsidianManageTagsTool/logic.js +161 -0
- package/dist/mcp-server/tools/obsidianManageTagsTool/registration.d.ts +3 -0
- package/dist/mcp-server/tools/obsidianManageTagsTool/registration.js +52 -0
- package/dist/mcp-server/tools/obsidianReadFileTool/index.d.ts +12 -0
- package/dist/mcp-server/tools/obsidianReadFileTool/index.js +12 -0
- package/dist/mcp-server/tools/obsidianReadFileTool/logic.d.ts +87 -0
- package/dist/mcp-server/tools/obsidianReadFileTool/logic.js +216 -0
- package/dist/mcp-server/tools/obsidianReadFileTool/registration.d.ts +20 -0
- package/dist/mcp-server/tools/obsidianReadFileTool/registration.js +101 -0
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/index.d.ts +12 -0
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/index.js +12 -0
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/logic.d.ts +255 -0
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/logic.js +583 -0
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/registration.d.ts +22 -0
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/registration.js +111 -0
- package/dist/mcp-server/tools/obsidianUpdateFileTool/index.d.ts +12 -0
- package/dist/mcp-server/tools/obsidianUpdateFileTool/index.js +12 -0
- package/dist/mcp-server/tools/obsidianUpdateFileTool/logic.d.ts +183 -0
- package/dist/mcp-server/tools/obsidianUpdateFileTool/logic.js +490 -0
- package/dist/mcp-server/tools/obsidianUpdateFileTool/registration.d.ts +21 -0
- package/dist/mcp-server/tools/obsidianUpdateFileTool/registration.js +108 -0
- package/dist/mcp-server/transports/authentication/authContext.d.ts +33 -0
- package/dist/mcp-server/transports/authentication/authContext.js +24 -0
- package/dist/mcp-server/transports/authentication/authMiddleware.d.ts +30 -0
- package/dist/mcp-server/transports/authentication/authMiddleware.js +145 -0
- package/dist/mcp-server/transports/authentication/authUtils.d.ts +18 -0
- package/dist/mcp-server/transports/authentication/authUtils.js +45 -0
- package/dist/mcp-server/transports/authentication/oauthMiddleware.d.ts +24 -0
- package/dist/mcp-server/transports/authentication/oauthMiddleware.js +109 -0
- package/dist/mcp-server/transports/authentication/types.d.ts +17 -0
- package/dist/mcp-server/transports/authentication/types.js +5 -0
- package/dist/mcp-server/transports/httpTransport.d.ts +24 -0
- package/dist/mcp-server/transports/httpTransport.js +496 -0
- package/dist/mcp-server/transports/stdioTransport.d.ts +42 -0
- package/dist/mcp-server/transports/stdioTransport.js +63 -0
- package/dist/services/obsidianRestAPI/index.d.ts +15 -0
- package/dist/services/obsidianRestAPI/index.js +17 -0
- package/dist/services/obsidianRestAPI/methods/activeFileMethods.d.ts +38 -0
- package/dist/services/obsidianRestAPI/methods/activeFileMethods.js +62 -0
- package/dist/services/obsidianRestAPI/methods/commandMethods.d.ts +22 -0
- package/dist/services/obsidianRestAPI/methods/commandMethods.js +31 -0
- package/dist/services/obsidianRestAPI/methods/openMethods.d.ts +16 -0
- package/dist/services/obsidianRestAPI/methods/openMethods.js +21 -0
- package/dist/services/obsidianRestAPI/methods/patchMethods.d.ts +37 -0
- package/dist/services/obsidianRestAPI/methods/patchMethods.js +94 -0
- package/dist/services/obsidianRestAPI/methods/periodicNoteMethods.d.ts +42 -0
- package/dist/services/obsidianRestAPI/methods/periodicNoteMethods.js +66 -0
- package/dist/services/obsidianRestAPI/methods/searchMethods.d.ts +25 -0
- package/dist/services/obsidianRestAPI/methods/searchMethods.js +36 -0
- package/dist/services/obsidianRestAPI/methods/vaultMethods.d.ts +58 -0
- package/dist/services/obsidianRestAPI/methods/vaultMethods.js +144 -0
- package/dist/services/obsidianRestAPI/service.d.ts +195 -0
- package/dist/services/obsidianRestAPI/service.js +379 -0
- package/dist/services/obsidianRestAPI/types.d.ts +127 -0
- package/dist/services/obsidianRestAPI/types.js +7 -0
- package/dist/services/obsidianRestAPI/vaultCache/index.d.ts +4 -0
- package/dist/services/obsidianRestAPI/vaultCache/index.js +4 -0
- package/dist/services/obsidianRestAPI/vaultCache/service.d.ts +88 -0
- package/dist/services/obsidianRestAPI/vaultCache/service.js +299 -0
- package/dist/types-global/errors.d.ts +73 -0
- package/dist/types-global/errors.js +71 -0
- package/dist/utils/index.d.ts +5 -8
- package/dist/utils/index.js +13 -9
- package/dist/utils/internal/asyncUtils.d.ts +54 -0
- package/dist/utils/internal/asyncUtils.js +101 -0
- package/dist/utils/internal/errorHandler.d.ts +176 -0
- package/dist/utils/internal/errorHandler.js +351 -0
- package/dist/utils/internal/index.d.ts +4 -0
- package/dist/utils/internal/index.js +4 -0
- package/dist/utils/internal/logger.d.ts +141 -0
- package/dist/utils/internal/logger.js +406 -0
- package/dist/utils/internal/requestContext.d.ts +83 -0
- package/dist/utils/internal/requestContext.js +72 -0
- package/dist/utils/metrics/index.d.ts +1 -0
- package/dist/utils/metrics/index.js +1 -0
- package/dist/utils/metrics/tokenCounter.d.ts +27 -0
- package/dist/utils/metrics/tokenCounter.js +128 -0
- package/dist/utils/obsidian/index.d.ts +5 -0
- package/dist/utils/obsidian/index.js +5 -0
- package/dist/utils/obsidian/obsidianApiUtils.d.ts +14 -0
- package/dist/utils/obsidian/obsidianApiUtils.js +29 -0
- package/dist/utils/obsidian/obsidianStatUtils.d.ts +68 -0
- package/dist/utils/obsidian/obsidianStatUtils.js +143 -0
- package/dist/utils/parsing/dateParser.d.ts +56 -0
- package/dist/utils/parsing/dateParser.js +104 -0
- package/dist/utils/parsing/index.d.ts +2 -0
- package/dist/utils/parsing/index.js +3 -0
- package/dist/utils/parsing/jsonParser.d.ts +80 -0
- package/dist/utils/parsing/jsonParser.js +133 -0
- package/dist/utils/security/idGenerator.d.ts +140 -0
- package/dist/utils/security/idGenerator.js +194 -0
- package/dist/utils/security/index.d.ts +3 -0
- package/dist/utils/security/index.js +3 -0
- package/dist/utils/security/rateLimiter.d.ts +156 -0
- package/dist/utils/security/rateLimiter.js +235 -0
- package/dist/utils/security/sanitization.d.ts +244 -0
- package/dist/utils/security/sanitization.js +599 -0
- package/package.json +58 -37
- package/dist/index.js.map +0 -1
- package/dist/mcp/handlers.d.ts +0 -29
- package/dist/mcp/handlers.js +0 -305
- package/dist/mcp/handlers.js.map +0 -1
- package/dist/mcp/index.d.ts +0 -6
- package/dist/mcp/index.js +0 -7
- package/dist/mcp/index.js.map +0 -1
- package/dist/mcp/server.d.ts +0 -18
- package/dist/mcp/server.js +0 -240
- package/dist/mcp/server.js.map +0 -1
- package/dist/mcp/types.d.ts +0 -70
- package/dist/mcp/types.js +0 -49
- package/dist/mcp/types.js.map +0 -1
- package/dist/obsidian/client.d.ts +0 -109
- package/dist/obsidian/client.js +0 -403
- package/dist/obsidian/client.js.map +0 -1
- package/dist/obsidian/errors.d.ts +0 -28
- package/dist/obsidian/errors.js +0 -75
- package/dist/obsidian/errors.js.map +0 -1
- package/dist/obsidian/index.d.ts +0 -6
- package/dist/obsidian/index.js +0 -7
- package/dist/obsidian/index.js.map +0 -1
- package/dist/obsidian/types.d.ts +0 -107
- package/dist/obsidian/types.js +0 -12
- package/dist/obsidian/types.js.map +0 -1
- package/dist/resources/index.d.ts +0 -13
- package/dist/resources/index.js +0 -15
- package/dist/resources/index.js.map +0 -1
- package/dist/resources/tags.d.ts +0 -39
- package/dist/resources/tags.js +0 -257
- package/dist/resources/tags.js.map +0 -1
- package/dist/resources/types.d.ts +0 -27
- package/dist/resources/types.js +0 -5
- package/dist/resources/types.js.map +0 -1
- package/dist/tools/base.d.ts +0 -46
- package/dist/tools/base.js +0 -88
- package/dist/tools/base.js.map +0 -1
- package/dist/tools/files/content.d.ts +0 -58
- package/dist/tools/files/content.js +0 -171
- package/dist/tools/files/content.js.map +0 -1
- package/dist/tools/files/index.d.ts +0 -14
- package/dist/tools/files/index.js +0 -22
- package/dist/tools/files/index.js.map +0 -1
- package/dist/tools/files/list.d.ts +0 -35
- package/dist/tools/files/list.js +0 -133
- package/dist/tools/files/list.js.map +0 -1
- package/dist/tools/index.d.ts +0 -21
- package/dist/tools/index.js +0 -31
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/properties/index.d.ts +0 -14
- package/dist/tools/properties/index.js +0 -19
- package/dist/tools/properties/index.js.map +0 -1
- package/dist/tools/properties/manager.d.ts +0 -62
- package/dist/tools/properties/manager.js +0 -302
- package/dist/tools/properties/manager.js.map +0 -1
- package/dist/tools/properties/tools.d.ts +0 -47
- package/dist/tools/properties/tools.js +0 -239
- package/dist/tools/properties/tools.js.map +0 -1
- package/dist/tools/properties/types.d.ts +0 -141
- package/dist/tools/properties/types.js +0 -70
- package/dist/tools/properties/types.js.map +0 -1
- package/dist/tools/search/complex.d.ts +0 -38
- package/dist/tools/search/complex.js +0 -270
- package/dist/tools/search/complex.js.map +0 -1
- package/dist/tools/search/index.d.ts +0 -14
- package/dist/tools/search/index.js +0 -20
- package/dist/tools/search/index.js.map +0 -1
- package/dist/tools/search/simple.d.ts +0 -25
- package/dist/tools/search/simple.js +0 -127
- package/dist/tools/search/simple.js.map +0 -1
- package/dist/utils/errors.d.ts +0 -24
- package/dist/utils/errors.js +0 -59
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/idGenerator.d.ts +0 -15
- package/dist/utils/idGenerator.js +0 -21
- package/dist/utils/idGenerator.js.map +0 -1
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/logging.d.ts +0 -245
- package/dist/utils/logging.js +0 -417
- package/dist/utils/logging.js.map +0 -1
- package/dist/utils/rate-limiting.d.ts +0 -50
- package/dist/utils/rate-limiting.js +0 -94
- package/dist/utils/rate-limiting.js.map +0 -1
- package/dist/utils/tokenization.d.ts +0 -28
- package/dist/utils/tokenization.js +0 -75
- package/dist/utils/tokenization.js.map +0 -1
- package/dist/utils/validation.d.ts +0 -22
- package/dist/utils/validation.js +0 -92
- package/dist/utils/validation.js.map +0 -1
|
@@ -0,0 +1,211 @@
|
|
|
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 { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
17
|
+
// Import validated configuration and environment details.
|
|
18
|
+
import { config, environment } from "../config/index.js";
|
|
19
|
+
// Import core utilities: ErrorHandler, logger, requestContextService.
|
|
20
|
+
import { ErrorHandler, logger, requestContextService } from "../utils/index.js";
|
|
21
|
+
// Import registration functions for specific resources and tools.
|
|
22
|
+
import { registerObsidianDeleteFileTool } from "./tools/obsidianDeleteFileTool/index.js";
|
|
23
|
+
import { registerObsidianGlobalSearchTool } from "./tools/obsidianGlobalSearchTool/index.js";
|
|
24
|
+
import { registerObsidianListFilesTool } from "./tools/obsidianListFilesTool/index.js";
|
|
25
|
+
import { registerObsidianReadFileTool } from "./tools/obsidianReadFileTool/index.js";
|
|
26
|
+
import { registerObsidianSearchReplaceTool } from "./tools/obsidianSearchReplaceTool/index.js";
|
|
27
|
+
import { registerObsidianUpdateFileTool } from "./tools/obsidianUpdateFileTool/index.js";
|
|
28
|
+
import { registerObsidianManageFrontmatterTool } from "./tools/obsidianManageFrontmatterTool/index.js";
|
|
29
|
+
import { registerObsidianManageTagsTool } from "./tools/obsidianManageTagsTool/index.js";
|
|
30
|
+
// Import transport setup functions.
|
|
31
|
+
import { startHttpTransport } from "./transports/httpTransport.js";
|
|
32
|
+
import { connectStdioTransport } from "./transports/stdioTransport.js";
|
|
33
|
+
/**
|
|
34
|
+
* Creates and configures a new instance of the `McpServer`.
|
|
35
|
+
*
|
|
36
|
+
* This function is central to defining the server's identity and functionality
|
|
37
|
+
* as presented to connecting clients during the MCP initialization phase.
|
|
38
|
+
* It uses pre-instantiated shared services like Obsidian API and Vault Cache.
|
|
39
|
+
*
|
|
40
|
+
* MCP Spec Relevance:
|
|
41
|
+
* - Server Identity (`serverInfo`): The `name` and `version` provided here are part
|
|
42
|
+
* of the `ServerInformation` object returned in the `InitializeResult` message.
|
|
43
|
+
* - Capabilities Declaration: Declares supported features (logging, dynamic resources/tools).
|
|
44
|
+
* - Resource/Tool Registration: Calls registration functions, passing necessary service instances.
|
|
45
|
+
*
|
|
46
|
+
* Design Note: This factory is called once for 'stdio' transport and per session for 'http' transport.
|
|
47
|
+
*
|
|
48
|
+
* @param {ObsidianRestApiService} obsidianService - The shared Obsidian REST API service instance.
|
|
49
|
+
* @param {VaultCacheService | undefined} vaultCacheService - The shared Vault Cache service instance, which may be undefined if disabled.
|
|
50
|
+
* @returns {Promise<McpServer>} A promise resolving with the configured `McpServer` instance.
|
|
51
|
+
* @throws {Error} If any resource or tool registration fails.
|
|
52
|
+
* @private
|
|
53
|
+
*/
|
|
54
|
+
async function createMcpServerInstance(obsidianService, vaultCacheService) {
|
|
55
|
+
const context = requestContextService.createRequestContext({
|
|
56
|
+
operation: "createMcpServerInstance",
|
|
57
|
+
});
|
|
58
|
+
logger.info("Initializing MCP server instance with shared services", context);
|
|
59
|
+
requestContextService.configure({
|
|
60
|
+
appName: config.mcpServerName,
|
|
61
|
+
appVersion: config.mcpServerVersion,
|
|
62
|
+
environment,
|
|
63
|
+
});
|
|
64
|
+
logger.debug("Instantiating McpServer with capabilities", {
|
|
65
|
+
...context,
|
|
66
|
+
serverInfo: {
|
|
67
|
+
name: config.mcpServerName,
|
|
68
|
+
version: config.mcpServerVersion,
|
|
69
|
+
},
|
|
70
|
+
capabilities: {
|
|
71
|
+
logging: {},
|
|
72
|
+
resources: { listChanged: true },
|
|
73
|
+
tools: { listChanged: true },
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
const server = new McpServer({ name: config.mcpServerName, version: config.mcpServerVersion }, {
|
|
77
|
+
capabilities: {
|
|
78
|
+
logging: {}, // Server can receive logging/setLevel and send notifications/message
|
|
79
|
+
resources: { listChanged: true }, // Server supports dynamic resource lists
|
|
80
|
+
tools: { listChanged: true }, // Server supports dynamic tool lists
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
try {
|
|
84
|
+
logger.debug("Registering resources and tools using shared services...", context);
|
|
85
|
+
// Register all tools, passing the vaultCacheService which may be undefined
|
|
86
|
+
await registerObsidianListFilesTool(server, obsidianService);
|
|
87
|
+
await registerObsidianReadFileTool(server, obsidianService);
|
|
88
|
+
await registerObsidianDeleteFileTool(server, obsidianService, vaultCacheService);
|
|
89
|
+
if (vaultCacheService) {
|
|
90
|
+
await registerObsidianGlobalSearchTool(server, obsidianService, vaultCacheService);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
logger.warning("Skipping registration of 'obsidian_global_search' because the Vault Cache Service is disabled.", context);
|
|
94
|
+
}
|
|
95
|
+
await registerObsidianSearchReplaceTool(server, obsidianService, vaultCacheService);
|
|
96
|
+
await registerObsidianUpdateFileTool(server, obsidianService, vaultCacheService);
|
|
97
|
+
await registerObsidianManageFrontmatterTool(server, obsidianService, vaultCacheService);
|
|
98
|
+
await registerObsidianManageTagsTool(server, obsidianService, vaultCacheService);
|
|
99
|
+
logger.info("Resources and tools registered successfully", context);
|
|
100
|
+
if (vaultCacheService) {
|
|
101
|
+
logger.info("Triggering background vault cache build (if not already built/building)...", context);
|
|
102
|
+
// Intentionally not awaiting this promise to allow server startup to proceed.
|
|
103
|
+
// Errors are logged within the catch block.
|
|
104
|
+
vaultCacheService.buildVaultCache().catch((cacheBuildError) => {
|
|
105
|
+
logger.error("Error occurred during background vault cache build", {
|
|
106
|
+
...context, // Use the initial context for correlation
|
|
107
|
+
subOperation: "BackgroundVaultCacheBuild", // Add sub-operation for clarity
|
|
108
|
+
error: cacheBuildError instanceof Error
|
|
109
|
+
? cacheBuildError.message
|
|
110
|
+
: String(cacheBuildError),
|
|
111
|
+
stack: cacheBuildError instanceof Error
|
|
112
|
+
? cacheBuildError.stack
|
|
113
|
+
: undefined,
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
logger.error("Failed to register resources/tools", {
|
|
120
|
+
...context,
|
|
121
|
+
error: err instanceof Error ? err.message : String(err),
|
|
122
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
123
|
+
});
|
|
124
|
+
throw err; // Re-throw to be caught by the caller (e.g., startTransport)
|
|
125
|
+
}
|
|
126
|
+
return server;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Selects, sets up, and starts the appropriate MCP transport layer based on configuration.
|
|
130
|
+
* This function acts as the bridge between the core server logic and the communication channel.
|
|
131
|
+
* It now accepts shared service instances to pass them down the chain.
|
|
132
|
+
*
|
|
133
|
+
* MCP Spec Relevance:
|
|
134
|
+
* - Transport Selection: Uses `config.mcpTransportType` ('stdio' or 'http').
|
|
135
|
+
* - Transport Connection: Calls dedicated functions for chosen transport.
|
|
136
|
+
* - Server Instance Lifecycle: Single instance for 'stdio', per-session for 'http'.
|
|
137
|
+
*
|
|
138
|
+
* @param {ObsidianRestApiService} obsidianService - The shared Obsidian REST API service instance.
|
|
139
|
+
* @param {VaultCacheService | undefined} vaultCacheService - The shared Vault Cache service instance.
|
|
140
|
+
* @returns {Promise<McpServer | void>} Resolves with the `McpServer` instance for 'stdio', or `void` for 'http'.
|
|
141
|
+
* @throws {Error} If the configured transport type is unsupported or if transport setup fails.
|
|
142
|
+
* @private
|
|
143
|
+
*/
|
|
144
|
+
async function startTransport(obsidianService, vaultCacheService) {
|
|
145
|
+
const transportType = config.mcpTransportType;
|
|
146
|
+
const context = requestContextService.createRequestContext({
|
|
147
|
+
operation: "startTransport",
|
|
148
|
+
transport: transportType,
|
|
149
|
+
});
|
|
150
|
+
logger.info(`Starting transport: ${transportType}`, context);
|
|
151
|
+
if (transportType === "http") {
|
|
152
|
+
logger.debug("Delegating to startHttpTransport with a factory for McpServer instances...", context);
|
|
153
|
+
// For HTTP, startHttpTransport manages its own lifecycle and server instances per session.
|
|
154
|
+
// It needs a factory function to create new McpServer instances, passing along the shared services.
|
|
155
|
+
const mcpServerFactory = async () => createMcpServerInstance(obsidianService, vaultCacheService);
|
|
156
|
+
const httpServerInstance = await startHttpTransport(mcpServerFactory, context);
|
|
157
|
+
return httpServerInstance; // Return the http.Server instance.
|
|
158
|
+
}
|
|
159
|
+
if (transportType === "stdio") {
|
|
160
|
+
logger.debug("Creating single McpServer instance for stdio transport using shared services...", context);
|
|
161
|
+
const server = await createMcpServerInstance(obsidianService, vaultCacheService);
|
|
162
|
+
logger.debug("Delegating to connectStdioTransport...", context);
|
|
163
|
+
await connectStdioTransport(server, context);
|
|
164
|
+
return server; // Return the single server instance for stdio.
|
|
165
|
+
}
|
|
166
|
+
// Should not be reached if config validation is effective.
|
|
167
|
+
logger.fatal(`Unsupported transport type configured: ${transportType}`, context);
|
|
168
|
+
throw new Error(`Unsupported transport type: ${transportType}. Must be 'stdio' or 'http'.`);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Main application entry point. Initializes services and starts the MCP server.
|
|
172
|
+
* Orchestrates server startup, transport selection, and top-level error handling.
|
|
173
|
+
*
|
|
174
|
+
* MCP Spec Relevance:
|
|
175
|
+
* - Manages server startup, leading to a server ready for MCP messages.
|
|
176
|
+
* - Handles critical startup failures, ensuring appropriate process exit.
|
|
177
|
+
*
|
|
178
|
+
* @param {ObsidianRestApiService} obsidianService - The shared Obsidian REST API service instance, instantiated by the caller (e.g., index.ts).
|
|
179
|
+
* @param {VaultCacheService | undefined} vaultCacheService - The shared Vault Cache service instance, instantiated by the caller (e.g., index.ts).
|
|
180
|
+
* @returns {Promise<void | McpServer>} For 'stdio', resolves with `McpServer`. For 'http', runs indefinitely.
|
|
181
|
+
* Rejects on critical failure, leading to process exit.
|
|
182
|
+
*/
|
|
183
|
+
export async function initializeAndStartServer(obsidianService, vaultCacheService) {
|
|
184
|
+
const context = requestContextService.createRequestContext({
|
|
185
|
+
operation: "initializeAndStartServer",
|
|
186
|
+
});
|
|
187
|
+
logger.info("MCP Server initialization sequence started (services provided).", context);
|
|
188
|
+
try {
|
|
189
|
+
// Services are now provided by the caller (e.g., index.ts)
|
|
190
|
+
logger.debug("Using provided shared services (ObsidianRestApiService, VaultCacheService).", context);
|
|
191
|
+
// Initiate the transport setup based on configuration, passing shared services.
|
|
192
|
+
const result = await startTransport(obsidianService, vaultCacheService);
|
|
193
|
+
logger.info("MCP Server initialization sequence completed successfully.", context);
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
logger.fatal("Critical error during MCP server initialization.", {
|
|
198
|
+
...context,
|
|
199
|
+
error: err instanceof Error ? err.message : String(err),
|
|
200
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
201
|
+
});
|
|
202
|
+
// Ensure the error is handled by our centralized handler, which might log more details or perform cleanup.
|
|
203
|
+
ErrorHandler.handleError(err, {
|
|
204
|
+
operation: "initializeAndStartServer", // More specific operation
|
|
205
|
+
context: context, // Pass the existing context
|
|
206
|
+
critical: true, // This is a critical failure
|
|
207
|
+
});
|
|
208
|
+
logger.info("Exiting process due to critical initialization error.", context);
|
|
209
|
+
process.exit(1); // Exit with a non-zero code to indicate failure.
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Barrel file for the 'obsidian_delete_file' MCP tool.
|
|
3
|
+
*
|
|
4
|
+
* This file serves as the public entry point for the obsidian_delete_file tool module.
|
|
5
|
+
* It re-exports the primary registration function (`registerObsidianDeleteFileTool`)
|
|
6
|
+
* from the './registration.js' module. This pattern simplifies imports for consumers
|
|
7
|
+
* of the tool, allowing them to import necessary components from a single location.
|
|
8
|
+
*
|
|
9
|
+
* Consumers (like the main server setup) should import the registration function
|
|
10
|
+
* from this file to integrate the tool into the MCP server instance.
|
|
11
|
+
*/
|
|
12
|
+
export { registerObsidianDeleteFileTool } from "./registration.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Barrel file for the 'obsidian_delete_file' MCP tool.
|
|
3
|
+
*
|
|
4
|
+
* This file serves as the public entry point for the obsidian_delete_file tool module.
|
|
5
|
+
* It re-exports the primary registration function (`registerObsidianDeleteFileTool`)
|
|
6
|
+
* from the './registration.js' module. This pattern simplifies imports for consumers
|
|
7
|
+
* of the tool, allowing them to import necessary components from a single location.
|
|
8
|
+
*
|
|
9
|
+
* Consumers (like the main server setup) should import the registration function
|
|
10
|
+
* from this file to integrate the tool into the MCP server instance.
|
|
11
|
+
*/
|
|
12
|
+
export { registerObsidianDeleteFileTool } from "./registration.js";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ObsidianRestApiService, VaultCacheService } from "../../../services/obsidianRestAPI/index.js";
|
|
3
|
+
import { RequestContext } from "../../../utils/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Zod schema for validating the input parameters of the 'obsidian_delete_file' tool.
|
|
6
|
+
*/
|
|
7
|
+
export declare const ObsidianDeleteFileInputSchema: z.ZodObject<{
|
|
8
|
+
/**
|
|
9
|
+
* The vault-relative path to the file to be permanently deleted.
|
|
10
|
+
* Must include the file extension (e.g., "Old Notes/Obsolete File.md").
|
|
11
|
+
* The tool first attempts a case-sensitive match. If not found, it attempts
|
|
12
|
+
* a case-insensitive fallback search within the same directory.
|
|
13
|
+
*/
|
|
14
|
+
filePath: z.ZodString;
|
|
15
|
+
}, "strip", z.ZodTypeAny, {
|
|
16
|
+
filePath: string;
|
|
17
|
+
}, {
|
|
18
|
+
filePath: string;
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* TypeScript type inferred from the input schema (`ObsidianDeleteFileInputSchema`).
|
|
22
|
+
* Represents the validated input parameters used within the core processing logic.
|
|
23
|
+
*/
|
|
24
|
+
export type ObsidianDeleteFileInput = z.infer<typeof ObsidianDeleteFileInputSchema>;
|
|
25
|
+
/**
|
|
26
|
+
* Defines the structure of the successful response returned by the `processObsidianDeleteFile` function.
|
|
27
|
+
* This object is typically serialized to JSON and sent back to the client.
|
|
28
|
+
*/
|
|
29
|
+
export interface ObsidianDeleteFileResponse {
|
|
30
|
+
/** Indicates whether the deletion operation was successful. */
|
|
31
|
+
success: boolean;
|
|
32
|
+
/** A human-readable message confirming the deletion and specifying the path used. */
|
|
33
|
+
message: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Processes the core logic for deleting a file from the Obsidian vault.
|
|
37
|
+
*
|
|
38
|
+
* It attempts to delete the file using the provided path (case-sensitive first).
|
|
39
|
+
* If that fails with a 'NOT_FOUND' error, it attempts a case-insensitive fallback:
|
|
40
|
+
* it lists the directory, finds a unique case-insensitive match for the filename,
|
|
41
|
+
* and retries the deletion with the corrected path.
|
|
42
|
+
*
|
|
43
|
+
* @param {ObsidianDeleteFileInput} params - The validated input parameters.
|
|
44
|
+
* @param {RequestContext} context - The request context for logging and correlation.
|
|
45
|
+
* @param {ObsidianRestApiService} obsidianService - An instance of the Obsidian REST API service.
|
|
46
|
+
* @returns {Promise<ObsidianDeleteFileResponse>} A promise resolving to the structured success response
|
|
47
|
+
* containing a confirmation message.
|
|
48
|
+
* @throws {McpError} Throws an McpError if the file cannot be found (even with fallback),
|
|
49
|
+
* if there's an ambiguous fallback match, or if any other API interaction fails.
|
|
50
|
+
*/
|
|
51
|
+
export declare const processObsidianDeleteFile: (params: ObsidianDeleteFileInput, context: RequestContext, obsidianService: ObsidianRestApiService, vaultCacheService: VaultCacheService | undefined) => Promise<ObsidianDeleteFileResponse>;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import path from "node:path"; // node:path provides OS-specific path functions; using path.posix for vault path manipulation.
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
|
|
4
|
+
import { logger, retryWithDelay, } from "../../../utils/index.js";
|
|
5
|
+
// ====================================================================================
|
|
6
|
+
// Schema Definitions for Input Validation
|
|
7
|
+
// ====================================================================================
|
|
8
|
+
/**
|
|
9
|
+
* Zod schema for validating the input parameters of the 'obsidian_delete_file' tool.
|
|
10
|
+
*/
|
|
11
|
+
export const ObsidianDeleteFileInputSchema = z
|
|
12
|
+
.object({
|
|
13
|
+
/**
|
|
14
|
+
* The vault-relative path to the file to be permanently deleted.
|
|
15
|
+
* Must include the file extension (e.g., "Old Notes/Obsolete File.md").
|
|
16
|
+
* The tool first attempts a case-sensitive match. If not found, it attempts
|
|
17
|
+
* a case-insensitive fallback search within the same directory.
|
|
18
|
+
*/
|
|
19
|
+
filePath: z
|
|
20
|
+
.string()
|
|
21
|
+
.min(1, "filePath cannot be empty")
|
|
22
|
+
.describe('The vault-relative path to the file to be deleted (e.g., "archive/old-file.md"). Tries case-sensitive first, then case-insensitive fallback.'),
|
|
23
|
+
})
|
|
24
|
+
.describe("Input parameters for permanently deleting a specific file within the connected Obsidian vault. Includes a case-insensitive path fallback.");
|
|
25
|
+
// ====================================================================================
|
|
26
|
+
// Core Logic Function
|
|
27
|
+
// ====================================================================================
|
|
28
|
+
/**
|
|
29
|
+
* Processes the core logic for deleting a file from the Obsidian vault.
|
|
30
|
+
*
|
|
31
|
+
* It attempts to delete the file using the provided path (case-sensitive first).
|
|
32
|
+
* If that fails with a 'NOT_FOUND' error, it attempts a case-insensitive fallback:
|
|
33
|
+
* it lists the directory, finds a unique case-insensitive match for the filename,
|
|
34
|
+
* and retries the deletion with the corrected path.
|
|
35
|
+
*
|
|
36
|
+
* @param {ObsidianDeleteFileInput} params - The validated input parameters.
|
|
37
|
+
* @param {RequestContext} context - The request context for logging and correlation.
|
|
38
|
+
* @param {ObsidianRestApiService} obsidianService - An instance of the Obsidian REST API service.
|
|
39
|
+
* @returns {Promise<ObsidianDeleteFileResponse>} A promise resolving to the structured success response
|
|
40
|
+
* containing a confirmation message.
|
|
41
|
+
* @throws {McpError} Throws an McpError if the file cannot be found (even with fallback),
|
|
42
|
+
* if there's an ambiguous fallback match, or if any other API interaction fails.
|
|
43
|
+
*/
|
|
44
|
+
export const processObsidianDeleteFile = async (params, context, obsidianService, vaultCacheService) => {
|
|
45
|
+
const { filePath: originalFilePath } = params;
|
|
46
|
+
let effectiveFilePath = originalFilePath; // Track the path actually used for deletion
|
|
47
|
+
logger.debug(`Processing obsidian_delete_file request for path: ${originalFilePath}`, context);
|
|
48
|
+
const shouldRetryNotFound = (err) => err instanceof McpError && err.code === BaseErrorCode.NOT_FOUND;
|
|
49
|
+
try {
|
|
50
|
+
// --- Attempt 1: Delete using the provided path (case-sensitive) ---
|
|
51
|
+
const deleteContext = {
|
|
52
|
+
...context,
|
|
53
|
+
operation: "deleteFileAttempt",
|
|
54
|
+
caseSensitive: true,
|
|
55
|
+
};
|
|
56
|
+
logger.debug(`Attempting to delete file (case-sensitive): ${originalFilePath}`, deleteContext);
|
|
57
|
+
await retryWithDelay(() => obsidianService.deleteFile(originalFilePath, deleteContext), {
|
|
58
|
+
operationName: "deleteFile",
|
|
59
|
+
context: deleteContext,
|
|
60
|
+
maxRetries: 3,
|
|
61
|
+
delayMs: 300,
|
|
62
|
+
shouldRetry: shouldRetryNotFound,
|
|
63
|
+
});
|
|
64
|
+
// If the above call succeeds, the file was deleted using the exact path.
|
|
65
|
+
logger.debug(`Successfully deleted file using exact path: ${originalFilePath}`, deleteContext);
|
|
66
|
+
if (vaultCacheService) {
|
|
67
|
+
await vaultCacheService.updateCacheForFile(originalFilePath, deleteContext);
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
success: true,
|
|
71
|
+
message: `File '${originalFilePath}' deleted successfully.`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
// --- Attempt 2: Case-insensitive fallback if initial delete failed with NOT_FOUND ---
|
|
76
|
+
if (error instanceof McpError && error.code === BaseErrorCode.NOT_FOUND) {
|
|
77
|
+
logger.info(`File not found with exact path: ${originalFilePath}. Attempting case-insensitive fallback for deletion.`, context);
|
|
78
|
+
const fallbackContext = { ...context, operation: "deleteFileFallback" };
|
|
79
|
+
try {
|
|
80
|
+
// Use POSIX path functions for vault path manipulation
|
|
81
|
+
const dirname = path.posix.dirname(originalFilePath);
|
|
82
|
+
const filenameLower = path.posix
|
|
83
|
+
.basename(originalFilePath)
|
|
84
|
+
.toLowerCase();
|
|
85
|
+
// Handle case where the file is in the vault root (dirname is '.')
|
|
86
|
+
const dirToList = dirname === "." ? "/" : dirname;
|
|
87
|
+
logger.debug(`Listing directory for fallback deletion: ${dirToList}`, fallbackContext);
|
|
88
|
+
const filesInDir = await retryWithDelay(() => obsidianService.listFiles(dirToList, fallbackContext), {
|
|
89
|
+
operationName: "listFilesForDeleteFallback",
|
|
90
|
+
context: fallbackContext,
|
|
91
|
+
maxRetries: 3,
|
|
92
|
+
delayMs: 300,
|
|
93
|
+
shouldRetry: shouldRetryNotFound,
|
|
94
|
+
});
|
|
95
|
+
// Filter directory listing for files matching the lowercase filename
|
|
96
|
+
const matches = filesInDir.filter((f) => !f.endsWith("/") && // Ensure it's a file
|
|
97
|
+
path.posix.basename(f).toLowerCase() === filenameLower);
|
|
98
|
+
if (matches.length === 1) {
|
|
99
|
+
// Found exactly one case-insensitive match
|
|
100
|
+
const correctFilename = path.posix.basename(matches[0]);
|
|
101
|
+
effectiveFilePath = path.posix.join(dirname, correctFilename); // Update the path to use
|
|
102
|
+
logger.info(`Found case-insensitive match: ${effectiveFilePath}. Retrying delete.`, fallbackContext);
|
|
103
|
+
// Retry deleting with the correctly cased path
|
|
104
|
+
const retryContext = {
|
|
105
|
+
...fallbackContext,
|
|
106
|
+
subOperation: "retryDelete",
|
|
107
|
+
effectiveFilePath,
|
|
108
|
+
};
|
|
109
|
+
await retryWithDelay(() => obsidianService.deleteFile(effectiveFilePath, retryContext), {
|
|
110
|
+
operationName: "deleteFileFallback",
|
|
111
|
+
context: retryContext,
|
|
112
|
+
maxRetries: 3,
|
|
113
|
+
delayMs: 300,
|
|
114
|
+
shouldRetry: shouldRetryNotFound,
|
|
115
|
+
});
|
|
116
|
+
logger.debug(`Successfully deleted file using fallback path: ${effectiveFilePath}`, retryContext);
|
|
117
|
+
if (vaultCacheService) {
|
|
118
|
+
await vaultCacheService.updateCacheForFile(effectiveFilePath, retryContext);
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
success: true,
|
|
122
|
+
message: `File '${effectiveFilePath}' (found via case-insensitive match for '${originalFilePath}') deleted successfully.`,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
else if (matches.length > 1) {
|
|
126
|
+
// Ambiguous match: Multiple files match case-insensitively
|
|
127
|
+
const errorMsg = `Deletion failed: Ambiguous case-insensitive matches for '${originalFilePath}'. Found: [${matches.join(", ")}]. Cannot determine which file to delete.`;
|
|
128
|
+
logger.error(errorMsg, { ...fallbackContext, matches });
|
|
129
|
+
// Use CONFLICT code for ambiguity, as NOT_FOUND isn't quite right anymore.
|
|
130
|
+
throw new McpError(BaseErrorCode.CONFLICT, errorMsg, fallbackContext);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// No match found even with fallback
|
|
134
|
+
const errorMsg = `Deletion failed: File not found for '${originalFilePath}' (case-insensitive fallback also failed).`;
|
|
135
|
+
logger.error(errorMsg, fallbackContext);
|
|
136
|
+
// Stick with NOT_FOUND as the original error reason holds.
|
|
137
|
+
throw new McpError(BaseErrorCode.NOT_FOUND, errorMsg, fallbackContext);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (fallbackError) {
|
|
141
|
+
// Catch errors specifically from the fallback logic (e.g., listFiles error, retry delete error)
|
|
142
|
+
if (fallbackError instanceof McpError) {
|
|
143
|
+
// Log and re-throw known errors from fallback
|
|
144
|
+
logger.error(`McpError during fallback deletion for ${originalFilePath}: ${fallbackError.message}`, fallbackError, fallbackContext);
|
|
145
|
+
throw fallbackError;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
// Wrap unexpected fallback errors
|
|
149
|
+
const errorMessage = `Unexpected error during case-insensitive fallback deletion for ${originalFilePath}`;
|
|
150
|
+
logger.error(errorMessage, fallbackError instanceof Error ? fallbackError : undefined, fallbackContext);
|
|
151
|
+
throw new McpError(BaseErrorCode.INTERNAL_ERROR, `${errorMessage}: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`, fallbackContext);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
// Re-throw errors from the initial delete attempt that were not NOT_FOUND or McpError
|
|
157
|
+
if (error instanceof McpError) {
|
|
158
|
+
logger.error(`McpError during initial delete attempt for ${originalFilePath}: ${error.message}`, error, context);
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
const errorMessage = `Unexpected error deleting Obsidian file ${originalFilePath}`;
|
|
163
|
+
logger.error(errorMessage, error instanceof Error ? error : undefined, context);
|
|
164
|
+
throw new McpError(BaseErrorCode.INTERNAL_ERROR, `${errorMessage}: ${error instanceof Error ? error.message : String(error)}`, context);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { ObsidianRestApiService, VaultCacheService } from "../../../services/obsidianRestAPI/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Registers the 'obsidian_delete_file' tool with the MCP server.
|
|
5
|
+
*
|
|
6
|
+
* This tool permanently deletes a specified file from the user's Obsidian vault.
|
|
7
|
+
* It requires the vault-relative path, including the file extension. The tool
|
|
8
|
+
* attempts a case-sensitive deletion first, followed by a case-insensitive
|
|
9
|
+
* fallback search and delete if the initial attempt fails with a 'NOT_FOUND' error.
|
|
10
|
+
*
|
|
11
|
+
* The response is a JSON string containing a success status and a confirmation message.
|
|
12
|
+
*
|
|
13
|
+
* @param {McpServer} server - The MCP server instance to register the tool with.
|
|
14
|
+
* @param {ObsidianRestApiService} obsidianService - An instance of the Obsidian REST API service
|
|
15
|
+
* used to interact with the user's Obsidian vault.
|
|
16
|
+
* @returns {Promise<void>} A promise that resolves when the tool registration is complete or rejects on error.
|
|
17
|
+
* @throws {McpError} Throws an McpError if registration fails critically.
|
|
18
|
+
*/
|
|
19
|
+
export declare const registerObsidianDeleteFileTool: (server: McpServer, obsidianService: ObsidianRestApiService, vaultCacheService: VaultCacheService | undefined) => Promise<void>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
|
|
2
|
+
import { ErrorHandler, logger, requestContextService, } from "../../../utils/index.js";
|
|
3
|
+
import { ObsidianDeleteFileInputSchema, processObsidianDeleteFile, } from "./logic.js";
|
|
4
|
+
/**
|
|
5
|
+
* Registers the 'obsidian_delete_file' tool with the MCP server.
|
|
6
|
+
*
|
|
7
|
+
* This tool permanently deletes a specified file from the user's Obsidian vault.
|
|
8
|
+
* It requires the vault-relative path, including the file extension. The tool
|
|
9
|
+
* attempts a case-sensitive deletion first, followed by a case-insensitive
|
|
10
|
+
* fallback search and delete if the initial attempt fails with a 'NOT_FOUND' error.
|
|
11
|
+
*
|
|
12
|
+
* The response is a JSON string containing a success status and a confirmation message.
|
|
13
|
+
*
|
|
14
|
+
* @param {McpServer} server - The MCP server instance to register the tool with.
|
|
15
|
+
* @param {ObsidianRestApiService} obsidianService - An instance of the Obsidian REST API service
|
|
16
|
+
* used to interact with the user's Obsidian vault.
|
|
17
|
+
* @returns {Promise<void>} A promise that resolves when the tool registration is complete or rejects on error.
|
|
18
|
+
* @throws {McpError} Throws an McpError if registration fails critically.
|
|
19
|
+
*/
|
|
20
|
+
export const registerObsidianDeleteFileTool = async (server, obsidianService, vaultCacheService) => {
|
|
21
|
+
const toolName = "obsidian_delete_file";
|
|
22
|
+
// Updated description to accurately reflect the response (no timestamp)
|
|
23
|
+
const toolDescription = "Permanently deletes a specified file from the Obsidian vault. Tries the exact path first, then attempts a case-insensitive fallback if the file is not found. Requires the vault-relative path including the file extension. Returns a success message.";
|
|
24
|
+
// Create a context specifically for the registration process.
|
|
25
|
+
const registrationContext = requestContextService.createRequestContext({
|
|
26
|
+
operation: "RegisterObsidianDeleteFileTool",
|
|
27
|
+
toolName: toolName,
|
|
28
|
+
module: "ObsidianDeleteFileRegistration", // Identify the module
|
|
29
|
+
});
|
|
30
|
+
logger.info(`Attempting to register tool: ${toolName}`, registrationContext);
|
|
31
|
+
// Wrap the registration logic in a tryCatch block for robust error handling during server setup.
|
|
32
|
+
await ErrorHandler.tryCatch(async () => {
|
|
33
|
+
// Use the high-level SDK method `server.tool` for registration.
|
|
34
|
+
server.tool(toolName, toolDescription, ObsidianDeleteFileInputSchema.shape, // Provide the Zod schema shape for input definition.
|
|
35
|
+
/**
|
|
36
|
+
* The handler function executed when the 'obsidian_delete_file' tool is called by the client.
|
|
37
|
+
*
|
|
38
|
+
* @param {ObsidianDeleteFileInput} params - The input parameters received from the client,
|
|
39
|
+
* validated against the ObsidianDeleteFileInputSchema shape.
|
|
40
|
+
* @returns {Promise<CallToolResult>} A promise resolving to the structured result for the MCP client,
|
|
41
|
+
* containing either the successful response data (serialized JSON) or an error indication.
|
|
42
|
+
*/
|
|
43
|
+
async (params) => {
|
|
44
|
+
// Type matches the inferred input schema
|
|
45
|
+
// Create a specific context for this handler invocation.
|
|
46
|
+
const handlerContext = requestContextService.createRequestContext({
|
|
47
|
+
parentContext: registrationContext, // Link to registration context
|
|
48
|
+
operation: "HandleObsidianDeleteFileRequest",
|
|
49
|
+
toolName: toolName,
|
|
50
|
+
params: { filePath: params.filePath }, // Log the file path being targeted
|
|
51
|
+
});
|
|
52
|
+
logger.debug(`Handling '${toolName}' request`, handlerContext);
|
|
53
|
+
// Wrap the core logic execution in a tryCatch block.
|
|
54
|
+
return await ErrorHandler.tryCatch(async () => {
|
|
55
|
+
// Delegate the actual file deletion logic to the processing function.
|
|
56
|
+
// Note: Input schema and shape are identical, no separate refinement parse needed here.
|
|
57
|
+
const response = await processObsidianDeleteFile(params, handlerContext, obsidianService, vaultCacheService);
|
|
58
|
+
logger.debug(`'${toolName}' processed successfully`, handlerContext);
|
|
59
|
+
// Format the successful response object from the logic function into the required MCP CallToolResult structure.
|
|
60
|
+
// The response object (success, message) is serialized to JSON.
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: "text", // Standard content type for structured JSON data
|
|
65
|
+
text: JSON.stringify(response, null, 2), // Pretty-print JSON
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
isError: false, // Indicate successful execution
|
|
69
|
+
};
|
|
70
|
+
}, {
|
|
71
|
+
// Configuration for the inner error handler (processing logic).
|
|
72
|
+
operation: `processing ${toolName} handler`,
|
|
73
|
+
context: handlerContext,
|
|
74
|
+
input: params, // Log the full input parameters if an error occurs.
|
|
75
|
+
// Custom error mapping for consistent error reporting.
|
|
76
|
+
errorMapper: (error) => new McpError(error instanceof McpError
|
|
77
|
+
? error.code
|
|
78
|
+
: BaseErrorCode.INTERNAL_ERROR, `Error processing ${toolName} tool: ${error instanceof Error ? error.message : "Unknown error"}`, { ...handlerContext }),
|
|
79
|
+
}); // End of inner ErrorHandler.tryCatch
|
|
80
|
+
}); // End of server.tool call
|
|
81
|
+
logger.info(`Tool registered successfully: ${toolName}`, registrationContext);
|
|
82
|
+
}, {
|
|
83
|
+
// Configuration for the outer error handler (registration process).
|
|
84
|
+
operation: `registering tool ${toolName}`,
|
|
85
|
+
context: registrationContext,
|
|
86
|
+
errorCode: BaseErrorCode.INTERNAL_ERROR, // Default error code for registration failure.
|
|
87
|
+
// Custom error mapping for registration failures.
|
|
88
|
+
errorMapper: (error) => new McpError(error instanceof McpError ? error.code : BaseErrorCode.INTERNAL_ERROR, `Failed to register tool '${toolName}': ${error instanceof Error ? error.message : "Unknown error"}`, { ...registrationContext }),
|
|
89
|
+
critical: true, // Treat registration failure as critical.
|
|
90
|
+
}); // End of outer ErrorHandler.tryCatch
|
|
91
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Barrel file for the 'obsidian_global_search' MCP tool.
|
|
3
|
+
*
|
|
4
|
+
* This file serves as the public entry point for the obsidian_global_search tool module.
|
|
5
|
+
* It re-exports the primary registration function (`registerObsidianGlobalSearchTool`)
|
|
6
|
+
* from the './registration.js' module. This pattern simplifies imports for consumers
|
|
7
|
+
* of the tool, allowing them to import necessary components from a single location.
|
|
8
|
+
*
|
|
9
|
+
* Consumers (like the main server setup) should import the registration function
|
|
10
|
+
* from this file to integrate the tool into the MCP server instance.
|
|
11
|
+
*/
|
|
12
|
+
export { registerObsidianGlobalSearchTool } from "./registration.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Barrel file for the 'obsidian_global_search' MCP tool.
|
|
3
|
+
*
|
|
4
|
+
* This file serves as the public entry point for the obsidian_global_search tool module.
|
|
5
|
+
* It re-exports the primary registration function (`registerObsidianGlobalSearchTool`)
|
|
6
|
+
* from the './registration.js' module. This pattern simplifies imports for consumers
|
|
7
|
+
* of the tool, allowing them to import necessary components from a single location.
|
|
8
|
+
*
|
|
9
|
+
* Consumers (like the main server setup) should import the registration function
|
|
10
|
+
* from this file to integrate the tool into the MCP server instance.
|
|
11
|
+
*/
|
|
12
|
+
export { registerObsidianGlobalSearchTool } from "./registration.js"; // Ensure '.js' extension for ES module resolution
|