Skip to content

Commit 1f68c28

Browse files
committedMar 20, 2025
Debugging and testing.
1 parent fadb607 commit 1f68c28

File tree

2 files changed

+53
-102
lines changed

2 files changed

+53
-102
lines changed
 

‎package.json

+5-10
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@
33
"version": "1.0.0",
44
"description": "",
55
"main": "index.js",
6+
"type": "module",
67
"scripts": {
7-
"test": "echo \"Error: no test specified\" && exit 1"
8+
"test": "echo \"Error: no test specified\" && exit 1",
9+
"build": "tsc",
10+
"start": "node build/index.js"
811
},
912
"repository": {
1013
"type": "git",
1114
"url": "git+https://github.com/arashiyama/mcp-google-calendar.git"
1215
},
1316
"keywords": [],
14-
"author": "",
17+
"author": "Jonathan Care <jonc@lacunae.org>",
1518
"license": "ISC",
16-
"type": "commonjs",
1719
"bugs": {
1820
"url": "https://github.com/arashiyama/mcp-google-calendar/issues"
1921
},
@@ -27,13 +29,6 @@
2729
"devDependencies": {
2830
"@types/node": "^22.13.10",
2931
"typescript": "^5.8.2"
30-
},
31-
{
32-
"type": "module",
33-
"scripts": {
34-
"build": "tsc",
35-
"start": "node build/index.js"
36-
}
3732
}
3833
}
3934

‎src/index.ts

+48-92
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import dotenv from 'dotenv';
77
import fs from 'fs';
88
import path from 'path';
99
import { fileURLToPath } from 'url';
10+
import { z } from 'zod';
1011

1112
// Load environment variables
1213
dotenv.config();
@@ -52,12 +53,7 @@ server.tool(
5253
async () => {
5354
if (authenticated) {
5455
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." }]
6157
};
6258
}
6359

@@ -82,10 +78,7 @@ server.tool(
8278
"set-auth-code",
8379
"Set the authorization code from Google OAuth flow",
8480
{
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")
8982
},
9083
async ({ code }) => {
9184
try {
@@ -97,12 +90,7 @@ server.tool(
9790
authenticated = true;
9891

9992
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!" }]
10694
};
10795
} catch (error) {
10896
console.error('Error retrieving access token:', error);
@@ -124,18 +112,14 @@ server.tool(
124112
"list-events",
125113
"List upcoming calendar events",
126114
{
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())
137121
},
138-
async ({ maxResults = 10, timeMin = new Date().toISOString() }) => {
122+
async ({ maxResults, timeMin }) => {
139123
if (!authenticated) {
140124
return {
141125
content: [
@@ -161,12 +145,7 @@ server.tool(
161145

162146
if (events.length === 0) {
163147
return {
164-
content: [
165-
{
166-
type: "text",
167-
text: "No upcoming events found."
168-
}
169-
]
148+
content: [{ type: "text", text: "No upcoming events found." }]
170149
};
171150
}
172151

@@ -177,12 +156,7 @@ server.tool(
177156
}).join('\n---\n\n');
178157

179158
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}` }]
186160
};
187161
} catch (error) {
188162
console.error('Error retrieving events:', error);
@@ -204,34 +178,21 @@ server.tool(
204178
"create-event",
205179
"Create a new calendar event",
206180
{
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([])
235196
},
236197
async ({ summary, description, location, startDateTime, endDateTime, attendees = [] }) => {
237198
if (!authenticated) {
@@ -247,23 +208,24 @@ server.tool(
247208
}
248209

249210
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
253212
const event: calendar_v3.Schema$Event = {
254213
summary,
255214
description,
256215
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+
},
263224
};
264225

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 }));
267229
}
268230

269231
const response = await calendar.events.insert({
@@ -275,7 +237,7 @@ server.tool(
275237
content: [
276238
{
277239
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}`
279241
}
280242
]
281243
};
@@ -299,10 +261,8 @@ server.tool(
299261
"delete-event",
300262
"Delete a calendar event by ID",
301263
{
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")
306266
},
307267
async ({ eventId }) => {
308268
if (!authenticated) {
@@ -351,15 +311,11 @@ server.tool(
351311
"search-events",
352312
"Search for calendar events by query",
353313
{
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)
363319
},
364320
async ({ query, maxResults = 10 }) => {
365321
if (!authenticated) {

0 commit comments

Comments
 (0)
Failed to load comments.