@@ -7,6 +7,7 @@ import dotenv from 'dotenv';
7
7
import fs from 'fs' ;
8
8
import path from 'path' ;
9
9
import { fileURLToPath } from 'url' ;
10
+ import { z } from 'zod' ;
10
11
11
12
// Load environment variables
12
13
dotenv . config ( ) ;
@@ -52,12 +53,7 @@ server.tool(
52
53
async ( ) => {
53
54
if ( authenticated ) {
54
55
return {
55
- content : [
56
- {
57
- type : "text" ,
58
- text : "Already authenticated with Google Calendar."
59
- }
60
- ]
56
+ content : [ { type : "text" , text : "Already authenticated with Google Calendar." } ]
61
57
} ;
62
58
}
63
59
@@ -82,10 +78,7 @@ server.tool(
82
78
"set-auth-code" ,
83
79
"Set the authorization code from Google OAuth flow" ,
84
80
{
85
- code : {
86
- type : "string" ,
87
- description : "The authorization code from Google OAuth redirect"
88
- }
81
+ code : z . string ( ) . describe ( "The authorization code from Google OAuth redirect" )
89
82
} ,
90
83
async ( { code } ) => {
91
84
try {
@@ -97,12 +90,7 @@ server.tool(
97
90
authenticated = true ;
98
91
99
92
return {
100
- content : [
101
- {
102
- type : "text" ,
103
- text : "Successfully authenticated with Google Calendar!"
104
- }
105
- ]
93
+ content : [ { type : "text" , text : "Successfully authenticated with Google Calendar!" } ]
106
94
} ;
107
95
} catch ( error ) {
108
96
console . error ( 'Error retrieving access token:' , error ) ;
@@ -124,18 +112,14 @@ server.tool(
124
112
"list-events" ,
125
113
"List upcoming calendar events" ,
126
114
{
127
- maxResults : {
128
- type : "number" ,
129
- description : "Maximum number of events to return (default: 10)" ,
130
- default : 10
131
- } ,
132
- timeMin : {
133
- type : "string" ,
134
- description : "Start time in ISO format (default: now)" ,
135
- default : new Date ( ) . toISOString ( )
136
- }
115
+ maxResults : z . number ( )
116
+ . describe ( "Maximum number of events to return" )
117
+ . default ( 10 ) ,
118
+ timeMin : z . string ( )
119
+ . describe ( "Start time in ISO format" )
120
+ . default ( ( ) => new Date ( ) . toISOString ( ) )
137
121
} ,
138
- async ( { maxResults = 10 , timeMin = new Date ( ) . toISOString ( ) } ) => {
122
+ async ( { maxResults, timeMin } ) => {
139
123
if ( ! authenticated ) {
140
124
return {
141
125
content : [
@@ -161,12 +145,7 @@ server.tool(
161
145
162
146
if ( events . length === 0 ) {
163
147
return {
164
- content : [
165
- {
166
- type : "text" ,
167
- text : "No upcoming events found."
168
- }
169
- ]
148
+ content : [ { type : "text" , text : "No upcoming events found." } ]
170
149
} ;
171
150
}
172
151
@@ -177,12 +156,7 @@ server.tool(
177
156
} ) . join ( '\n---\n\n' ) ;
178
157
179
158
return {
180
- content : [
181
- {
182
- type : "text" ,
183
- text : `Upcoming events:\n\n${ formattedEvents } `
184
- }
185
- ]
159
+ content : [ { type : "text" , text : `Upcoming events:\n\n${ formattedEvents } ` } ]
186
160
} ;
187
161
} catch ( error ) {
188
162
console . error ( 'Error retrieving events:' , error ) ;
@@ -204,34 +178,21 @@ server.tool(
204
178
"create-event" ,
205
179
"Create a new calendar event" ,
206
180
{
207
- summary : {
208
- type : "string" ,
209
- description : "Event title/summary"
210
- } ,
211
- description : {
212
- type : "string" ,
213
- description : "Event description (optional)"
214
- } ,
215
- location : {
216
- type : "string" ,
217
- description : "Event location (optional)"
218
- } ,
219
- startDateTime : {
220
- type : "string" ,
221
- description : "Start date and time in ISO format or YYYY-MM-DD format"
222
- } ,
223
- endDateTime : {
224
- type : "string" ,
225
- description : "End date and time in ISO format or YYYY-MM-DD format"
226
- } ,
227
- attendees : {
228
- type : "array" ,
229
- description : "List of attendee email addresses (optional)" ,
230
- items : {
231
- type : "string"
232
- } ,
233
- default : [ ]
234
- }
181
+ summary : z . string ( )
182
+ . describe ( "Event title/summary" ) ,
183
+ description : z . string ( )
184
+ . describe ( "Event description" )
185
+ . optional ( ) ,
186
+ location : z . string ( )
187
+ . describe ( "Event location" )
188
+ . optional ( ) ,
189
+ startDateTime : z . string ( )
190
+ . describe ( "Start date and time in ISO format (e.g., 2024-03-15T09:00:00Z) or YYYY-MM-DD format" ) ,
191
+ endDateTime : z . string ( )
192
+ . describe ( "End date and time in ISO format (e.g., 2024-03-15T10:00:00Z) or YYYY-MM-DD format" ) ,
193
+ attendees : z . array ( z . string ( ) )
194
+ . describe ( "List of attendee email addresses" )
195
+ . default ( [ ] )
235
196
} ,
236
197
async ( { summary, description, location, startDateTime, endDateTime, attendees = [ ] } ) => {
237
198
if ( ! authenticated ) {
@@ -247,23 +208,24 @@ server.tool(
247
208
}
248
209
249
210
try {
250
- // Determine if the dates are full ISO datetimes or just dates
251
- const isFullDay = ! startDateTime . includes ( 'T' ) && ! endDateTime . includes ( 'T' ) ;
252
-
211
+ // Create event object
253
212
const event : calendar_v3 . Schema$Event = {
254
213
summary,
255
214
description,
256
215
location,
257
- start : isFullDay
258
- ? { date : startDateTime }
259
- : { dateTime : startDateTime , timeZone : Intl . DateTimeFormat ( ) . resolvedOptions ( ) . timeZone } ,
260
- end : isFullDay
261
- ? { date : endDateTime }
262
- : { dateTime : endDateTime , timeZone : Intl . DateTimeFormat ( ) . resolvedOptions ( ) . timeZone } ,
216
+ start : {
217
+ dateTime : startDateTime . includes ( 'T' ) ? startDateTime : undefined ,
218
+ date : ! startDateTime . includes ( 'T' ) ? startDateTime : undefined ,
219
+ } ,
220
+ end : {
221
+ dateTime : endDateTime . includes ( 'T' ) ? endDateTime : undefined ,
222
+ date : ! endDateTime . includes ( 'T' ) ? endDateTime : undefined ,
223
+ } ,
263
224
} ;
264
225
265
- if ( attendees && attendees . length > 0 ) {
266
- event . attendees = attendees . map ( email => ( { email } ) ) ;
226
+ // Add attendees if provided
227
+ if ( attendees . length > 0 ) {
228
+ event . attendees = attendees . map ( ( email : string ) => ( { email } ) ) ;
267
229
}
268
230
269
231
const response = await calendar . events . insert ( {
@@ -275,7 +237,7 @@ server.tool(
275
237
content : [
276
238
{
277
239
type : "text" ,
278
- text : `Event created successfully! Event ID: ${ response . data . id } \nEvent link: ${ response . data . htmlLink } `
240
+ text : `Event created successfully! Event ID: ${ response . data . id } `
279
241
}
280
242
]
281
243
} ;
@@ -299,10 +261,8 @@ server.tool(
299
261
"delete-event" ,
300
262
"Delete a calendar event by ID" ,
301
263
{
302
- eventId : {
303
- type : "string" ,
304
- description : "ID of the event to delete"
305
- }
264
+ eventId : z . string ( )
265
+ . describe ( "ID of the event to delete" )
306
266
} ,
307
267
async ( { eventId } ) => {
308
268
if ( ! authenticated ) {
@@ -351,15 +311,11 @@ server.tool(
351
311
"search-events" ,
352
312
"Search for calendar events by query" ,
353
313
{
354
- query : {
355
- type : "string" ,
356
- description : "Search query (searches in title, description, location, etc.)"
357
- } ,
358
- maxResults : {
359
- type : "number" ,
360
- description : "Maximum number of events to return" ,
361
- default : 10
362
- }
314
+ query : z . string ( )
315
+ . describe ( "Search query (searches in title, description, location, etc.)" ) ,
316
+ maxResults : z . number ( )
317
+ . describe ( "Maximum number of events to return" )
318
+ . default ( 10 )
363
319
} ,
364
320
async ( { query, maxResults = 10 } ) => {
365
321
if ( ! authenticated ) {
0 commit comments