dav-mcp 3.0.0 → 3.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/CHANGELOG.md CHANGED
@@ -7,6 +7,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.0.1] - 2026-01-20
11
+
12
+ ### Added
13
+ - **CLI flags**: `--http` and `--port` flags for easier server startup
14
+ - `npx dav-mcp` → STDIO mode (default)
15
+ - `npx dav-mcp --http` → HTTP mode on port 3000
16
+ - `npx dav-mcp --http --port=8080` → HTTP mode with custom port
17
+
18
+ ### Changed
19
+ - **README restructured** by user type (Claude Desktop, n8n, Docker)
20
+ - **Simplified setup**: No git clone needed for most users, just `npx dav-mcp`
21
+ - **Cleaned up `.env.example`**: Removed unnecessary `MCP_SERVER_NAME` and `MCP_SERVER_VERSION`
22
+
23
+ ## [3.0.0] - 2026-01-20
24
+
25
+ ### Breaking Changes
26
+ - **Removed HTTP+SSE transport**: The deprecated `/sse` and `/messages` endpoints have been removed
27
+ - **New transports**: Replaced with STDIO and Stateless HTTP transports
28
+ - **n8n users**: Must update endpoint from `/sse` to `/mcp`
29
+
30
+ ### Added
31
+ - **STDIO transport** (`src/server-stdio.js`): For local clients (Claude Desktop, Cursor, npx)
32
+ - **Stateless HTTP transport** (`src/server-http.js`): For remote clients (n8n, cloud deployments)
33
+ - **MIGRATION.md**: Upgrade guide for migrating from v2.x
34
+
35
+ ### Changed
36
+ - Default `npm start` now runs STDIO server (was SSE)
37
+ - Logger now writes to stderr in STDIO mode (preserves stdout for JSON-RPC)
38
+ - Dockerfile updated to use HTTP server
39
+ - Simplified HTTP server (stateless, no session management)
40
+
41
+ ### Removed
42
+ - `src/index.js` (old HTTP+SSE server)
43
+ - Session management in HTTP transport
44
+ - `/sse` endpoint
45
+ - `/messages` endpoint
46
+
47
+ ### Migration
48
+ See [MIGRATION.md](MIGRATION.md) for detailed upgrade instructions.
49
+
10
50
  ## [2.7.0] - 2025-10-30
11
51
 
12
52
  ### Added
package/README.md CHANGED
@@ -4,65 +4,108 @@
4
4
 
5
5
  Built on 26 production-ready tools spanning CalDAV, CardDAV, and VTODO protocols.
6
6
 
