diff --git a/README.md b/README.md index dc699a0..18c9889 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,8 @@ Once this plugin is approved and available in the Obsidian Community Plugins dir ### Claude Desktop / Other Clients **Option 1: Direct HTTP Transport** (if your client supports it) + + For HTTP (default): ```json { "mcpServers": { @@ -183,6 +185,21 @@ Once this plugin is approved and available in the Obsidian Community Plugins dir } ``` + For HTTPS (requires proper certificate trust): + ```json + { + "mcpServers": { + "obsidian": { + "transport": { + "type": "http", + "url": "https://localhost:3443/mcp" + } + } + } + } + ``` + Note: Direct HTTPS transport may not work with self-signed certificates. Use Option 2 with mcp-remote for HTTPS. + **Option 2: Via mcp-remote** (recommended for Claude Desktop) Since Claude Desktop might not support streaming HTTP transport yet, use mcp-remote: @@ -205,6 +222,57 @@ Once this plugin is approved and available in the Obsidian Community Plugins dir - Windows: `%APPDATA%\Claude\claude_desktop_config.json` - Linux: `~/.config/Claude/claude_desktop_config.json` +### HTTPS/TLS Configuration (v0.9.0+) + +The plugin supports secure HTTPS connections with automatic self-signed certificate generation: + +1. **Enable HTTPS in Plugin Settings**: + - Go to plugin settings in Obsidian + - Find "HTTPS/TLS Settings" section + - Toggle "Enable HTTPS Server" + - Default HTTPS port is 3443 (configurable) + - Certificates are auto-generated on first launch + +2. **Configure Claude Code / MCP Client**: + + Since most MCP clients don't trust self-signed certificates by default, you'll need to use `mcp-remote` with environment variables: + + **Manual JSON Configuration Required**: + ```json + { + "mcpServers": { + "obsidian-vault-name": { + "command": "npx", + "args": [ + "mcp-remote", + "https://localhost:3443/mcp", + "--header", + "Authorization: Bearer YOUR_API_KEY_HERE" + ], + "env": { + "NODE_TLS_REJECT_UNAUTHORIZED": "0" + } + } + } + } + ``` + + **Important Notes**: + - Replace `YOUR_API_KEY_HERE` with your actual API key from plugin settings + - The `env` section with `NODE_TLS_REJECT_UNAUTHORIZED` is required for self-signed certificates + - Command-line tools like `claude mcp add` don't support environment variables, so manual JSON editing is required + - Edit your configuration file directly at the locations listed above + +3. **Certificate Storage**: + - Auto-generated certificates are stored in: `.obsidian/plugins/semantic-vault-mcp/certificates/` + - Certificate paths will auto-populate in settings after generation + - Certificates are valid for 1 year and include localhost/127.0.0.1 SANs + +4. **Security Considerations**: + - Self-signed certificates are suitable for local development + - For production use, consider using proper certificates from a CA + - The `NODE_TLS_REJECT_UNAUTHORIZED=0` setting disables certificate validation - use only for local connections + ### Concurrent Sessions for Agent Swarms (v0.5.8+) Enable multiple AI agents to work with your vault simultaneously without blocking each other: diff --git a/src/main.ts b/src/main.ts index 4e56620..6259293 100644 --- a/src/main.ts +++ b/src/main.ts @@ -152,44 +152,49 @@ export default class ObsidianMCPPlugin extends Plugin { async startMCPServer(): Promise { try { + // Determine which port to check based on whether HTTPS is enabled + const isHttps = this.settings.httpsEnabled && this.settings.certificateConfig?.enabled; + const portToUse = isHttps ? this.settings.httpsPort : this.settings.httpPort; + const protocol = isHttps ? 'HTTPS' : 'HTTP'; + // Check for port conflicts and auto-switch if needed if (this.settings.autoDetectPortConflicts) { - const status = await this.checkPortConflict(this.settings.httpPort); + const status = await this.checkPortConflict(portToUse); if (status === 'in-use') { - const suggestedPort = await this.findAvailablePort(this.settings.httpPort); + const suggestedPort = await this.findAvailablePort(portToUse); if (suggestedPort === 0) { // All alternate ports are busy - const portsChecked = `${this.settings.httpPort}, ${this.settings.httpPort + 1}, ${this.settings.httpPort + 2}, ${this.settings.httpPort + 3}`; + const portsChecked = `${portToUse}, ${portToUse + 1}, ${portToUse + 2}, ${portToUse + 3}`; Debug.error(`❌ Failed to find available port after 3 attempts. Ports checked: ${portsChecked}`); Debug.error('Please check for other applications using these ports or firewall/security software blocking access.'); - new Notice(`Cannot start MCP server: Ports ${this.settings.httpPort}-${this.settings.httpPort + 3} are all in use. Check console for details.`); + new Notice(`Cannot start MCP server: Ports ${portToUse}-${portToUse + 3} are all in use. Check console for details.`); this.updateStatusBar(); return; } - Debug.log(`⚠️ Port ${this.settings.httpPort} is in use, switching to port ${suggestedPort}`); - new Notice(`Port ${this.settings.httpPort} is in use. Switching to port ${suggestedPort}`); + Debug.log(`⚠️ ${protocol} Port ${portToUse} is in use, switching to port ${suggestedPort}`); + new Notice(`${protocol} Port ${portToUse} is in use. Switching to port ${suggestedPort}`); // Temporarily use the suggested port for this session this.mcpServer = new MCPHttpServer(this.app, suggestedPort, this); await this.mcpServer.start(); this.updateStatusBar(); - Debug.log(`✅ MCP server started on alternate port ${suggestedPort}`); + Debug.log(`✅ MCP server started on alternate ${protocol} port ${suggestedPort}`); if (this.settings.showConnectionStatus) { - new Notice(`MCP server started on port ${suggestedPort} (default port was in use)`); + new Notice(`MCP server started on ${protocol} port ${suggestedPort} (default port was in use)`); } return; } } - Debug.log(`🚀 Starting MCP server on port ${this.settings.httpPort}...`); - this.mcpServer = new MCPHttpServer(this.app, this.settings.httpPort, this); + Debug.log(`🚀 Starting MCP server on ${protocol} port ${portToUse}...`); + this.mcpServer = new MCPHttpServer(this.app, portToUse, this); await this.mcpServer.start(); this.updateStatusBar(); Debug.log('✅ MCP server started successfully'); if (this.settings.showConnectionStatus) { - new Notice(`MCP server started on port ${this.settings.httpPort}`); + new Notice(`MCP server started on ${protocol} port ${portToUse}`); } } catch (error) { Debug.error('❌ Failed to start MCP server:', error); @@ -678,7 +683,7 @@ class MCPSettingTab extends PluginSettingTab { })); if (this.plugin.settings.httpsEnabled) { - new Setting(containerEl) + const httpsPortSetting = new Setting(containerEl) .setName('HTTPS Port') .setDesc('Port for HTTPS MCP server (default: 3443)') .addText(text => text @@ -689,9 +694,14 @@ class MCPSettingTab extends PluginSettingTab { if (!isNaN(port) && port > 0 && port < 65536) { this.plugin.settings.httpsPort = port; await this.plugin.saveSettings(); + // Check port availability for HTTPS + this.checkHttpsPortAvailability(port, httpsPortSetting); } })); + // Check HTTPS port availability on load + this.checkHttpsPortAvailability(this.plugin.settings.httpsPort, httpsPortSetting); + new Setting(containerEl) .setName('Auto-generate Certificate') .setDesc('Automatically generate a self-signed certificate if none exists') @@ -1393,6 +1403,26 @@ class MCPSettingTab extends PluginSettingTab { setting.setDesc('Port for HTTP MCP server (default: 3001)'); } } + + private async checkHttpsPortAvailability(port: number, setting: Setting): Promise { + if (!this.plugin.settings.autoDetectPortConflicts) return; + + const status = await this.plugin.checkPortConflict(port); + + switch (status) { + case 'available': + setting.setDesc(`Port for HTTPS MCP server (default: 3443) ✅ Available`); + break; + case 'this-server': + setting.setDesc(`Port for HTTPS MCP server (default: 3443) 🟢 This server`); + break; + case 'in-use': + setting.setDesc(`Port for HTTPS MCP server (default: 3443) ⚠️ Port ${port} in use`); + break; + default: + setting.setDesc('Port for HTTPS MCP server (default: 3443)'); + } + } refreshConnectionStatus(): void { // Simply refresh the entire settings display to ensure accurate data