morgen-mcp 0.1.1 → 0.1.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/package.json +2 -2
- package/src/calendar-cache.js +8 -1
- package/src/client.js +5 -0
- package/src/index.js +1 -1
- package/src/tools-events.js +22 -10
- package/src/tools-tasks.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "morgen-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "MCP server for Morgen — events, tasks, and calendar management for Claude Code via natural language",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"test": "vitest run"
|
|
21
21
|
},
|
|
22
22
|
"engines": {
|
|
23
|
-
"node": ">=
|
|
23
|
+
"node": ">=20"
|
|
24
24
|
},
|
|
25
25
|
"publishConfig": {
|
|
26
26
|
"access": "public",
|
package/src/calendar-cache.js
CHANGED
|
@@ -9,6 +9,7 @@ const TTL_MS = 10 * 60 * 1000;
|
|
|
9
9
|
|
|
10
10
|
let cache = null;
|
|
11
11
|
let expiresAt = 0;
|
|
12
|
+
let loadingPromise = null;
|
|
12
13
|
|
|
13
14
|
async function loadCache() {
|
|
14
15
|
const raw = await morgenFetch("/v3/calendars/list", { points: 10 });
|
|
@@ -45,7 +46,11 @@ async function loadCache() {
|
|
|
45
46
|
|
|
46
47
|
export async function getCalendarCache() {
|
|
47
48
|
if (cache && expiresAt > Date.now()) return cache;
|
|
48
|
-
|
|
49
|
+
if (loadingPromise) return loadingPromise;
|
|
50
|
+
loadingPromise = loadCache().finally(() => {
|
|
51
|
+
loadingPromise = null;
|
|
52
|
+
});
|
|
53
|
+
return loadingPromise;
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
export async function resolveCalendarMeta(calendarId) {
|
|
@@ -93,12 +98,14 @@ export async function getAllAccountsWithCalendars() {
|
|
|
93
98
|
export function _resetCalendarCache() {
|
|
94
99
|
cache = null;
|
|
95
100
|
expiresAt = 0;
|
|
101
|
+
loadingPromise = null;
|
|
96
102
|
}
|
|
97
103
|
|
|
98
104
|
// Test helper: preload the cache with fake entries so handlers can look up
|
|
99
105
|
// calendar metadata without hitting a real API. Entries should be
|
|
100
106
|
// { id, accountId, name?, readOnly?, integrationId?, color? } objects.
|
|
101
107
|
export function _seedCalendarCache(entries) {
|
|
108
|
+
loadingPromise = null;
|
|
102
109
|
const byId = new Map();
|
|
103
110
|
const byAccount = new Map();
|
|
104
111
|
for (const e of entries) {
|
package/src/client.js
CHANGED
|
@@ -34,6 +34,11 @@ function msUntilFits(now, incomingPoints) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
function enforceRateLimit(points) {
|
|
37
|
+
if (points > RATE_LIMIT_POINTS) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`request requires ${points} points but the Morgen rate limit budget is only ${RATE_LIMIT_POINTS} points per 15 minutes`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
37
42
|
const now = Date.now();
|
|
38
43
|
pruneLedger(now);
|
|
39
44
|
|
package/src/index.js
CHANGED
package/src/tools-events.js
CHANGED
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
groupCalendarIdsByAccount,
|
|
23
23
|
resolveCalendarMeta,
|
|
24
24
|
resolveDefaultCalendarMeta,
|
|
25
|
-
_resetCalendarCache,
|
|
26
25
|
} from "./calendar-cache.js";
|
|
27
26
|
import {
|
|
28
27
|
EVENT_TOOLS,
|
|
@@ -69,8 +68,16 @@ function validateRequiredString(value, field, maxLen) {
|
|
|
69
68
|
validateString(value, field, maxLen);
|
|
70
69
|
}
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
// Morgen represents event locations as a keyed map of Location objects, not a
|
|
72
|
+
// scalar string. Wrap the caller-provided string into the minimum viable shape.
|
|
73
|
+
function toLocationsMap(locationString) {
|
|
74
|
+
if (!locationString) return undefined;
|
|
75
|
+
return {
|
|
76
|
+
[locationString]: {
|
|
77
|
+
"@type": "Location",
|
|
78
|
+
name: locationString,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
74
81
|
}
|
|
75
82
|
|
|
76
83
|
// ---------- handlers ----------
|
|
@@ -186,7 +193,7 @@ async function handleCreateEvent(args = {}) {
|
|
|
186
193
|
};
|
|
187
194
|
|
|
188
195
|
if (args.description !== undefined) body.description = args.description;
|
|
189
|
-
if (args.location !== undefined) body.
|
|
196
|
+
if (args.location !== undefined) body.locations = toLocationsMap(args.location);
|
|
190
197
|
if (args.participants !== undefined) {
|
|
191
198
|
body.participants = toParticipantMap(args.participants);
|
|
192
199
|
}
|
|
@@ -242,15 +249,20 @@ async function handleUpdateEvent(args = {}) {
|
|
|
242
249
|
calendarId: calendarMeta.id,
|
|
243
250
|
};
|
|
244
251
|
if (args.title !== undefined) body.title = args.title;
|
|
245
|
-
|
|
246
|
-
|
|
252
|
+
// Morgen requires the full timing quartet (start, duration, timeZone,
|
|
253
|
+
// showWithoutTime) whenever any timing field is updated.
|
|
254
|
+
if (args.start !== undefined || args.end !== undefined) {
|
|
255
|
+
if (args.start !== undefined) {
|
|
256
|
+
body.start = isoUtcToLocal(args.start, timeZone);
|
|
257
|
+
}
|
|
247
258
|
body.timeZone = timeZone;
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
259
|
+
if (args.start !== undefined && args.end !== undefined) {
|
|
260
|
+
body.duration = isoDurationFromRange(args.start, args.end);
|
|
261
|
+
}
|
|
262
|
+
body.showWithoutTime = false;
|
|
251
263
|
}
|
|
252
264
|
if (args.description !== undefined) body.description = args.description;
|
|
253
|
-
if (args.location !== undefined) body.
|
|
265
|
+
if (args.location !== undefined) body.locations = toLocationsMap(args.location);
|
|
254
266
|
if (args.participants !== undefined) {
|
|
255
267
|
body.participants = toParticipantMap(args.participants);
|
|
256
268
|
}
|
package/src/tools-tasks.js
CHANGED
|
@@ -293,7 +293,7 @@ export const taskHandlers = {
|
|
|
293
293
|
move_task: async (args = {}) => {
|
|
294
294
|
const id = validateId(args.task_id, "task_id");
|
|
295
295
|
const taskListId = validateId(args.task_list_id, "task_list_id");
|
|
296
|
-
const response = await morgenFetch("/v3/tasks/
|
|
296
|
+
const response = await morgenFetch("/v3/tasks/update", {
|
|
297
297
|
method: "POST",
|
|
298
298
|
body: { id, taskListId },
|
|
299
299
|
points: 1,
|