7
- Built for n8n, Claude Desktop, and any MCP client.
8
-
9
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
8
  [![npm version](https://badge.fury.io/js/dav-mcp.svg)](https://www.npmjs.com/package/dav-mcp)
11
9
 
12
10
  ---
13
11
 
14
- ### The Orchestration
12
+ ## Quick Start
15
13
 
16
- When partial tools force your AI to improvise, complete tools let it **execute precise operations across all components**.
14
+ ### Claude Desktop / Cursor (Local)
17
15
 
18
- | Capability | dav-mcp | Most MCPs |
19
- |------------|---------|-------------------|
20
- | **Calendar Management** | ✅ Full CRUD (11 tools) | ⚠️ Create + list only (2-3 tools) |
21
- | **Contact Management** | ✅ Complete CardDAV (8 tools) | ❌ Often missing entirely |
22
- | **Task Management** | ✅ Full VTODO support (7 tools) | ❌ Rarely included |
23
- | **Field-Based Updates** | ✅ All RFC properties + custom fields | ❌ Rarely available |
24
- | **Server-Side Filtering** | ✅ Efficient queries | ❌ Dumps all data |
25
- | **Multi-Provider** | ✅ Any CalDAV/CardDAV server | ⚠️ Limited provider support |
26
- | **Total Tools** | **26 tools** | **2-6 tools** |
16
+ Add to your MCP config file:
17
+
18
+ ```json
19
+ {
20
+ "mcpServers": {
21
+ "dav-mcp": {
22
+ "command": "npx",
23
+ "args": ["-y", "dav-mcp"],
24
+ "env": {
25
+ "CALDAV_SERVER_URL": "https://dav.example.com",
26
+ "CALDAV_USERNAME": "your_username",
27
+ "CALDAV_PASSWORD": "your_password"
28
+ }
29
+ }
30
+ }
31
+ }
32
+ ```
33
+
34
+ **Config file locations:**
35
+ - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
36
+ - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
37
+ - **Linux**: `~/.config/Claude/claude_desktop_config.json`
27
38
 
39
+ Restart Claude Desktop after adding the configuration.
28
40
 
29
41
  ---
30
42
 
31
- ## 🚀 Full Feature Set
43
+ ### n8n (Remote HTTP)
32
44
 
33
- ### Protocol Support
34
- - **26 MCP Tools**: Full CRUD operations for calendars, contacts, and tasks
35
- - **CalDAV Integration**: ~88% tsdav coverage (11 tools)
36
- - **CardDAV Integration**: 100% tsdav coverage (8 tools)
37
- - **VTODO Support**: Full task management with status, priorities, due dates (7 tools)
38
- - **Field-Based Updates**: Field-agnostic updates via tsdav-utils - supports all RFC 5545/6350 properties + custom X-* fields
39
- - **RFC-Compliant**: ical.js for RFC 5545 (iCalendar) and RFC 6350 (vCard) support
45
+ Start the HTTP server:
40
46
 
41
- ### Production-Ready Infrastructure
42
- - **MCP Protocol**: STDIO transport for local clients, Stateless HTTP for remote
43
- - **Multi-Server Tested**: Works with Radicale, Baikal, Nextcloud, iCloud
44
- - **Bearer Auth**: Token authentication for HTTP transport
45
- - **Structured Logging**: Custom JSON logger with millisecond precision
47
+ ```bash
48
+ CALDAV_SERVER_URL=https://dav.example.com \
49
+ CALDAV_USERNAME=your_username \
50
+ CALDAV_PASSWORD=your_password \
51
+ BEARER_TOKEN=your-secret-token \
52
+ npx dav-mcp --http
53
+ ```
54
+
55
+ Then in n8n:
56
+ 1. Add **AI Agent** node
57
+ 2. Add **MCP Client Tool** node and connect to AI Agent
58
+ 3. Configure:
59
+ - **MCP Endpoint**: `http://localhost:3000/mcp`
60
+ - **Authentication**: Bearer
61
+ - **Token**: your-secret-token
62
+
63
+ **Custom port:**
64
+ ```bash
65
+ npx dav-mcp --http --port=8080
66
+ ```
67
+
68
+ ---
46
69
 
47
- ### Efficiency Features
48
- - **Server-Side Filtering**: calendar_query, addressbook_query, todo_query with smart filters reduce data transfer
49
- - **LLM-Optimized Tool Design**: PREFERRED/WARNING labels guide AI to efficient choices
50
- - **Batch Operations**: multi_get tools fetch multiple specific items without loading entire collections
70
+ ### Docker
71
+
72
+ ```bash
73
+ git clone https://github.com/PhilflowIO/dav-mcp.git
74
+ cd dav-mcp
75
+ cp .env.example .env
76
+ # Edit .env with your credentials
77
+ docker-compose up
78
+ ```
51
79
 
52
80
  ---
53
81
 
54
- ## 📋 Available Tools (26 Total)
82
+ ## The Orchestration
83
+
84
+ When partial tools force your AI to improvise, complete tools let it **execute precise operations across all components**.
85
+
86
+ | Capability | dav-mcp | Most MCPs |
87
+ |------------|---------|-----------|
88
+ | **Calendar Management** | Full CRUD (11 tools) | Create + list only (2-3 tools) |
89
+ | **Contact Management** | Complete CardDAV (8 tools) | Often missing entirely |
90
+ | **Task Management** | Full VTODO support (7 tools) | Rarely included |
91
+ | **Field-Based Updates** | All RFC properties + custom fields | Rarely available |
92
+ | **Server-Side Filtering** | Efficient queries | Dumps all data |
93
+ | **Multi-Provider** | Any CalDAV/CardDAV server | Limited provider support |
94
+ | **Total Tools** | **26 tools** | **2-6 tools** |
95
+
96
+ ---
55
97
 
98
+ ## Available Tools (26 Total)
56
99
 
57
100
  ### CalDAV Tools (11 tools)
58
101
 
59
102
  1. **list_calendars** - List all available calendars
60
- 2. **list_events** - List ALL events (⚠️ WARNING: use calendar_query for filtered searches)
103
+ 2. **list_events** - List ALL events (use calendar_query for filtered searches)
61
104
  3. **create_event** - Create a new calendar event
62
- 4. **update_event** - PREFERRED: Update any event field (SUMMARY, LOCATION, DTSTART, STATUS, custom X-* properties)
105
+ 4. **update_event** - PREFERRED: Update any event field (SUMMARY, LOCATION, DTSTART, STATUS, custom X-* properties)
63
106
  5. **update_event_raw** - Update event with raw iCal data (advanced)
64
107
  6. **delete_event** - Delete an event permanently
65
- 7. **calendar_query** - PREFERRED: Search and filter events efficiently by text, date range, or location
108
+ 7. **calendar_query** - PREFERRED: Search and filter events efficiently by text, date range, or location
66
109
  8. **make_calendar** - Create a new calendar collection
67
110
  9. **update_calendar** - Update calendar properties (display name, description, color, timezone)
68
111
  10. **delete_calendar** - Permanently delete a calendar and all its events
@@ -71,29 +114,27 @@ When partial tools force your AI to improvise, complete tools let it **execute p
71
114
  ### CardDAV Tools (8 tools)
72
115
 
73
116
  12. **list_addressbooks** - List all available address books
74
- 13. **list_contacts** - List ALL contacts (⚠️ WARNING: use addressbook_query for filtered searches)
117
+ 13. **list_contacts** - List ALL contacts (use addressbook_query for filtered searches)
75
118
  14. **create_contact** - Create a new contact (vCard)
76
- 15. **update_contact** - PREFERRED: Update any contact field (FN, EMAIL, TEL, ORG, ADR, custom X-* properties)
119
+ 15. **update_contact** - PREFERRED: Update any contact field (FN, EMAIL, TEL, ORG, ADR, custom X-* properties)
77
120
  16. **update_contact_raw** - Update contact with raw vCard data (advanced)
78
121
  17. **delete_contact** - Delete a contact permanently
79
- 18. **addressbook_query** - PREFERRED: Search and filter contacts efficiently by name, email, or organization
122
+ 18. **addressbook_query** - PREFERRED: Search and filter contacts efficiently by name, email, or organization
80
123
  19. **addressbook_multi_get** - Batch fetch multiple specific contacts by URLs
81
124
 
82
125
  ### VTODO Tools (7 tools)
83
126
 
84
- 20. **list_todos** - List ALL todos/tasks (⚠️ WARNING: use todo_query for filtered searches)
127
+ 20. **list_todos** - List ALL todos/tasks (use todo_query for filtered searches)
85
128
  21. **create_todo** - Create a new todo/task with optional due date, priority, status
86
- 22. **update_todo** - PREFERRED: Update any todo field (SUMMARY, STATUS, PRIORITY, DUE, PERCENT-COMPLETE, custom X-* properties)
129
+ 22. **update_todo** - PREFERRED: Update any todo field (SUMMARY, STATUS, PRIORITY, DUE, PERCENT-COMPLETE, custom X-* properties)
87
130
  23. **update_todo_raw** - Update todo with raw VTODO iCal data (advanced)
88
131
  24. **delete_todo** - Delete a todo/task permanently
89
- 25. **todo_query** - PREFERRED: Search and filter todos efficiently by status/due date
132
+ 25. **todo_query** - PREFERRED: Search and filter todos efficiently by status/due date
90
133
  26. **todo_multi_get** - Batch fetch multiple specific todos by URLs
91
134
 
92
135
  ---
93
136
 
94
- ## 💡 Real-World Applications
95
-
96
- See how complete tool coverage transforms basic operations into powerful workflows.
137
+ ## Real-World Applications
97
138
 
98
139
  ### n8n Automation Workflows
99
140
  - **Meeting Management**: "Show me all Friday meetings" → calendar_query with date filter returns only relevant events
@@ -109,120 +150,54 @@ See how complete tool coverage transforms basic operations into powerful workflo
109
150
 
110
151
  ---
111
152
 
112
- ## 🛠️ Start Orchestrating in 5 Minutes
113
-
114
- ### Installation
115
-
116
- ```bash
117
- git clone https://github.com/PhilflowIO/dav-mcp.git
118
- cd dav-mcp
119
- npm install
120
- cp .env.example .env
121
- ```
122
-
123
- ### Configuration
153
+ ## Works Across All Major Providers
124
154
 
125
- Edit `.env`:
126
-
127
- ```env
128
- # CalDAV/CardDAV Server
129
- CALDAV_SERVER_URL=https://dav.example.com
130
- CALDAV_USERNAME=your_username
131
- CALDAV_PASSWORD=your_password
132
-
133
- # MCP Server
134
- PORT=3000
135
- MCP_SERVER_NAME=dav-mcp
136
- MCP_SERVER_VERSION=2.6.0
137
-
138
- # Authentication (optional)
139
- BEARER_TOKEN=your-secure-token-here
140
- ```
155
+ Works with any CalDAV/CardDAV server that follows RFC 4791 and RFC 6352:
141
156
 
142
- ### Start Server
143
-
144
- ```bash
145
- npm start
146
- # Server runs on: http://localhost:3000
147
- ```
157
+ - **Nextcloud** - Full support
158
+ - **Baikal** - Full support
159
+ - **Radicale** - Full support
160
+ - **iCloud** - Works with app-specific password
161
+ - **Any RFC-compliant server** - Standard protocol support
148
162
 
149
163
  ---
150
164
 
151
- ## 🔗 Integration
152
-
153
- ### n8n Workflow
154
-
155
- 1. **Start HTTP server**: `BEARER_TOKEN=your-token npm run start:http`
156
- 2. **Add "AI Agent" node**
157
- 3. **Add "MCP Client Tool" node** and connect to AI Agent
158
- 4. **Configure the connection:**
159
- - **MCP Endpoint**: `http://localhost:3000/mcp`
160
- - **Authentication Method**: `Bearer`
161
- - **Bearer Token**: Your token
162
-
163
- 5. **Example prompts:**
164
-
165
- ```
166
- "List all my calendars"
167
- "Create a meeting tomorrow at 2 PM"
168
- "Show me all events in October"
169
- "Find all contacts at Google"
170
- "Create a new contact for Jane Smith"
171
- "Show overdue tasks with high priority"
172
- ```
173
-
174
- ### Claude Desktop
165
+ ## Google Calendar (OAuth2)
175
166
 
176
- Add to `claude_desktop_config.json`:
167
+ For Google Calendar, use OAuth2 authentication:
177
168
 
178
169
  ```json
179
170
  {
180
171
  "mcpServers": {
181
172
  "dav-mcp": {
182
- "command": "node",
183
- "args": ["/absolute/path/to/dav-mcp/src/server-stdio.js"],
173
+ "command": "npx",
174
+ "args": ["-y", "dav-mcp"],
184
175
  "env": {
185
- "CALDAV_SERVER_URL": "https://dav.example.com",
186
- "CALDAV_USERNAME": "your_username",
187
- "CALDAV_PASSWORD": "your_password"
176
+ "AUTH_METHOD": "OAuth",
177
+ "GOOGLE_USER": "your@gmail.com",
178
+ "GOOGLE_CLIENT_ID": "your-client-id",
179
+ "GOOGLE_CLIENT_SECRET": "your-client-secret",
180
+ "GOOGLE_REFRESH_TOKEN": "your-refresh-token"
188
181
  }
189
182
  }
190
183
  }
191
184
  }
192
185
  ```
193
186
 
194
- **Config file locations:**
195
- - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
196
- - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
197
- - **Linux**: `~/.config/Claude/claude_desktop_config.json`
198
-
199
- **Restart Claude Desktop** after adding the configuration.
200
-
201
187
  ---
202
188
 
203
- ## 🌐 Works Across All Major Providers
204
-
205
- Works with any CalDAV/CardDAV server that follows RFC 4791 and RFC 6352:
206
-
207
- - ✅ **Nextcloud** - Full support
208
- - ✅ **Baikal** - Full support
209
- - ✅ **Radicale** - Full support
210
- - ✅ **iCloud** - Works with app-specific password
211
- - ✅ **Any RFC-compliant server** - Standard protocol support
212
-
213
-
214
- ## 🔒 Security
189
+ ## Security
215
190
 
216
191
  - **Input Validation**: All inputs validated with Zod schemas before execution
217
- - **Rate Limiting**: 100 requests/minute per session
218
- - **Bearer Auth**: Optional token authentication
192
+ - **Rate Limiting**: 100 requests/minute per session (HTTP mode)
193
+ - **Bearer Auth**: Token authentication for HTTP transport
219
194
  - **No Credential Storage**: Pass-through only, never logged or cached
220
195
  - **Structured Logging**: Audit trail with request IDs, no PII exposure
221
196
  - **CORS Protection**: Whitelist origins, block cross-site attacks
222
197
 
198
+ ---
223
199
 
224
-
225
- ## 📚 Documentation
200
+ ## Documentation
226
201
 
227
202
  - **[MCP Specification](https://modelcontextprotocol.io/specification/2025-03-26)** - Model Context Protocol docs
228
203
  - **[tsdav Docs](https://tsdav.vercel.app/docs/intro)** - CalDAV/CardDAV library reference
@@ -231,19 +206,19 @@ Works with any CalDAV/CardDAV server that follows RFC 4791 and RFC 6352:
231
206
 
232
207
  ---
233
208
 
234
- ## 🤝 Contributing
209
+ ## Contributing
235
210
 
236
211
  Pull requests are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
237
212
 
238
213
  ---
239
214
 
240
- ## 📄 License
215
+ ## License
241
216
 
242
217
  MIT License - see [LICENSE](LICENSE) for details
243
218
 
244
219
  ---
245
220
 
246
- ## 🙏 Acknowledgments
221
+ ## Acknowledgments
247
222
 
248
223
  Built with:
249
224
  - **[tsdav](https://github.com/natelindev/tsdav)** - Excellent TypeScript CalDAV/CardDAV library
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dav-mcp",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "Complete DAV integration for AI - Calendar (CalDAV), Contacts (CardDAV), and Tasks (VTODO) management via MCP protocol",
5
5
  "type": "module",
6
6
  "main": "src/server-stdio.js",
@@ -60,10 +60,10 @@
60
60
  "CHANGELOG.md"
61
61
  ],
62
62
  "dependencies": {
63
- "@modelcontextprotocol/sdk": "^1.18.2",
63
+ "@modelcontextprotocol/sdk": "^1.25.3",
64
64
  "cors": "^2.8.5",
65
- "dotenv": "^16.4.7",
66
- "express": "^4.21.2",
65
+ "dotenv": "^17.2.3",
66
+ "express": "^5.2.1",
67
67
  "express-rate-limit": "^8.1.0",
68
68
  "ical.js": "^2.2.1",
69
69
  "tsdav": "github:PhilflowIO/tsdav#master",
package/src/logger.js CHANGED
@@ -9,6 +9,9 @@
9
9
  // Detect STDIO transport mode - must write to stderr to preserve stdout for JSON-RPC
10
10
  const isStdioMode = process.env.MCP_TRANSPORT === 'stdio';
11
11
 
12
+ // Cache environment check to avoid repeated env access (satisfies CodeQL)
13
+ const isProduction = process.env.NODE_ENV === 'production';
14
+
12
15
  class JSONLogger {
13
16
  constructor(context = {}, level = 'info') {
14
17
  this.context = context;
@@ -61,7 +64,7 @@ class JSONLogger {
61
64
  : (msg) => console.log(msg);
62
65
 
63
66
  // Pretty-print in development, single-line JSON in production
64
- if (process.env.NODE_ENV !== 'production') {
67
+ if (!isProduction) {
65
68
  // Development: colored output with readable format
66
69
  const colorMap = {
67
70
  error: '\x1b[31m', // Red
@@ -175,7 +175,7 @@ function createMCPServer(requestId) {
175
175
  const server = new Server(
176
176
  {
177
177
  name: process.env.MCP_SERVER_NAME || 'dav-mcp',
178
- version: process.env.MCP_SERVER_VERSION || '3.0.0',
178
+ version: process.env.MCP_SERVER_VERSION || '3.0.1',
179
179
  },
180
180
  {
181
181
  capabilities: {
@@ -310,7 +310,7 @@ app.get('/health', (req, res) => {
310
310
  res.json({
311
311
  status: 'healthy',
312
312
  server: process.env.MCP_SERVER_NAME || 'dav-mcp',
313
- version: process.env.MCP_SERVER_VERSION || '3.0.0',
313
+ version: process.env.MCP_SERVER_VERSION || '3.0.1',
314
314
  transport: 'http-stateless',
315
315
  timestamp: new Date().toISOString(),
316
316
  tools: tools.length,
@@ -324,7 +324,7 @@ app.get('/health', (req, res) => {
324
324
  app.get('/', (req, res) => {
325
325
  res.json({
326
326
  name: process.env.MCP_SERVER_NAME || 'dav-mcp',
327
- version: process.env.MCP_SERVER_VERSION || '3.0.0',
327
+ version: process.env.MCP_SERVER_VERSION || '3.0.1',
328
328
  transport: 'http-stateless',
329
329
  description: 'MCP Streamable HTTP Server for CalDAV/CardDAV integration (stateless)',
330
330
  endpoints: {
@@ -1,23 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * dav-mcp STDIO Server
3
+ * dav-mcp - Main Entry Point
4
4
  *
5
- * Entry point for local MCP clients: Claude Desktop, Cursor, VS Code, npx usage
6
- *
7
- * CRITICAL: This server uses STDIO transport. All logging MUST go to stderr
8
- * to avoid corrupting JSON-RPC messages on stdout.
5
+ * Supports two modes:
6
+ * - STDIO (default): For local clients (Claude Desktop, Cursor, VS Code)
7
+ * - HTTP (--http flag): For remote clients (n8n, cloud deployments)
9
8
  *
10
9
  * Usage:
11
- * MCP_TRANSPORT=stdio node src/server-stdio.js
12
- *
13
- * Or via npx (after npm publish):
14
- * npx dav-mcp
10
+ * npx dav-mcp # STDIO mode (default)
11
+ * npx dav-mcp --http # HTTP mode on port 3000
12
+ * npx dav-mcp --http --port=8080 # HTTP mode on custom port
15
13
  *
16
14
  * Configuration via environment variables:
17
15
  * - CALDAV_SERVER_URL: CalDAV server URL
18
16
  * - CALDAV_USERNAME: Username for Basic Auth
19
17
  * - CALDAV_PASSWORD: Password for Basic Auth
20
18
  * - AUTH_METHOD: 'Basic' (default) or 'OAuth'
19
+ * - BEARER_TOKEN: Required for HTTP mode
21
20
  *
22
21
  * For OAuth2 (Google Calendar):
23
22
  * - GOOGLE_SERVER_URL: Google CalDAV URL
@@ -27,146 +26,161 @@
27
26
  * - GOOGLE_REFRESH_TOKEN: OAuth2 refresh token
28
27
  */
29
28
 
30
- // Set STDIO mode BEFORE importing logger
31
- process.env.MCP_TRANSPORT = 'stdio';
29
+ // Parse CLI arguments BEFORE any imports
30
+ const args = process.argv.slice(2);
31
+ const isHttpMode = args.includes('--http');
32
+ const portArg = args.find(a => a.startsWith('--port='));
32
33
 
33
- import dotenv from 'dotenv';
34
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
35
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
36
- import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
34
+ if (isHttpMode) {
35
+ // HTTP mode - set port and load HTTP server
36
+ if (portArg) {
37
+ process.env.PORT = portArg.split('=')[1];
38
+ }
39
+ // Dynamic import of HTTP server (it will start itself)
40
+ import('./server-http.js');
41
+ } else {
42
+ // STDIO mode - run STDIO server
43
+ startStdioServer();
44
+ }
37
45
 
38
- import { tsdavManager } from './tsdav-client.js';
39
- import { tools } from './tools/index.js';
40
- import { createToolErrorResponse, MCP_ERROR_CODES } from './error-handler.js';
41
- import { logger } from './logger.js';
42
- import { initializeToolCallLogger, getToolCallLogger } from './tool-call-logger.js';
46
+ async function startStdioServer() {
47
+ // Set STDIO mode BEFORE importing logger
48
+ process.env.MCP_TRANSPORT = 'stdio';
49
+
50
+ const dotenv = await import('dotenv');
51
+ const { Server } = await import('@modelcontextprotocol/sdk/server/index.js');
52
+ const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
53
+ const { ListToolsRequestSchema, CallToolRequestSchema } = await import('@modelcontextprotocol/sdk/types.js');
54
+
55
+ const { tsdavManager } = await import('./tsdav-client.js');
56
+ const { tools } = await import('./tools/index.js');
57
+ const { createToolErrorResponse, MCP_ERROR_CODES } = await import('./error-handler.js');
58
+ const { logger } = await import('./logger.js');
59
+ const { initializeToolCallLogger, getToolCallLogger } = await import('./tool-call-logger.js');
60
+
61
+ // Load environment variables
62
+ dotenv.default.config();
63
+
64
+ /**
65
+ * Initialize tsdav clients based on auth method
66
+ */
67
+ async function initializeTsdav() {
68
+ const authMethod = process.env.AUTH_METHOD || 'Basic';
69
+
70
+ if (authMethod === 'OAuth' || authMethod === 'Oauth') {
71
+ // OAuth2 Configuration (e.g., Google Calendar)
72
+ logger.info('Initializing with OAuth2 authentication');
73
+
74
+ if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_CLIENT_SECRET || !process.env.GOOGLE_REFRESH_TOKEN) {
75
+ throw new Error('OAuth2 requires GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and GOOGLE_REFRESH_TOKEN');
76
+ }
77
+
78
+ await tsdavManager.initialize({
79
+ serverUrl: process.env.GOOGLE_SERVER_URL || 'https://apidata.googleusercontent.com/caldav/v2/',
80
+ authMethod: 'OAuth',
81
+ username: process.env.GOOGLE_USER,
82
+ clientId: process.env.GOOGLE_CLIENT_ID,
83
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET,
84
+ refreshToken: process.env.GOOGLE_REFRESH_TOKEN,
85
+ tokenUrl: process.env.GOOGLE_TOKEN_URL || 'https://accounts.google.com/o/oauth2/token',
86
+ });
43
87
 
44
- // Load environment variables
45
- dotenv.config();
88
+ logger.info('OAuth2 clients initialized successfully');
89
+ } else {
90
+ // Basic Auth Configuration (standard CalDAV servers)
91
+ logger.info('Initializing with Basic authentication');
46
92
 
47
- /**
48
- * Initialize tsdav clients based on auth method
49
- */
50
- async function initializeTsdav() {
51
- const authMethod = process.env.AUTH_METHOD || 'Basic';
93
+ if (!process.env.CALDAV_SERVER_URL || !process.env.CALDAV_USERNAME || !process.env.CALDAV_PASSWORD) {
94
+ throw new Error('Basic Auth requires CALDAV_SERVER_URL, CALDAV_USERNAME, and CALDAV_PASSWORD');
95
+ }
52
96
 
53
- if (authMethod === 'OAuth' || authMethod === 'Oauth') {
54
- // OAuth2 Configuration (e.g., Google Calendar)
55
- logger.info('Initializing with OAuth2 authentication');
97
+ await tsdavManager.initialize({
98
+ serverUrl: process.env.CALDAV_SERVER_URL,
99
+ authMethod: 'Basic',
100
+ username: process.env.CALDAV_USERNAME,
101
+ password: process.env.CALDAV_PASSWORD,
102
+ });
56
103
 
57
- if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_CLIENT_SECRET || !process.env.GOOGLE_REFRESH_TOKEN) {
58
- throw new Error('OAuth2 requires GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and GOOGLE_REFRESH_TOKEN');
104
+ logger.info('Basic Auth clients initialized successfully');
59
105
  }
106
+ }
60
107
 
61
- await tsdavManager.initialize({
62
- serverUrl: process.env.GOOGLE_SERVER_URL || 'https://apidata.googleusercontent.com/caldav/v2/',
63
- authMethod: 'OAuth',
64
- username: process.env.GOOGLE_USER,
65
- clientId: process.env.GOOGLE_CLIENT_ID,
66
- clientSecret: process.env.GOOGLE_CLIENT_SECRET,
67
- refreshToken: process.env.GOOGLE_REFRESH_TOKEN,
68
- tokenUrl: process.env.GOOGLE_TOKEN_URL || 'https://accounts.google.com/o/oauth2/token',
108
+ /**
109
+ * Create MCP Server with tool handlers
110
+ */
111
+ function createMCPServer() {
112
+ const server = new Server(
113
+ {
114
+ name: process.env.MCP_SERVER_NAME || 'dav-mcp',
115
+ version: process.env.MCP_SERVER_VERSION || '3.0.1',
116
+ },
117
+ {
118
+ capabilities: {
119
+ tools: {},
120
+ },
121
+ }
122
+ );
123
+
124
+ // Register tools/list handler
125
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
126
+ logger.debug({ count: tools.length }, 'tools/list request received');
127
+ return {
128
+ tools: tools.map(t => ({
129
+ name: t.name,
130
+ description: t.description,
131
+ inputSchema: t.inputSchema,
132
+ })),
133
+ };
69
134
  });
70
135
 
71
- logger.info('OAuth2 clients initialized successfully');
72
- } else {
73
- // Basic Auth Configuration (standard CalDAV servers)
74
- logger.info('Initializing with Basic authentication');
75
-
76
- if (!process.env.CALDAV_SERVER_URL || !process.env.CALDAV_USERNAME || !process.env.CALDAV_PASSWORD) {
77
- throw new Error('Basic Auth requires CALDAV_SERVER_URL, CALDAV_USERNAME, and CALDAV_PASSWORD');
78
- }
79
-
80
- await tsdavManager.initialize({
81
- serverUrl: process.env.CALDAV_SERVER_URL,
82
- authMethod: 'Basic',
83
- username: process.env.CALDAV_USERNAME,
84
- password: process.env.CALDAV_PASSWORD,
136
+ // Register tools/call handler
137
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
138
+ const toolName = request.params.name;
139
+ const toolArgs = request.params.arguments || {};
140
+ const toolCallLogger = getToolCallLogger();
141
+
142
+ logger.info({ tool: toolName }, 'tools/call request received');
143
+
144
+ const tool = tools.find(t => t.name === toolName);
145
+ if (!tool) {
146
+ logger.error({ tool: toolName }, 'Tool not found');
147
+ const error = new Error(`Unknown tool: ${toolName}`);
148
+ error.code = MCP_ERROR_CODES.METHOD_NOT_FOUND;
149
+ throw error;
150
+ }
151
+
152
+ const startTime = Date.now();
153
+ toolCallLogger.logToolCallStart(toolName, toolArgs, { transport: 'stdio' });
154
+
155
+ try {
156
+ logger.debug({ tool: toolName }, 'Executing tool');
157
+ const result = await tool.handler(toolArgs);
158
+ const duration = Date.now() - startTime;
159
+
160
+ logger.info({ tool: toolName, duration }, 'Tool executed successfully');
161
+ toolCallLogger.logToolCallSuccess(toolName, toolArgs, result, {
162
+ transport: 'stdio',
163
+ duration,
164
+ });
165
+
166
+ return result;
167
+ } catch (error) {
168
+ const duration = Date.now() - startTime;
169
+
170
+ logger.error({ tool: toolName, error: error.message }, 'Tool execution error');
171
+ toolCallLogger.logToolCallError(toolName, toolArgs, error, {
172
+ transport: 'stdio',
173
+ duration,
174
+ });
175
+
176
+ return createToolErrorResponse(error, process.env.NODE_ENV === 'development');
177
+ }
85
178
  });
86
179
 
87
- logger.info('Basic Auth clients initialized successfully');
180
+ return server;
88
181
  }
89
- }
90
-
91
- /**
92
- * Create MCP Server with tool handlers
93
- */
94
- function createMCPServer() {
95
- const server = new Server(
96
- {
97
- name: process.env.MCP_SERVER_NAME || 'dav-mcp',
98
- version: process.env.MCP_SERVER_VERSION || '3.0.0',
99
- },
100
- {
101
- capabilities: {
102
- tools: {},
103
- },
104
- }
105
- );
106
-
107
- // Register tools/list handler
108
- server.setRequestHandler(ListToolsRequestSchema, async () => {
109
- logger.debug({ count: tools.length }, 'tools/list request received');
110
- return {
111
- tools: tools.map(t => ({
112
- name: t.name,
113
- description: t.description,
114
- inputSchema: t.inputSchema,
115
- })),
116
- };
117
- });
118
-
119
- // Register tools/call handler
120
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
121
- const toolName = request.params.name;
122
- const args = request.params.arguments || {};
123
- const toolCallLogger = getToolCallLogger();
124
-
125
- logger.info({ tool: toolName }, 'tools/call request received');
126
-
127
- const tool = tools.find(t => t.name === toolName);
128
- if (!tool) {
129
- logger.error({ tool: toolName }, 'Tool not found');
130
- const error = new Error(`Unknown tool: ${toolName}`);
131
- error.code = MCP_ERROR_CODES.METHOD_NOT_FOUND;
132
- throw error;
133
- }
134
182
 
135
- const startTime = Date.now();
136
- toolCallLogger.logToolCallStart(toolName, args, { transport: 'stdio' });
137
-
138
- try {
139
- logger.debug({ tool: toolName }, 'Executing tool');
140
- const result = await tool.handler(args);
141
- const duration = Date.now() - startTime;
142
-
143
- logger.info({ tool: toolName, duration }, 'Tool executed successfully');
144
- toolCallLogger.logToolCallSuccess(toolName, args, result, {
145
- transport: 'stdio',
146
- duration,
147
- });
148
-
149
- return result;
150
- } catch (error) {
151
- const duration = Date.now() - startTime;
152
-
153
- logger.error({ tool: toolName, error: error.message }, 'Tool execution error');
154
- toolCallLogger.logToolCallError(toolName, args, error, {
155
- transport: 'stdio',
156
- duration,
157
- });
158
-
159
- return createToolErrorResponse(error, process.env.NODE_ENV === 'development');
160
- }
161
- });
162
-
163
- return server;
164
- }
165
-
166
- /**
167
- * Main entry point
168
- */
169
- async function main() {
183
+ // Main entry point
170
184
  try {
171
185
  logger.info('Starting dav-mcp STDIO server...');
172
186
 
@@ -189,7 +203,7 @@ async function main() {
189
203
 
190
204
  logger.info({
191
205
  name: process.env.MCP_SERVER_NAME || 'dav-mcp',
192
- version: process.env.MCP_SERVER_VERSION || '3.0.0',
206
+ version: process.env.MCP_SERVER_VERSION || '3.0.1',
193
207
  tools: tools.length,
194
208
  }, 'dav-mcp STDIO server ready');
195
209
 
@@ -197,29 +211,26 @@ async function main() {
197
211
  logger.error({ error: error.message, stack: error.stack }, 'Fatal error starting server');
198
212
  process.exit(1);
199
213
  }
200
- }
201
214
 
202
- // Graceful shutdown handlers
203
- process.on('SIGTERM', () => {
204
- logger.info('Received SIGTERM, shutting down...');
205
- process.exit(0);
206
- });
207
-
208
- process.on('SIGINT', () => {
209
- logger.info('Received SIGINT, shutting down...');
210
- process.exit(0);
211
- });
212
-
213
- // Handle uncaught errors gracefully
214
- process.on('uncaughtException', (error) => {
215
- logger.error({ error: error.message, stack: error.stack }, 'Uncaught exception');
216
- process.exit(1);
217
- });
218
-
219
- process.on('unhandledRejection', (reason) => {
220
- logger.error({ reason }, 'Unhandled promise rejection');
221
- process.exit(1);
222
- });
223
-
224
- // Start the server
225
- main();
215
+ // Graceful shutdown handlers
216
+ process.on('SIGTERM', () => {
217
+ logger.info('Received SIGTERM, shutting down...');
218
+ process.exit(0);
219
+ });
220
+
221
+ process.on('SIGINT', () => {
222
+ logger.info('Received SIGINT, shutting down...');
223
+ process.exit(0);
224
+ });
225
+
226
+ // Handle uncaught errors gracefully
227
+ process.on('uncaughtException', (error) => {
228
+ logger.error({ error: error.message, stack: error.stack }, 'Uncaught exception');
229
+ process.exit(1);
230
+ });
231
+
232
+ process.on('unhandledRejection', (reason) => {
233
+ logger.error({ reason }, 'Unhandled promise rejection');
234
+ process.exit(1);
235
+ });
236
+ }
@@ -27,7 +27,7 @@ export const deleteTodo = {
27
27
  const client = tsdavManager.getCalDavClient();
28
28
 
29
29
  await client.deleteTodo({
30
- todo: {
30
+ calendarObject: {
31
31
  url: validated.todo_url,
32
32
  etag: validated.todo_etag,
33
33
  },
@@ -99,7 +99,7 @@ export const updateTodoFields = {
99
99
 
100
100
  // Step 3: Send the updated todo back to server
101
101
  const updateResponse = await client.updateTodo({
102
- todo: {
102
+ calendarObject: {
103
103
  url: validated.todo_url,
104
104
  data: updatedData,
105
105
  etag: validated.todo_etag
@@ -31,7 +31,7 @@ export const updateTodoRaw = {
31
31
  const client = tsdavManager.getCalDavClient();
32
32
 
33
33
  const result = await client.updateTodo({
34
- todo: {
34
+ calendarObject: {
35
35
  url: validated.todo_url,
36
36
  data: validated.updated_ical_data,
37
37
  etag: validated.todo_etag,