dav-mcp 3.0.1 → 3.0.3
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/package.json +4 -4
- package/src/logger.js +4 -1
- package/src/tools/calendar/update-event-fields.js +29 -34
- package/src/tools/contacts/update-contact-fields.js +29 -34
- package/src/tools/todos/delete-todo.js +1 -1
- package/src/tools/todos/update-todo-fields.js +29 -34
- package/src/tools/todos/update-todo-raw.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dav-mcp",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
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.
|
|
63
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
64
64
|
"cors": "^2.8.5",
|
|
65
|
-
"dotenv": "^
|
|
66
|
-
"express": "^
|
|
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 (
|
|
67
|
+
if (!isProduction) {
|
|
65
68
|
// Development: colored output with readable format
|
|
66
69
|
const colorMap = {
|
|
67
70
|
error: '\x1b[31m', // Red
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { tsdavManager } from '../../tsdav-client.js';
|
|
2
2
|
import { validateInput } from '../../validation.js';
|
|
3
|
-
import { formatSuccess
|
|
3
|
+
import { formatSuccess } from '../../formatters.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { updateFields } from 'tsdav-utils';
|
|
6
6
|
|
|
@@ -76,44 +76,39 @@ export const updateEventFields = {
|
|
|
76
76
|
required: ['event_url', 'event_etag']
|
|
77
77
|
},
|
|
78
78
|
handler: async (args) => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const client = tsdavManager.getCalDavClient();
|
|
79
|
+
const validated = validateInput(updateEventFieldsSchema, args);
|
|
80
|
+
const client = tsdavManager.getCalDavClient();
|
|
82
81
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
// Step 1: Fetch the current event from server
|
|
83
|
+
const calendarUrl = validated.event_url.substring(0, validated.event_url.lastIndexOf('/') + 1);
|
|
84
|
+
const currentEvents = await client.fetchCalendarObjects({
|
|
85
|
+
calendar: { url: calendarUrl },
|
|
86
|
+
objectUrls: [validated.event_url]
|
|
87
|
+
});
|
|
89
88
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const calendarObject = currentEvents[0];
|
|
89
|
+
if (!currentEvents || currentEvents.length === 0) {
|
|
90
|
+
throw new Error('Event not found');
|
|
91
|
+
}
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
// Accepts any RFC 5545 property name (UPPERCASE)
|
|
98
|
-
const updatedData = updateFields(calendarObject, validated.fields || {});
|
|
93
|
+
const calendarObject = currentEvents[0];
|
|
99
94
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
url: validated.event_url,
|
|
104
|
-
data: updatedData,
|
|
105
|
-
etag: validated.event_etag
|
|
106
|
-
}
|
|
107
|
-
});
|
|
95
|
+
// Step 2: Update fields using tsdav-utils (field-agnostic)
|
|
96
|
+
// Accepts any RFC 5545 property name (UPPERCASE)
|
|
97
|
+
const updatedData = updateFields(calendarObject, validated.fields || {});
|
|
108
98
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
99
|
+
// Step 3: Send the updated event back to server
|
|
100
|
+
const updateResponse = await client.updateCalendarObject({
|
|
101
|
+
calendarObject: {
|
|
102
|
+
url: validated.event_url,
|
|
103
|
+
data: updatedData,
|
|
104
|
+
etag: validated.event_etag
|
|
105
|
+
}
|
|
106
|
+
});
|
|
114
107
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
108
|
+
return formatSuccess('Event updated successfully', {
|
|
109
|
+
etag: updateResponse.etag,
|
|
110
|
+
updated_fields: Object.keys(validated.fields || {}),
|
|
111
|
+
message: `Updated ${Object.keys(validated.fields || {}).length} field(s): ${Object.keys(validated.fields || {}).join(', ')}`
|
|
112
|
+
});
|
|
118
113
|
}
|
|
119
114
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { tsdavManager } from '../../tsdav-client.js';
|
|
2
2
|
import { validateInput } from '../../validation.js';
|
|
3
|
-
import { formatSuccess
|
|
3
|
+
import { formatSuccess } from '../../formatters.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { updateFields } from 'tsdav-utils';
|
|
6
6
|
|
|
@@ -92,44 +92,39 @@ export const updateContactFields = {
|
|
|
92
92
|
required: ['vcard_url', 'vcard_etag']
|
|
93
93
|
},
|
|
94
94
|
handler: async (args) => {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const client = tsdavManager.getCardDavClient();
|
|
95
|
+
const validated = validateInput(updateContactFieldsSchema, args);
|
|
96
|
+
const client = tsdavManager.getCardDavClient();
|
|
98
97
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
// Step 1: Fetch the current vCard from server
|
|
99
|
+
const addressBookUrl = validated.vcard_url.substring(0, validated.vcard_url.lastIndexOf('/') + 1);
|
|
100
|
+
const currentVCards = await client.fetchVCards({
|
|
101
|
+
addressBook: { url: addressBookUrl },
|
|
102
|
+
objectUrls: [validated.vcard_url]
|
|
103
|
+
});
|
|
105
104
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const vCardObject = currentVCards[0];
|
|
105
|
+
if (!currentVCards || currentVCards.length === 0) {
|
|
106
|
+
throw new Error('Contact not found');
|
|
107
|
+
}
|
|
111
108
|
|
|
112
|
-
|
|
113
|
-
// Accepts any RFC 6350 vCard property name (UPPERCASE)
|
|
114
|
-
const updatedData = updateFields(vCardObject, validated.fields || {});
|
|
109
|
+
const vCardObject = currentVCards[0];
|
|
115
110
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
url: validated.vcard_url,
|
|
120
|
-
data: updatedData,
|
|
121
|
-
etag: validated.vcard_etag
|
|
122
|
-
}
|
|
123
|
-
});
|
|
111
|
+
// Step 2: Update fields using tsdav-utils (field-agnostic)
|
|
112
|
+
// Accepts any RFC 6350 vCard property name (UPPERCASE)
|
|
113
|
+
const updatedData = updateFields(vCardObject, validated.fields || {});
|
|
124
114
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
115
|
+
// Step 3: Send the updated vCard back to server
|
|
116
|
+
const updateResponse = await client.updateVCard({
|
|
117
|
+
vCard: {
|
|
118
|
+
url: validated.vcard_url,
|
|
119
|
+
data: updatedData,
|
|
120
|
+
etag: validated.vcard_etag
|
|
121
|
+
}
|
|
122
|
+
});
|
|
130
123
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
124
|
+
return formatSuccess('Contact updated successfully', {
|
|
125
|
+
etag: updateResponse.etag,
|
|
126
|
+
updated_fields: Object.keys(validated.fields || {}),
|
|
127
|
+
message: `Updated ${Object.keys(validated.fields || {}).length} field(s): ${Object.keys(validated.fields || {}).join(', ')}`
|
|
128
|
+
});
|
|
134
129
|
}
|
|
135
130
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { tsdavManager } from '../../tsdav-client.js';
|
|
2
2
|
import { validateInput } from '../../validation.js';
|
|
3
|
-
import { formatSuccess
|
|
3
|
+
import { formatSuccess } from '../../formatters.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { updateFields } from 'tsdav-utils';
|
|
6
6
|
|
|
@@ -76,44 +76,39 @@ export const updateTodoFields = {
|
|
|
76
76
|
required: ['todo_url', 'todo_etag']
|
|
77
77
|
},
|
|
78
78
|
handler: async (args) => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const client = tsdavManager.getCalDavClient();
|
|
79
|
+
const validated = validateInput(updateTodoFieldsSchema, args);
|
|
80
|
+
const client = tsdavManager.getCalDavClient();
|
|
82
81
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
// Step 1: Fetch the current todo from server
|
|
83
|
+
const calendarUrl = validated.todo_url.substring(0, validated.todo_url.lastIndexOf('/') + 1);
|
|
84
|
+
const currentTodos = await client.fetchTodos({
|
|
85
|
+
calendar: { url: calendarUrl },
|
|
86
|
+
objectUrls: [validated.todo_url]
|
|
87
|
+
});
|
|
89
88
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const todoObject = currentTodos[0];
|
|
89
|
+
if (!currentTodos || currentTodos.length === 0) {
|
|
90
|
+
throw new Error('Todo not found');
|
|
91
|
+
}
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
// Accepts any RFC 5545 VTODO property name (UPPERCASE)
|
|
98
|
-
const updatedData = updateFields(todoObject, validated.fields || {});
|
|
93
|
+
const todoObject = currentTodos[0];
|
|
99
94
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
url: validated.todo_url,
|
|
104
|
-
data: updatedData,
|
|
105
|
-
etag: validated.todo_etag
|
|
106
|
-
}
|
|
107
|
-
});
|
|
95
|
+
// Step 2: Update fields using tsdav-utils (field-agnostic)
|
|
96
|
+
// Accepts any RFC 5545 VTODO property name (UPPERCASE)
|
|
97
|
+
const updatedData = updateFields(todoObject, validated.fields || {});
|
|
108
98
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
99
|
+
// Step 3: Send the updated todo back to server
|
|
100
|
+
const updateResponse = await client.updateTodo({
|
|
101
|
+
calendarObject: {
|
|
102
|
+
url: validated.todo_url,
|
|
103
|
+
data: updatedData,
|
|
104
|
+
etag: validated.todo_etag
|
|
105
|
+
}
|
|
106
|
+
});
|
|
114
107
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
108
|
+
return formatSuccess('Todo updated successfully', {
|
|
109
|
+
etag: updateResponse.etag,
|
|
110
|
+
updated_fields: Object.keys(validated.fields || {}),
|
|
111
|
+
message: `Updated ${Object.keys(validated.fields || {}).length} field(s): ${Object.keys(validated.fields || {}).join(', ')}`
|
|
112
|
+
});
|
|
118
113
|
}
|
|
119
114
|
};
|