google-calendar-workspace-mcp-server 0.0.8 → 0.0.10
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/build/index.integration-with-mock.js +5 -5
- package/package.json +2 -2
- package/shared/server.d.ts +0 -1
- package/shared/server.js +0 -2
- package/shared/tools/create-event.js +5 -3
- package/shared/tools/get-event.js +5 -3
- package/shared/tools/list-events.js +5 -3
- package/shared/tools/update-event.js +5 -3
- package/shared/utils/calendar-helpers.d.ts +18 -0
- package/shared/utils/calendar-helpers.js +36 -0
|
@@ -33,7 +33,7 @@ class MockCalendarClient {
|
|
|
33
33
|
timeZone: 'America/New_York',
|
|
34
34
|
},
|
|
35
35
|
status: 'confirmed',
|
|
36
|
-
htmlLink: 'https://
|
|
36
|
+
htmlLink: 'https://www.google.com/calendar/event?eid=ZXZlbnQxX2VpZA',
|
|
37
37
|
attendees: [
|
|
38
38
|
{
|
|
39
39
|
email: 'user1@example.com',
|
|
@@ -55,7 +55,7 @@ class MockCalendarClient {
|
|
|
55
55
|
date: '2024-01-17',
|
|
56
56
|
},
|
|
57
57
|
status: 'confirmed',
|
|
58
|
-
htmlLink: 'https://
|
|
58
|
+
htmlLink: 'https://www.google.com/calendar/event?eid=ZXZlbnQyX2VpZA',
|
|
59
59
|
},
|
|
60
60
|
];
|
|
61
61
|
const filteredEvents = options?.q
|
|
@@ -87,7 +87,7 @@ class MockCalendarClient {
|
|
|
87
87
|
timeZone: 'America/New_York',
|
|
88
88
|
},
|
|
89
89
|
status: 'confirmed',
|
|
90
|
-
htmlLink: 'https://
|
|
90
|
+
htmlLink: 'https://www.google.com/calendar/event?eid=ZXZlbnQxX2VpZA',
|
|
91
91
|
created: '2024-01-01T00:00:00Z',
|
|
92
92
|
updated: '2024-01-01T00:00:00Z',
|
|
93
93
|
creator: {
|
|
@@ -132,7 +132,7 @@ class MockCalendarClient {
|
|
|
132
132
|
start: event.start || { dateTime: '2024-01-20T10:00:00-05:00' },
|
|
133
133
|
end: event.end || { dateTime: '2024-01-20T11:00:00-05:00' },
|
|
134
134
|
status: 'confirmed',
|
|
135
|
-
htmlLink: 'https://
|
|
135
|
+
htmlLink: 'https://www.google.com/calendar/event?eid=bmV3X2V2ZW50X2VpZA',
|
|
136
136
|
created: new Date().toISOString(),
|
|
137
137
|
updated: new Date().toISOString(),
|
|
138
138
|
attendees: event.attendees,
|
|
@@ -150,7 +150,7 @@ class MockCalendarClient {
|
|
|
150
150
|
start: event.start || { dateTime: '2024-01-20T10:00:00-05:00' },
|
|
151
151
|
end: event.end || { dateTime: '2024-01-20T11:00:00-05:00' },
|
|
152
152
|
status: 'confirmed',
|
|
153
|
-
htmlLink: `https://
|
|
153
|
+
htmlLink: `https://www.google.com/calendar/event?eid=dXBkYXRlZF8${eventId}`,
|
|
154
154
|
created: '2024-01-01T00:00:00Z',
|
|
155
155
|
updated: new Date().toISOString(),
|
|
156
156
|
attendees: event.attendees,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "google-calendar-workspace-mcp-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "MCP server for Google Calendar integration with service account support",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"stage-publish": "npm version"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
33
33
|
"google-auth-library": "^10.5.0",
|
|
34
34
|
"zod": "^3.24.1"
|
|
35
35
|
},
|
package/shared/server.d.ts
CHANGED
|
@@ -72,7 +72,6 @@ export interface ServiceAccountCredentials {
|
|
|
72
72
|
* Google Calendar API client implementation using service account with domain-wide delegation
|
|
73
73
|
*/
|
|
74
74
|
export declare class ServiceAccountCalendarClient implements ICalendarClient {
|
|
75
|
-
private impersonateEmail;
|
|
76
75
|
private baseUrl;
|
|
77
76
|
private jwtClient;
|
|
78
77
|
private cachedToken;
|
package/shared/server.js
CHANGED
|
@@ -5,14 +5,12 @@ import { createRegisterTools, parseEnabledToolGroups } from './tools.js';
|
|
|
5
5
|
* Google Calendar API client implementation using service account with domain-wide delegation
|
|
6
6
|
*/
|
|
7
7
|
export class ServiceAccountCalendarClient {
|
|
8
|
-
impersonateEmail;
|
|
9
8
|
baseUrl = 'https://www.googleapis.com/calendar/v3';
|
|
10
9
|
jwtClient;
|
|
11
10
|
cachedToken = null;
|
|
12
11
|
tokenExpiry = 0;
|
|
13
12
|
refreshPromise = null;
|
|
14
13
|
constructor(credentials, impersonateEmail) {
|
|
15
|
-
this.impersonateEmail = impersonateEmail;
|
|
16
14
|
this.jwtClient = new JWT({
|
|
17
15
|
email: credentials.client_email,
|
|
18
16
|
key: credentials.private_key,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { logError } from '../logging.js';
|
|
3
|
+
import { buildCalendarEventUrl } from '../utils/calendar-helpers.js';
|
|
3
4
|
export const CreateEventSchema = z.object({
|
|
4
5
|
calendar_id: z
|
|
5
6
|
.string()
|
|
@@ -214,9 +215,10 @@ export function createEventTool(server, clientFactory) {
|
|
|
214
215
|
output += ` - ${attachment.title || attachment.fileUrl}\n`;
|
|
215
216
|
}
|
|
216
217
|
}
|
|
217
|
-
// Link
|
|
218
|
-
|
|
219
|
-
|
|
218
|
+
// Link — universal calendar.google.com form (eid embeds calendar context).
|
|
219
|
+
const eventUrl = buildCalendarEventUrl(result.htmlLink);
|
|
220
|
+
if (eventUrl) {
|
|
221
|
+
output += `\n**Event Link:** ${eventUrl}\n`;
|
|
220
222
|
}
|
|
221
223
|
return {
|
|
222
224
|
content: [
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { logError } from '../logging.js';
|
|
3
|
+
import { buildCalendarEventUrl } from '../utils/calendar-helpers.js';
|
|
3
4
|
export const GetEventSchema = z.object({
|
|
4
5
|
calendar_id: z
|
|
5
6
|
.string()
|
|
@@ -128,9 +129,10 @@ export function getEventTool(server, clientFactory) {
|
|
|
128
129
|
if (event.updated) {
|
|
129
130
|
output += `**Updated:** ${new Date(event.updated).toLocaleString()}\n`;
|
|
130
131
|
}
|
|
131
|
-
// Link
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
// Link — universal calendar.google.com form (eid embeds calendar context).
|
|
133
|
+
const eventUrl = buildCalendarEventUrl(event.htmlLink);
|
|
134
|
+
if (eventUrl) {
|
|
135
|
+
output += `\n**Event Link:** ${eventUrl}\n`;
|
|
134
136
|
}
|
|
135
137
|
return {
|
|
136
138
|
content: [
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { logError } from '../logging.js';
|
|
3
|
+
import { buildCalendarEventUrl } from '../utils/calendar-helpers.js';
|
|
3
4
|
export const ListEventsSchema = z.object({
|
|
4
5
|
calendar_id: z
|
|
5
6
|
.string()
|
|
@@ -156,9 +157,10 @@ export function listEventsTool(server, clientFactory) {
|
|
|
156
157
|
: event.description;
|
|
157
158
|
output += `**Description:** ${truncated}\n`;
|
|
158
159
|
}
|
|
159
|
-
// Link
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
// Link — universal calendar.google.com form (eid embeds calendar context).
|
|
161
|
+
const eventUrl = buildCalendarEventUrl(event.htmlLink);
|
|
162
|
+
if (eventUrl) {
|
|
163
|
+
output += `**Link:** ${eventUrl}\n`;
|
|
162
164
|
}
|
|
163
165
|
output += '\n---\n\n';
|
|
164
166
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { logError } from '../logging.js';
|
|
3
|
+
import { buildCalendarEventUrl } from '../utils/calendar-helpers.js';
|
|
3
4
|
export const UpdateEventSchema = z.object({
|
|
4
5
|
event_id: z.string().min(1).describe('The ID of the event to update.'),
|
|
5
6
|
calendar_id: z
|
|
@@ -238,9 +239,10 @@ export function updateEventTool(server, clientFactory) {
|
|
|
238
239
|
output += ` - ${attachment.title || attachment.fileUrl}\n`;
|
|
239
240
|
}
|
|
240
241
|
}
|
|
241
|
-
// Link
|
|
242
|
-
|
|
243
|
-
|
|
242
|
+
// Link — universal calendar.google.com form (eid embeds calendar context).
|
|
243
|
+
const eventUrl = buildCalendarEventUrl(result.htmlLink);
|
|
244
|
+
if (eventUrl) {
|
|
245
|
+
output += `\n**Event Link:** ${eventUrl}\n`;
|
|
244
246
|
}
|
|
245
247
|
return {
|
|
246
248
|
content: [
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds a universal Google Calendar event URL on `calendar.google.com`.
|
|
3
|
+
*
|
|
4
|
+
* The eid embedded in `htmlLink` already encodes the calendar context — it
|
|
5
|
+
* base64-decodes to `<event-id> <calendar-id>`, so `calendar.google.com`
|
|
6
|
+
* can route the click to the correct calendar regardless of which Google
|
|
7
|
+
* accounts the reader is signed into.
|
|
8
|
+
*
|
|
9
|
+
* The raw `htmlLink` from Google's API uses `www.google.com` and redirects
|
|
10
|
+
* through Google's general router, which can guess wrong when the reader
|
|
11
|
+
* is signed into multiple accounts. Pointing directly at `calendar.google.com`
|
|
12
|
+
* with the same eid bypasses that redirect.
|
|
13
|
+
*
|
|
14
|
+
* Falls back to the original `htmlLink` if it cannot be parsed (e.g. the
|
|
15
|
+
* API response omitted `htmlLink` or it lacks an `eid` parameter), and
|
|
16
|
+
* returns `undefined` if `htmlLink` is missing entirely.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildCalendarEventUrl(htmlLink: string | undefined): string | undefined;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds a universal Google Calendar event URL on `calendar.google.com`.
|
|
3
|
+
*
|
|
4
|
+
* The eid embedded in `htmlLink` already encodes the calendar context — it
|
|
5
|
+
* base64-decodes to `<event-id> <calendar-id>`, so `calendar.google.com`
|
|
6
|
+
* can route the click to the correct calendar regardless of which Google
|
|
7
|
+
* accounts the reader is signed into.
|
|
8
|
+
*
|
|
9
|
+
* The raw `htmlLink` from Google's API uses `www.google.com` and redirects
|
|
10
|
+
* through Google's general router, which can guess wrong when the reader
|
|
11
|
+
* is signed into multiple accounts. Pointing directly at `calendar.google.com`
|
|
12
|
+
* with the same eid bypasses that redirect.
|
|
13
|
+
*
|
|
14
|
+
* Falls back to the original `htmlLink` if it cannot be parsed (e.g. the
|
|
15
|
+
* API response omitted `htmlLink` or it lacks an `eid` parameter), and
|
|
16
|
+
* returns `undefined` if `htmlLink` is missing entirely.
|
|
17
|
+
*/
|
|
18
|
+
export function buildCalendarEventUrl(htmlLink) {
|
|
19
|
+
if (!htmlLink) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const eid = extractEid(htmlLink);
|
|
23
|
+
if (!eid) {
|
|
24
|
+
return htmlLink;
|
|
25
|
+
}
|
|
26
|
+
return `https://calendar.google.com/calendar/event?eid=${eid}`;
|
|
27
|
+
}
|
|
28
|
+
function extractEid(htmlLink) {
|
|
29
|
+
try {
|
|
30
|
+
const url = new URL(htmlLink);
|
|
31
|
+
return url.searchParams.get('eid') ?? undefined;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
}
|