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.
@@ -33,7 +33,7 @@ class MockCalendarClient {
33
33
  timeZone: 'America/New_York',
34
34
  },
35
35
  status: 'confirmed',
36
- htmlLink: 'https://calendar.google.com/event?eid=event1',
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://calendar.google.com/event?eid=event2',
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://calendar.google.com/event?eid=event1',
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://calendar.google.com/event?eid=new-event-id',
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://calendar.google.com/event?eid=${eventId}`,
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.8",
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.19.1",
32
+ "@modelcontextprotocol/sdk": "^1.29.0",
33
33
  "google-auth-library": "^10.5.0",
34
34
  "zod": "^3.24.1"
35
35
  },
@@ -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
- if (result.htmlLink) {
219
- output += `\n**Event Link:** ${result.htmlLink}\n`;
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
- if (event.htmlLink) {
133
- output += `\n**Event Link:** ${event.htmlLink}\n`;
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
- if (event.htmlLink) {
161
- output += `**Link:** ${event.htmlLink}\n`;
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
- if (result.htmlLink) {
243
- output += `\n**Event Link:** ${result.htmlLink}\n`;
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
+ }