google-tools-mcp 1.2.11 → 2.0.0

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.
Files changed (97) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +555 -276
  3. package/dist/cachedToolsList.js +52 -52
  4. package/dist/googleDocsApiHelpers.js +76 -12
  5. package/dist/helpers.js +572 -252
  6. package/dist/httpAuth.js +296 -0
  7. package/dist/index.js +162 -11
  8. package/dist/logger.js +87 -87
  9. package/dist/markdown-transformer/docsToMarkdown.js +222 -21
  10. package/dist/markdown-transformer/index.js +19 -9
  11. package/dist/markdown-transformer/markdownToDocs.js +458 -28
  12. package/dist/readTracker.js +136 -17
  13. package/dist/sessionContext.js +37 -0
  14. package/dist/setup.js +187 -9
  15. package/dist/tools/calendar/getBusy.js +64 -64
  16. package/dist/tools/calendar/getEvents.js +141 -141
  17. package/dist/tools/calendar/getFree.js +225 -225
  18. package/dist/tools/calendar/index.js +19 -19
  19. package/dist/tools/calendar/listCalendars.js +38 -38
  20. package/dist/tools/calendar/listRecurringInstances.js +83 -83
  21. package/dist/tools/calendar/manageCalendar.js +121 -121
  22. package/dist/tools/calendar/manageEvent.js +258 -258
  23. package/dist/tools/calendar/moveEvent.js +66 -66
  24. package/dist/tools/docs/addTab.js +10 -12
  25. package/dist/tools/docs/appendToGoogleDoc.js +9 -4
  26. package/dist/tools/docs/deleteRange.js +9 -4
  27. package/dist/tools/docs/findAndReplace.js +9 -4
  28. package/dist/tools/docs/formatting/applyParagraphStyle.js +4 -1
  29. package/dist/tools/docs/formatting/getFormatting.js +171 -171
  30. package/dist/tools/docs/insertImage.js +15 -2
  31. package/dist/tools/docs/insertPageBreak.js +4 -1
  32. package/dist/tools/docs/insertTable.js +4 -1
  33. package/dist/tools/docs/insertTableWithData.js +4 -1
  34. package/dist/tools/docs/modifyText.js +14 -4
  35. package/dist/tools/docs/modifyText.test.js +84 -84
  36. package/dist/tools/docs/readGoogleDoc.js +71 -11
  37. package/dist/tools/docs/renameTab.js +12 -14
  38. package/dist/tools/drafts.js +165 -165
  39. package/dist/tools/drive/createDocument.js +13 -1
  40. package/dist/tools/drive/downloadFile.js +268 -268
  41. package/dist/tools/drive/getFileInfo.js +48 -48
  42. package/dist/tools/drive/getFilePath.js +50 -50
  43. package/dist/tools/drive/listDriveFiles.js +112 -112
  44. package/dist/tools/drive/listSharedDrives.js +52 -52
  45. package/dist/tools/drive/listSharedWithMe.js +97 -97
  46. package/dist/tools/drive/uploadFile.js +111 -111
  47. package/dist/tools/extras/index.js +9 -9
  48. package/dist/tools/extras/readDriveFile.js +212 -206
  49. package/dist/tools/extras/readFile.js +84 -84
  50. package/dist/tools/extras/searchFileContents.js +81 -81
  51. package/dist/tools/forms/batchUpdateForm.js +81 -81
  52. package/dist/tools/forms/createForm.js +54 -54
  53. package/dist/tools/forms/getForm.js +118 -118
  54. package/dist/tools/forms/getFormResponse.js +45 -45
  55. package/dist/tools/forms/index.js +15 -15
  56. package/dist/tools/forms/listFormResponses.js +56 -56
  57. package/dist/tools/forms/setPublishSettings.js +59 -59
  58. package/dist/tools/gmail/drafts.js +165 -165
  59. package/dist/tools/gmail/labels.js +54 -83
  60. package/dist/tools/gmail/messages.js +444 -441
  61. package/dist/tools/gmail/settings.js +358 -528
  62. package/dist/tools/gmail/threads.js +286 -164
  63. package/dist/tools/index.js +511 -473
  64. package/dist/tools/labels.js +103 -103
  65. package/dist/tools/legacyAliases.js +426 -0
  66. package/dist/tools/maps/directions.js +72 -0
  67. package/dist/tools/maps/geocode.js +16 -0
  68. package/dist/tools/maps/index.js +15 -0
  69. package/dist/tools/maps/mapsClient.js +83 -0
  70. package/dist/tools/maps/placeDetails.js +16 -0
  71. package/dist/tools/maps/reverseGeocode.js +19 -0
  72. package/dist/tools/maps/searchNearby.js +54 -0
  73. package/dist/tools/maps/searchPlaces.js +24 -0
  74. package/dist/tools/messages.js +448 -448
  75. package/dist/tools/settings.js +528 -528
  76. package/dist/tools/slides/createPresentation.js +104 -104
  77. package/dist/tools/slides/createShape.js +92 -92
  78. package/dist/tools/slides/createTextBox.js +82 -82
  79. package/dist/tools/slides/deleteSlide.js +30 -30
  80. package/dist/tools/slides/duplicateSlide.js +37 -37
  81. package/dist/tools/slides/exportThumbnail.js +42 -42
  82. package/dist/tools/slides/formatParagraph.js +72 -72
  83. package/dist/tools/slides/formatText.js +84 -84
  84. package/dist/tools/slides/getPresentation.js +87 -87
  85. package/dist/tools/slides/index.js +33 -33
  86. package/dist/tools/slides/reorderSlides.js +41 -41
  87. package/dist/tools/slides/replaceAllText.js +46 -46
  88. package/dist/tools/slides/setBackground.js +58 -58
  89. package/dist/tools/slides/speakerNotes.js +102 -102
  90. package/dist/tools/slides/styleShape.js +111 -111
  91. package/dist/tools/slides/updatePresentation.js +128 -128
  92. package/dist/tools/threads.js +145 -145
  93. package/dist/tools/utils/appendMarkdownToGoogleDoc.js +31 -7
  94. package/dist/tools/utils/replaceDocumentWithMarkdown.js +72 -9
  95. package/dist/updateCheck.js +202 -0
  96. package/dist/workspace.js +122 -0
  97. package/package.json +82 -80
@@ -1,258 +1,258 @@
1
- import { UserError } from 'fastmcp';
2
- import { z } from 'zod';
3
- import { getCalendarClient } from '../../clients.js';
4
-
5
- export function register(server) {
6
- server.addTool({
7
- name: 'manage_event',
8
- description:
9
- 'Create, update, or delete a calendar event. Supports attendees, Google Meet, reminders, attachments, visibility, and transparency settings. IMPORTANT: To modify an existing event, always use action "update" with the existing event_id. Never delete and recreate an event to make changes — this destroys the event ID, attendee RSVPs, and sent notifications.',
10
- parameters: z.object({
11
- action: z.enum(['create', 'update', 'delete']).describe('The operation to perform. Use "update" to modify existing events — do not delete and recreate. "delete" should only be used to permanently remove an event, not to redo one.'),
12
- calendar_id: z
13
- .string()
14
- .optional()
15
- .default('primary')
16
- .describe('Calendar ID. Defaults to "primary".'),
17
- event_id: z
18
- .string()
19
- .optional()
20
- .describe('Event ID — required for update and delete.'),
21
- summary: z
22
- .string()
23
- .optional()
24
- .describe('Event title. Required for create.'),
25
- start_time: z
26
- .string()
27
- .optional()
28
- .describe('Start time in RFC3339 format (e.g. "2025-03-15T10:00:00-05:00") or date for all-day (e.g. "2025-03-15"). Required for create.'),
29
- end_time: z
30
- .string()
31
- .optional()
32
- .describe('End time in RFC3339 format or date for all-day. Required for create.'),
33
- description: z.string().optional().describe('Event description/body.'),
34
- location: z.string().optional().describe('Event location.'),
35
- attendees: z
36
- .array(z.string())
37
- .optional()
38
- .describe('List of attendee email addresses.'),
39
- timezone: z
40
- .string()
41
- .optional()
42
- .describe('Timezone for the event (e.g. "America/New_York"). Defaults to calendar timezone.'),
43
- add_google_meet: z
44
- .boolean()
45
- .optional()
46
- .describe('If true, adds a Google Meet conference link. If false on update, removes it.'),
47
- reminders: z
48
- .array(
49
- z.object({
50
- method: z.enum(['email', 'popup']).describe('Reminder method.'),
51
- minutes: z.number().describe('Minutes before the event to send reminder.'),
52
- })
53
- )
54
- .optional()
55
- .describe('Custom reminders. Overrides calendar defaults.'),
56
- transparency: z
57
- .enum(['opaque', 'transparent'])
58
- .optional()
59
- .describe('"opaque" = busy (default), "transparent" = free/available.'),
60
- visibility: z
61
- .enum(['default', 'public', 'private', 'confidential'])
62
- .optional()
63
- .describe('Event visibility.'),
64
- color_id: z
65
- .string()
66
- .optional()
67
- .describe('Color ID for the event (1-11). Use list_calendars or Google Calendar docs for color mapping.'),
68
- recurrence: z
69
- .array(z.string())
70
- .optional()
71
- .describe('RRULE recurrence rules (e.g. ["RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR"]). Only for create.'),
72
- send_updates: z
73
- .enum(['all', 'externalOnly', 'none'])
74
- .optional()
75
- .default('all')
76
- .describe('Who to send email notifications to: "all" = all attendees (default), "externalOnly" = non-Google Calendar attendees only, "none" = no emails.'),
77
- }),
78
- execute: async (args, { log }) => {
79
- const calendar = await getCalendarClient();
80
-
81
- try {
82
- if (args.action === 'delete') {
83
- if (!args.event_id) throw new UserError('event_id is required for delete.');
84
- log.info(`Deleting event ${args.event_id} from ${args.calendar_id}`);
85
- await calendar.events.delete({
86
- calendarId: args.calendar_id,
87
- eventId: args.event_id,
88
- sendUpdates: args.send_updates,
89
- });
90
- return JSON.stringify({ success: true, message: `Event ${args.event_id} deleted.` });
91
- }
92
-
93
- if (args.action === 'create') {
94
- if (!args.summary) throw new UserError('summary is required for create.');
95
- if (!args.start_time) throw new UserError('start_time is required for create.');
96
- if (!args.end_time) throw new UserError('end_time is required for create.');
97
-
98
- const eventBody = buildEventBody(args);
99
- log.info(`Creating event "${args.summary}" on ${args.calendar_id}`);
100
-
101
- const params = {
102
- calendarId: args.calendar_id,
103
- requestBody: eventBody,
104
- conferenceDataVersion: args.add_google_meet ? 1 : 0,
105
- sendUpdates: args.send_updates,
106
- };
107
-
108
- const response = await calendar.events.insert(params);
109
- return JSON.stringify(formatResult('created', response.data), null, 2);
110
- }
111
-
112
- if (args.action === 'update') {
113
- if (!args.event_id) throw new UserError('event_id is required for update.');
114
- log.info(`Updating event ${args.event_id} on ${args.calendar_id}`);
115
-
116
- // Fetch existing event to preserve fields
117
- const existing = await calendar.events.get({
118
- calendarId: args.calendar_id,
119
- eventId: args.event_id,
120
- });
121
-
122
- const eventBody = buildUpdateBody(existing.data, args);
123
-
124
- const params = {
125
- calendarId: args.calendar_id,
126
- eventId: args.event_id,
127
- requestBody: eventBody,
128
- conferenceDataVersion: args.add_google_meet !== undefined ? 1 : 0,
129
- sendUpdates: args.send_updates,
130
- };
131
-
132
- const response = await calendar.events.update(params);
133
- return JSON.stringify(formatResult('updated', response.data), null, 2);
134
- }
135
-
136
- throw new UserError(`Unknown action: ${args.action}`);
137
- } catch (error) {
138
- if (error instanceof UserError) throw error;
139
- log.error(`Error in manage_event (${args.action}): ${error.message || error}`);
140
- if (error.code === 404)
141
- throw new UserError('Calendar or event not found. Check the IDs.');
142
- if (error.code === 403)
143
- throw new UserError('Permission denied. You may not have edit access to this calendar.');
144
- throw new UserError(`Failed to ${args.action} event: ${error.message || 'Unknown error'}`);
145
- }
146
- },
147
- });
148
- }
149
-
150
- function buildTimeField(timeStr, timezone) {
151
- // All-day events use 'date', timed events use 'dateTime'
152
- if (/^\d{4}-\d{2}-\d{2}$/.test(timeStr)) {
153
- return { date: timeStr };
154
- }
155
- const field = { dateTime: timeStr };
156
- if (timezone) field.timeZone = timezone;
157
- return field;
158
- }
159
-
160
- function buildEventBody(args) {
161
- const body = {
162
- summary: args.summary,
163
- start: buildTimeField(args.start_time, args.timezone),
164
- end: buildTimeField(args.end_time, args.timezone),
165
- };
166
-
167
- if (args.description) body.description = args.description;
168
- if (args.location) body.location = args.location;
169
- if (args.attendees) body.attendees = args.attendees.map((email) => ({ email }));
170
- if (args.transparency) body.transparency = args.transparency;
171
- if (args.visibility) body.visibility = args.visibility;
172
- if (args.color_id) body.colorId = args.color_id;
173
- if (args.recurrence) body.recurrence = args.recurrence;
174
-
175
- if (args.reminders) {
176
- body.reminders = {
177
- useDefault: false,
178
- overrides: args.reminders,
179
- };
180
- }
181
-
182
- if (args.add_google_meet) {
183
- body.conferenceData = {
184
- createRequest: {
185
- requestId: `meet-${Date.now()}`,
186
- conferenceSolutionKey: { type: 'hangoutsMeet' },
187
- },
188
- };
189
- }
190
-
191
- return body;
192
- }
193
-
194
- function buildUpdateBody(existing, args) {
195
- const body = { ...existing };
196
-
197
- // Remove read-only fields
198
- delete body.etag;
199
- delete body.kind;
200
- delete body.created;
201
- delete body.updated;
202
- delete body.creator;
203
- delete body.organizer;
204
- delete body.iCalUID;
205
- delete body.sequence;
206
- delete body.hangoutLink;
207
-
208
- if (args.summary !== undefined) body.summary = args.summary;
209
- if (args.description !== undefined) body.description = args.description;
210
- if (args.location !== undefined) body.location = args.location;
211
- if (args.transparency !== undefined) body.transparency = args.transparency;
212
- if (args.visibility !== undefined) body.visibility = args.visibility;
213
- if (args.color_id !== undefined) body.colorId = args.color_id;
214
-
215
- if (args.start_time) body.start = buildTimeField(args.start_time, args.timezone);
216
- if (args.end_time) body.end = buildTimeField(args.end_time, args.timezone);
217
-
218
- if (args.attendees) body.attendees = args.attendees.map((email) => ({ email }));
219
-
220
- if (args.reminders) {
221
- body.reminders = {
222
- useDefault: false,
223
- overrides: args.reminders,
224
- };
225
- }
226
-
227
- if (args.add_google_meet === true) {
228
- body.conferenceData = {
229
- createRequest: {
230
- requestId: `meet-${Date.now()}`,
231
- conferenceSolutionKey: { type: 'hangoutsMeet' },
232
- },
233
- };
234
- } else if (args.add_google_meet === false) {
235
- body.conferenceData = null;
236
- }
237
-
238
- return body;
239
- }
240
-
241
- function formatResult(action, event) {
242
- const result = {
243
- success: true,
244
- action,
245
- id: event.id,
246
- summary: event.summary,
247
- start: event.start?.dateTime || event.start?.date,
248
- end: event.end?.dateTime || event.end?.date,
249
- htmlLink: event.htmlLink,
250
- };
251
-
252
- if (event.hangoutLink) result.meetLink = event.hangoutLink;
253
- if (event.attendees?.length) {
254
- result.attendees = event.attendees.map((a) => a.email);
255
- }
256
-
257
- return result;
258
- }
1
+ import { UserError } from 'fastmcp';
2
+ import { z } from 'zod';
3
+ import { getCalendarClient } from '../../clients.js';
4
+
5
+ export function register(server) {
6
+ server.addTool({
7
+ name: 'manageEvent',
8
+ description:
9
+ 'Create, update, or delete a calendar event. Supports attendees, Google Meet, reminders, attachments, visibility, and transparency settings. IMPORTANT: To modify an existing event, always use action "update" with the existing event_id. Never delete and recreate an event to make changes — this destroys the event ID, attendee RSVPs, and sent notifications.',
10
+ parameters: z.object({
11
+ action: z.enum(['create', 'update', 'delete']).describe('The operation to perform. Use "update" to modify existing events — do not delete and recreate. "delete" should only be used to permanently remove an event, not to redo one.'),
12
+ calendar_id: z
13
+ .string()
14
+ .optional()
15
+ .default('primary')
16
+ .describe('Calendar ID. Defaults to "primary".'),
17
+ event_id: z
18
+ .string()
19
+ .optional()
20
+ .describe('Event ID — required for update and delete.'),
21
+ summary: z
22
+ .string()
23
+ .optional()
24
+ .describe('Event title. Required for create.'),
25
+ start_time: z
26
+ .string()
27
+ .optional()
28
+ .describe('Start time in RFC3339 format (e.g. "2025-03-15T10:00:00-05:00") or date for all-day (e.g. "2025-03-15"). Required for create.'),
29
+ end_time: z
30
+ .string()
31
+ .optional()
32
+ .describe('End time in RFC3339 format or date for all-day. Required for create.'),
33
+ description: z.string().optional().describe('Event description/body.'),
34
+ location: z.string().optional().describe('Event location.'),
35
+ attendees: z
36
+ .array(z.string())
37
+ .optional()
38
+ .describe('List of attendee email addresses.'),
39
+ timezone: z
40
+ .string()
41
+ .optional()
42
+ .describe('Timezone for the event (e.g. "America/New_York"). Defaults to calendar timezone.'),
43
+ add_google_meet: z
44
+ .boolean()
45
+ .optional()
46
+ .describe('If true, adds a Google Meet conference link. If false on update, removes it.'),
47
+ reminders: z
48
+ .array(
49
+ z.object({
50
+ method: z.enum(['email', 'popup']).describe('Reminder method.'),
51
+ minutes: z.number().describe('Minutes before the event to send reminder.'),
52
+ })
53
+ )
54
+ .optional()
55
+ .describe('Custom reminders. Overrides calendar defaults.'),
56
+ transparency: z
57
+ .enum(['opaque', 'transparent'])
58
+ .optional()
59
+ .describe('"opaque" = busy (default), "transparent" = free/available.'),
60
+ visibility: z
61
+ .enum(['default', 'public', 'private', 'confidential'])
62
+ .optional()
63
+ .describe('Event visibility.'),
64
+ color_id: z
65
+ .string()
66
+ .optional()
67
+ .describe('Color ID for the event (1-11). Use listCalendars or Google Calendar docs for color mapping.'),
68
+ recurrence: z
69
+ .array(z.string())
70
+ .optional()
71
+ .describe('RRULE recurrence rules (e.g. ["RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR"]). Only for create.'),
72
+ send_updates: z
73
+ .enum(['all', 'externalOnly', 'none'])
74
+ .optional()
75
+ .default('all')
76
+ .describe('Who to send email notifications to: "all" = all attendees (default), "externalOnly" = non-Google Calendar attendees only, "none" = no emails.'),
77
+ }),
78
+ execute: async (args, { log }) => {
79
+ const calendar = await getCalendarClient();
80
+
81
+ try {
82
+ if (args.action === 'delete') {
83
+ if (!args.event_id) throw new UserError('event_id is required for delete.');
84
+ log.info(`Deleting event ${args.event_id} from ${args.calendar_id}`);
85
+ await calendar.events.delete({
86
+ calendarId: args.calendar_id,
87
+ eventId: args.event_id,
88
+ sendUpdates: args.send_updates,
89
+ });
90
+ return JSON.stringify({ success: true, message: `Event ${args.event_id} deleted.` });
91
+ }
92
+
93
+ if (args.action === 'create') {
94
+ if (!args.summary) throw new UserError('summary is required for create.');
95
+ if (!args.start_time) throw new UserError('start_time is required for create.');
96
+ if (!args.end_time) throw new UserError('end_time is required for create.');
97
+
98
+ const eventBody = buildEventBody(args);
99
+ log.info(`Creating event "${args.summary}" on ${args.calendar_id}`);
100
+
101
+ const params = {
102
+ calendarId: args.calendar_id,
103
+ requestBody: eventBody,
104
+ conferenceDataVersion: args.add_google_meet ? 1 : 0,
105
+ sendUpdates: args.send_updates,
106
+ };
107
+
108
+ const response = await calendar.events.insert(params);
109
+ return JSON.stringify(formatResult('created', response.data), null, 2);
110
+ }
111
+
112
+ if (args.action === 'update') {
113
+ if (!args.event_id) throw new UserError('event_id is required for update.');
114
+ log.info(`Updating event ${args.event_id} on ${args.calendar_id}`);
115
+
116
+ // Fetch existing event to preserve fields
117
+ const existing = await calendar.events.get({
118
+ calendarId: args.calendar_id,
119
+ eventId: args.event_id,
120
+ });
121
+
122
+ const eventBody = buildUpdateBody(existing.data, args);
123
+
124
+ const params = {
125
+ calendarId: args.calendar_id,
126
+ eventId: args.event_id,
127
+ requestBody: eventBody,
128
+ conferenceDataVersion: args.add_google_meet !== undefined ? 1 : 0,
129
+ sendUpdates: args.send_updates,
130
+ };
131
+
132
+ const response = await calendar.events.update(params);
133
+ return JSON.stringify(formatResult('updated', response.data), null, 2);
134
+ }
135
+
136
+ throw new UserError(`Unknown action: ${args.action}`);
137
+ } catch (error) {
138
+ if (error instanceof UserError) throw error;
139
+ log.error(`Error in manageEvent (${args.action}): ${error.message || error}`);
140
+ if (error.code === 404)
141
+ throw new UserError('Calendar or event not found. Check the IDs.');
142
+ if (error.code === 403)
143
+ throw new UserError('Permission denied. You may not have edit access to this calendar.');
144
+ throw new UserError(`Failed to ${args.action} event: ${error.message || 'Unknown error'}`);
145
+ }
146
+ },
147
+ });
148
+ }
149
+
150
+ function buildTimeField(timeStr, timezone) {
151
+ // All-day events use 'date', timed events use 'dateTime'
152
+ if (/^\d{4}-\d{2}-\d{2}$/.test(timeStr)) {
153
+ return { date: timeStr };
154
+ }
155
+ const field = { dateTime: timeStr };
156
+ if (timezone) field.timeZone = timezone;
157
+ return field;
158
+ }
159
+
160
+ function buildEventBody(args) {
161
+ const body = {
162
+ summary: args.summary,
163
+ start: buildTimeField(args.start_time, args.timezone),
164
+ end: buildTimeField(args.end_time, args.timezone),
165
+ };
166
+
167
+ if (args.description) body.description = args.description;
168
+ if (args.location) body.location = args.location;
169
+ if (args.attendees) body.attendees = args.attendees.map((email) => ({ email }));
170
+ if (args.transparency) body.transparency = args.transparency;
171
+ if (args.visibility) body.visibility = args.visibility;
172
+ if (args.color_id) body.colorId = args.color_id;
173
+ if (args.recurrence) body.recurrence = args.recurrence;
174
+
175
+ if (args.reminders) {
176
+ body.reminders = {
177
+ useDefault: false,
178
+ overrides: args.reminders,
179
+ };
180
+ }
181
+
182
+ if (args.add_google_meet) {
183
+ body.conferenceData = {
184
+ createRequest: {
185
+ requestId: `meet-${Date.now()}`,
186
+ conferenceSolutionKey: { type: 'hangoutsMeet' },
187
+ },
188
+ };
189
+ }
190
+
191
+ return body;
192
+ }
193
+
194
+ function buildUpdateBody(existing, args) {
195
+ const body = { ...existing };
196
+
197
+ // Remove read-only fields
198
+ delete body.etag;
199
+ delete body.kind;
200
+ delete body.created;
201
+ delete body.updated;
202
+ delete body.creator;
203
+ delete body.organizer;
204
+ delete body.iCalUID;
205
+ delete body.sequence;
206
+ delete body.hangoutLink;
207
+
208
+ if (args.summary !== undefined) body.summary = args.summary;
209
+ if (args.description !== undefined) body.description = args.description;
210
+ if (args.location !== undefined) body.location = args.location;
211
+ if (args.transparency !== undefined) body.transparency = args.transparency;
212
+ if (args.visibility !== undefined) body.visibility = args.visibility;
213
+ if (args.color_id !== undefined) body.colorId = args.color_id;
214
+
215
+ if (args.start_time) body.start = buildTimeField(args.start_time, args.timezone);
216
+ if (args.end_time) body.end = buildTimeField(args.end_time, args.timezone);
217
+
218
+ if (args.attendees) body.attendees = args.attendees.map((email) => ({ email }));
219
+
220
+ if (args.reminders) {
221
+ body.reminders = {
222
+ useDefault: false,
223
+ overrides: args.reminders,
224
+ };
225
+ }
226
+
227
+ if (args.add_google_meet === true) {
228
+ body.conferenceData = {
229
+ createRequest: {
230
+ requestId: `meet-${Date.now()}`,
231
+ conferenceSolutionKey: { type: 'hangoutsMeet' },
232
+ },
233
+ };
234
+ } else if (args.add_google_meet === false) {
235
+ body.conferenceData = null;
236
+ }
237
+
238
+ return body;
239
+ }
240
+
241
+ function formatResult(action, event) {
242
+ const result = {
243
+ success: true,
244
+ action,
245
+ id: event.id,
246
+ summary: event.summary,
247
+ start: event.start?.dateTime || event.start?.date,
248
+ end: event.end?.dateTime || event.end?.date,
249
+ htmlLink: event.htmlLink,
250
+ };
251
+
252
+ if (event.hangoutLink) result.meetLink = event.hangoutLink;
253
+ if (event.attendees?.length) {
254
+ result.attendees = event.attendees.map((a) => a.email);
255
+ }
256
+
257
+ return result;
258
+ }
@@ -1,66 +1,66 @@
1
- import { UserError } from 'fastmcp';
2
- import { z } from 'zod';
3
- import { getCalendarClient } from '../../clients.js';
4
-
5
- export function register(server) {
6
- server.addTool({
7
- name: 'move_event',
8
- description:
9
- 'Moves an event from one calendar to another. The event is removed from the source calendar and added to the destination.',
10
- parameters: z.object({
11
- event_id: z.string().describe('The event ID to move.'),
12
- source_calendar_id: z
13
- .string()
14
- .optional()
15
- .default('primary')
16
- .describe('Calendar ID the event is currently in. Defaults to "primary".'),
17
- destination_calendar_id: z
18
- .string()
19
- .describe('Calendar ID to move the event to.'),
20
- send_updates: z
21
- .enum(['all', 'externalOnly', 'none'])
22
- .optional()
23
- .default('all')
24
- .describe('Who to send email notifications to: "all" = all attendees (default), "externalOnly" = non-Google Calendar attendees only, "none" = no emails.'),
25
- }),
26
- execute: async (args, { log }) => {
27
- const calendar = await getCalendarClient();
28
- log.info(
29
- `Moving event ${args.event_id} from ${args.source_calendar_id} to ${args.destination_calendar_id}`
30
- );
31
-
32
- try {
33
- const response = await calendar.events.move({
34
- calendarId: args.source_calendar_id,
35
- eventId: args.event_id,
36
- destination: args.destination_calendar_id,
37
- sendUpdates: args.send_updates,
38
- });
39
-
40
- const event = response.data;
41
- return JSON.stringify(
42
- {
43
- success: true,
44
- id: event.id,
45
- summary: event.summary,
46
- movedTo: args.destination_calendar_id,
47
- htmlLink: event.htmlLink,
48
- },
49
- null,
50
- 2
51
- );
52
- } catch (error) {
53
- log.error(`Error moving event: ${error.message || error}`);
54
- if (error.code === 404)
55
- throw new UserError(
56
- 'Event or calendar not found. Check the event_id and calendar IDs.'
57
- );
58
- if (error.code === 403)
59
- throw new UserError(
60
- 'Permission denied. You need edit access to both source and destination calendars.'
61
- );
62
- throw new UserError(`Failed to move event: ${error.message || 'Unknown error'}`);
63
- }
64
- },
65
- });
66
- }
1
+ import { UserError } from 'fastmcp';
2
+ import { z } from 'zod';
3
+ import { getCalendarClient } from '../../clients.js';
4
+
5
+ export function register(server) {
6
+ server.addTool({
7
+ name: 'moveEvent',
8
+ description:
9
+ 'Moves an event from one calendar to another. The event is removed from the source calendar and added to the destination.',
10
+ parameters: z.object({
11
+ event_id: z.string().describe('The event ID to move.'),
12
+ source_calendar_id: z
13
+ .string()
14
+ .optional()
15
+ .default('primary')
16
+ .describe('Calendar ID the event is currently in. Defaults to "primary".'),
17
+ destination_calendar_id: z
18
+ .string()
19
+ .describe('Calendar ID to move the event to.'),
20
+ send_updates: z
21
+ .enum(['all', 'externalOnly', 'none'])
22
+ .optional()
23
+ .default('all')
24
+ .describe('Who to send email notifications to: "all" = all attendees (default), "externalOnly" = non-Google Calendar attendees only, "none" = no emails.'),
25
+ }),
26
+ execute: async (args, { log }) => {
27
+ const calendar = await getCalendarClient();
28
+ log.info(
29
+ `Moving event ${args.event_id} from ${args.source_calendar_id} to ${args.destination_calendar_id}`
30
+ );
31
+
32
+ try {
33
+ const response = await calendar.events.move({
34
+ calendarId: args.source_calendar_id,
35
+ eventId: args.event_id,
36
+ destination: args.destination_calendar_id,
37
+ sendUpdates: args.send_updates,
38
+ });
39
+
40
+ const event = response.data;
41
+ return JSON.stringify(
42
+ {
43
+ success: true,
44
+ id: event.id,
45
+ summary: event.summary,
46
+ movedTo: args.destination_calendar_id,
47
+ htmlLink: event.htmlLink,
48
+ },
49
+ null,
50
+ 2
51
+ );
52
+ } catch (error) {
53
+ log.error(`Error moving event: ${error.message || error}`);
54
+ if (error.code === 404)
55
+ throw new UserError(
56
+ 'Event or calendar not found. Check the event_id and calendar IDs.'
57
+ );
58
+ if (error.code === 403)
59
+ throw new UserError(
60
+ 'Permission denied. You need edit access to both source and destination calendars.'
61
+ );
62
+ throw new UserError(`Failed to move event: ${error.message || 'Unknown error'}`);
63
+ }
64
+ },
65
+ });
66
+ }