google-calendar-workspace-mcp-server 0.0.1
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/README.md +262 -0
- package/build/index.d.ts +2 -0
- package/build/index.integration-with-mock.d.ts +6 -0
- package/build/index.integration-with-mock.js +195 -0
- package/build/index.js +35 -0
- package/package.json +49 -0
- package/shared/calendar-client/lib/api-errors.d.ts +4 -0
- package/shared/calendar-client/lib/api-errors.js +25 -0
- package/shared/calendar-client/lib/create-event.d.ts +2 -0
- package/shared/calendar-client/lib/create-event.js +13 -0
- package/shared/calendar-client/lib/get-event.d.ts +2 -0
- package/shared/calendar-client/lib/get-event.js +9 -0
- package/shared/calendar-client/lib/list-calendars.d.ts +5 -0
- package/shared/calendar-client/lib/list-calendars.js +14 -0
- package/shared/calendar-client/lib/list-events.d.ts +10 -0
- package/shared/calendar-client/lib/list-events.js +24 -0
- package/shared/calendar-client/lib/query-freebusy.d.ts +2 -0
- package/shared/calendar-client/lib/query-freebusy.js +13 -0
- package/shared/index.d.ts +2 -0
- package/shared/index.js +2 -0
- package/shared/logging.d.ts +10 -0
- package/shared/logging.js +23 -0
- package/shared/server.d.ts +130 -0
- package/shared/server.js +132 -0
- package/shared/tools/create-event.d.ts +110 -0
- package/shared/tools/create-event.js +195 -0
- package/shared/tools/get-event.d.ts +44 -0
- package/shared/tools/get-event.js +154 -0
- package/shared/tools/list-calendars.d.ts +36 -0
- package/shared/tools/list-calendars.js +88 -0
- package/shared/tools/list-events.d.ts +79 -0
- package/shared/tools/list-events.js +176 -0
- package/shared/tools/query-freebusy.d.ts +61 -0
- package/shared/tools/query-freebusy.js +110 -0
- package/shared/tools.d.ts +3 -0
- package/shared/tools.js +41 -0
- package/shared/types.d.ts +111 -0
- package/shared/types.js +4 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { logError } from '../logging.js';
|
|
3
|
+
export const GetEventSchema = z.object({
|
|
4
|
+
calendar_id: z
|
|
5
|
+
.string()
|
|
6
|
+
.optional()
|
|
7
|
+
.default('primary')
|
|
8
|
+
.describe('Calendar ID containing the event. Defaults to "primary".'),
|
|
9
|
+
event_id: z.string().min(1).describe('The ID of the event to retrieve.'),
|
|
10
|
+
});
|
|
11
|
+
export function getEventTool(server, clientFactory) {
|
|
12
|
+
return {
|
|
13
|
+
name: 'gcal_get_event',
|
|
14
|
+
description: 'Retrieves detailed information about a specific calendar event by ID. ' +
|
|
15
|
+
'Returns full event details including title, time, location, attendees, description, recurrence rules, and reminders. ' +
|
|
16
|
+
'Use this when you need complete information about a particular event.',
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
calendar_id: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: GetEventSchema.shape.calendar_id.description,
|
|
23
|
+
},
|
|
24
|
+
event_id: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: GetEventSchema.shape.event_id.description,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
required: ['event_id'],
|
|
30
|
+
},
|
|
31
|
+
handler: async (args) => {
|
|
32
|
+
try {
|
|
33
|
+
const parsed = GetEventSchema.parse(args);
|
|
34
|
+
const client = clientFactory();
|
|
35
|
+
const event = await client.getEvent(parsed.calendar_id, parsed.event_id);
|
|
36
|
+
let output = `# Event Details\n\n`;
|
|
37
|
+
output += `## ${event.summary || '(No title)'}\n\n`;
|
|
38
|
+
output += `**Event ID:** ${event.id}\n`;
|
|
39
|
+
// Status
|
|
40
|
+
if (event.status) {
|
|
41
|
+
output += `**Status:** ${event.status}\n`;
|
|
42
|
+
}
|
|
43
|
+
// Start time
|
|
44
|
+
if (event.start?.dateTime) {
|
|
45
|
+
output += `**Start:** ${new Date(event.start.dateTime).toLocaleString()}`;
|
|
46
|
+
if (event.start.timeZone) {
|
|
47
|
+
output += ` (${event.start.timeZone})`;
|
|
48
|
+
}
|
|
49
|
+
output += '\n';
|
|
50
|
+
}
|
|
51
|
+
else if (event.start?.date) {
|
|
52
|
+
output += `**Start:** ${event.start.date} (All day)\n`;
|
|
53
|
+
}
|
|
54
|
+
// End time
|
|
55
|
+
if (event.end?.dateTime) {
|
|
56
|
+
output += `**End:** ${new Date(event.end.dateTime).toLocaleString()}`;
|
|
57
|
+
if (event.end.timeZone) {
|
|
58
|
+
output += ` (${event.end.timeZone})`;
|
|
59
|
+
}
|
|
60
|
+
output += '\n';
|
|
61
|
+
}
|
|
62
|
+
else if (event.end?.date) {
|
|
63
|
+
output += `**End:** ${event.end.date} (All day)\n`;
|
|
64
|
+
}
|
|
65
|
+
// Location
|
|
66
|
+
if (event.location) {
|
|
67
|
+
output += `**Location:** ${event.location}\n`;
|
|
68
|
+
}
|
|
69
|
+
// Creator
|
|
70
|
+
if (event.creator) {
|
|
71
|
+
output += `**Created By:** ${event.creator.displayName || event.creator.email}\n`;
|
|
72
|
+
}
|
|
73
|
+
// Organizer
|
|
74
|
+
if (event.organizer) {
|
|
75
|
+
output += `**Organizer:** ${event.organizer.displayName || event.organizer.email}\n`;
|
|
76
|
+
}
|
|
77
|
+
// Attendees
|
|
78
|
+
if (event.attendees && event.attendees.length > 0) {
|
|
79
|
+
output += `\n### Attendees (${event.attendees.length})\n\n`;
|
|
80
|
+
for (const attendee of event.attendees) {
|
|
81
|
+
const name = attendee.displayName || attendee.email;
|
|
82
|
+
const status = attendee.responseStatus || 'needsAction';
|
|
83
|
+
const optional = attendee.organizer ? ' [Organizer]' : '';
|
|
84
|
+
output += `- ${name} - **${status}**${optional}\n`;
|
|
85
|
+
}
|
|
86
|
+
output += '\n';
|
|
87
|
+
}
|
|
88
|
+
// Description
|
|
89
|
+
if (event.description) {
|
|
90
|
+
output += `### Description\n\n${event.description}\n\n`;
|
|
91
|
+
}
|
|
92
|
+
// Recurrence
|
|
93
|
+
if (event.recurrence && event.recurrence.length > 0) {
|
|
94
|
+
output += `### Recurrence\n\n`;
|
|
95
|
+
for (const rule of event.recurrence) {
|
|
96
|
+
output += `- ${rule}\n`;
|
|
97
|
+
}
|
|
98
|
+
output += '\n';
|
|
99
|
+
}
|
|
100
|
+
// Reminders
|
|
101
|
+
if (event.reminders) {
|
|
102
|
+
output += `### Reminders\n\n`;
|
|
103
|
+
if (event.reminders.useDefault) {
|
|
104
|
+
output += `Using default reminders\n\n`;
|
|
105
|
+
}
|
|
106
|
+
else if (event.reminders.overrides && event.reminders.overrides.length > 0) {
|
|
107
|
+
for (const reminder of event.reminders.overrides) {
|
|
108
|
+
output += `- ${reminder.method}: ${reminder.minutes} minutes before\n`;
|
|
109
|
+
}
|
|
110
|
+
output += '\n';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Visibility and transparency
|
|
114
|
+
if (event.visibility) {
|
|
115
|
+
output += `**Visibility:** ${event.visibility}\n`;
|
|
116
|
+
}
|
|
117
|
+
if (event.transparency) {
|
|
118
|
+
output += `**Show as:** ${event.transparency}\n`;
|
|
119
|
+
}
|
|
120
|
+
// Metadata
|
|
121
|
+
if (event.created) {
|
|
122
|
+
output += `**Created:** ${new Date(event.created).toLocaleString()}\n`;
|
|
123
|
+
}
|
|
124
|
+
if (event.updated) {
|
|
125
|
+
output += `**Updated:** ${new Date(event.updated).toLocaleString()}\n`;
|
|
126
|
+
}
|
|
127
|
+
// Link
|
|
128
|
+
if (event.htmlLink) {
|
|
129
|
+
output += `\n**Event Link:** ${event.htmlLink}\n`;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
content: [
|
|
133
|
+
{
|
|
134
|
+
type: 'text',
|
|
135
|
+
text: output,
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
logError('get-event-tool', error);
|
|
142
|
+
return {
|
|
143
|
+
content: [
|
|
144
|
+
{
|
|
145
|
+
type: 'text',
|
|
146
|
+
text: `Error getting event: ${error instanceof Error ? error.message : String(error)}`,
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
isError: true,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { ClientFactory } from '../server.js';
|
|
4
|
+
export declare const ListCalendarsSchema: z.ZodObject<{
|
|
5
|
+
max_results: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
6
|
+
}, "strip", z.ZodTypeAny, {
|
|
7
|
+
max_results: number;
|
|
8
|
+
}, {
|
|
9
|
+
max_results?: number | undefined;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function listCalendarsTool(server: Server, clientFactory: ClientFactory): {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: "object";
|
|
16
|
+
properties: {
|
|
17
|
+
max_results: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string | undefined;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
handler: (args: unknown) => Promise<{
|
|
24
|
+
content: {
|
|
25
|
+
type: string;
|
|
26
|
+
text: string;
|
|
27
|
+
}[];
|
|
28
|
+
isError?: undefined;
|
|
29
|
+
} | {
|
|
30
|
+
content: {
|
|
31
|
+
type: string;
|
|
32
|
+
text: string;
|
|
33
|
+
}[];
|
|
34
|
+
isError: boolean;
|
|
35
|
+
}>;
|
|
36
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { logError } from '../logging.js';
|
|
3
|
+
export const ListCalendarsSchema = z.object({
|
|
4
|
+
max_results: z
|
|
5
|
+
.number()
|
|
6
|
+
.positive()
|
|
7
|
+
.max(250)
|
|
8
|
+
.optional()
|
|
9
|
+
.default(50)
|
|
10
|
+
.describe('Maximum number of calendars to return. Defaults to 50, maximum is 250.'),
|
|
11
|
+
});
|
|
12
|
+
export function listCalendarsTool(server, clientFactory) {
|
|
13
|
+
return {
|
|
14
|
+
name: 'gcal_list_calendars',
|
|
15
|
+
description: 'Lists all calendars available to the authenticated user. ' +
|
|
16
|
+
'Returns calendar details including ID, name, description, time zone, and access role. ' +
|
|
17
|
+
'Use this to discover available calendars before querying events, or to see which calendars the user has access to.',
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
properties: {
|
|
21
|
+
max_results: {
|
|
22
|
+
type: 'number',
|
|
23
|
+
description: ListCalendarsSchema.shape.max_results.description,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
handler: async (args) => {
|
|
28
|
+
try {
|
|
29
|
+
const parsed = ListCalendarsSchema.parse(args);
|
|
30
|
+
const client = clientFactory();
|
|
31
|
+
const result = await client.listCalendars({
|
|
32
|
+
maxResults: parsed.max_results,
|
|
33
|
+
});
|
|
34
|
+
const calendars = result.items || [];
|
|
35
|
+
if (calendars.length === 0) {
|
|
36
|
+
return {
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: 'text',
|
|
40
|
+
text: 'No calendars found.',
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
let output = `# Available Calendars (${calendars.length} found)\n\n`;
|
|
46
|
+
for (const calendar of calendars) {
|
|
47
|
+
output += `## ${calendar.summary}\n\n`;
|
|
48
|
+
output += `**Calendar ID:** ${calendar.id}\n`;
|
|
49
|
+
if (calendar.description) {
|
|
50
|
+
output += `**Description:** ${calendar.description}\n`;
|
|
51
|
+
}
|
|
52
|
+
output += `**Time Zone:** ${calendar.timeZone}\n`;
|
|
53
|
+
output += `**Access Role:** ${calendar.accessRole}\n`;
|
|
54
|
+
if (calendar.primary) {
|
|
55
|
+
output += `**Primary:** Yes\n`;
|
|
56
|
+
}
|
|
57
|
+
if (calendar.selected !== undefined) {
|
|
58
|
+
output += `**Selected:** ${calendar.selected ? 'Yes' : 'No'}\n`;
|
|
59
|
+
}
|
|
60
|
+
if (calendar.backgroundColor) {
|
|
61
|
+
output += `**Background Color:** ${calendar.backgroundColor}\n`;
|
|
62
|
+
}
|
|
63
|
+
output += '\n---\n\n';
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: 'text',
|
|
69
|
+
text: output,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
logError('list-calendars-tool', error);
|
|
76
|
+
return {
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: 'text',
|
|
80
|
+
text: `Error listing calendars: ${error instanceof Error ? error.message : String(error)}`,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
isError: true,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { ClientFactory } from '../server.js';
|
|
4
|
+
export declare const ListEventsSchema: z.ZodObject<{
|
|
5
|
+
calendar_id: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
6
|
+
time_min: z.ZodOptional<z.ZodString>;
|
|
7
|
+
time_max: z.ZodOptional<z.ZodString>;
|
|
8
|
+
max_results: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
9
|
+
query: z.ZodOptional<z.ZodString>;
|
|
10
|
+
single_events: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
11
|
+
order_by: z.ZodOptional<z.ZodEnum<["startTime", "updated"]>>;
|
|
12
|
+
}, "strip", z.ZodTypeAny, {
|
|
13
|
+
calendar_id: string;
|
|
14
|
+
max_results: number;
|
|
15
|
+
single_events: boolean;
|
|
16
|
+
time_min?: string | undefined;
|
|
17
|
+
time_max?: string | undefined;
|
|
18
|
+
query?: string | undefined;
|
|
19
|
+
order_by?: "startTime" | "updated" | undefined;
|
|
20
|
+
}, {
|
|
21
|
+
calendar_id?: string | undefined;
|
|
22
|
+
time_min?: string | undefined;
|
|
23
|
+
time_max?: string | undefined;
|
|
24
|
+
max_results?: number | undefined;
|
|
25
|
+
query?: string | undefined;
|
|
26
|
+
single_events?: boolean | undefined;
|
|
27
|
+
order_by?: "startTime" | "updated" | undefined;
|
|
28
|
+
}>;
|
|
29
|
+
export declare function listEventsTool(server: Server, clientFactory: ClientFactory): {
|
|
30
|
+
name: string;
|
|
31
|
+
description: string;
|
|
32
|
+
inputSchema: {
|
|
33
|
+
type: "object";
|
|
34
|
+
properties: {
|
|
35
|
+
calendar_id: {
|
|
36
|
+
type: string;
|
|
37
|
+
description: string | undefined;
|
|
38
|
+
};
|
|
39
|
+
time_min: {
|
|
40
|
+
type: string;
|
|
41
|
+
description: string | undefined;
|
|
42
|
+
};
|
|
43
|
+
time_max: {
|
|
44
|
+
type: string;
|
|
45
|
+
description: string | undefined;
|
|
46
|
+
};
|
|
47
|
+
max_results: {
|
|
48
|
+
type: string;
|
|
49
|
+
description: string | undefined;
|
|
50
|
+
};
|
|
51
|
+
query: {
|
|
52
|
+
type: string;
|
|
53
|
+
description: string | undefined;
|
|
54
|
+
};
|
|
55
|
+
single_events: {
|
|
56
|
+
type: string;
|
|
57
|
+
description: string | undefined;
|
|
58
|
+
};
|
|
59
|
+
order_by: {
|
|
60
|
+
type: string;
|
|
61
|
+
enum: string[];
|
|
62
|
+
description: string | undefined;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
handler: (args: unknown) => Promise<{
|
|
67
|
+
content: {
|
|
68
|
+
type: string;
|
|
69
|
+
text: string;
|
|
70
|
+
}[];
|
|
71
|
+
isError?: undefined;
|
|
72
|
+
} | {
|
|
73
|
+
content: {
|
|
74
|
+
type: string;
|
|
75
|
+
text: string;
|
|
76
|
+
}[];
|
|
77
|
+
isError: boolean;
|
|
78
|
+
}>;
|
|
79
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { logError } from '../logging.js';
|
|
3
|
+
export const ListEventsSchema = z.object({
|
|
4
|
+
calendar_id: z
|
|
5
|
+
.string()
|
|
6
|
+
.optional()
|
|
7
|
+
.default('primary')
|
|
8
|
+
.describe('Calendar ID to list events from. Defaults to "primary" (user\'s primary calendar).'),
|
|
9
|
+
time_min: z
|
|
10
|
+
.string()
|
|
11
|
+
.optional()
|
|
12
|
+
.describe('Lower bound (inclusive) for event start time in RFC3339 format (e.g., "2024-01-01T00:00:00Z"). Defaults to current time if not specified.'),
|
|
13
|
+
time_max: z
|
|
14
|
+
.string()
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('Upper bound (exclusive) for event start time in RFC3339 format (e.g., "2024-12-31T23:59:59Z").'),
|
|
17
|
+
max_results: z
|
|
18
|
+
.number()
|
|
19
|
+
.positive()
|
|
20
|
+
.max(250)
|
|
21
|
+
.optional()
|
|
22
|
+
.default(10)
|
|
23
|
+
.describe('Maximum number of events to return. Defaults to 10, maximum is 250.'),
|
|
24
|
+
query: z.string().optional().describe('Free text search query to filter events.'),
|
|
25
|
+
single_events: z
|
|
26
|
+
.boolean()
|
|
27
|
+
.optional()
|
|
28
|
+
.default(true)
|
|
29
|
+
.describe('Whether to expand recurring events into instances. Defaults to true to show individual occurrences.'),
|
|
30
|
+
order_by: z
|
|
31
|
+
.enum(['startTime', 'updated'])
|
|
32
|
+
.optional()
|
|
33
|
+
.describe('Order of events. "startTime" orders by start time (requires single_events=true), "updated" orders by last modification time.'),
|
|
34
|
+
});
|
|
35
|
+
export function listEventsTool(server, clientFactory) {
|
|
36
|
+
return {
|
|
37
|
+
name: 'gcal_list_events',
|
|
38
|
+
description: 'Lists events from a Google Calendar within an optional time range. ' +
|
|
39
|
+
'Returns event details including title, time, location, attendees, and description. ' +
|
|
40
|
+
'Useful for checking upcoming meetings, finding events by search query, or reviewing a specific time period. ' +
|
|
41
|
+
'By default, shows the next 10 events from the primary calendar.',
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: 'object',
|
|
44
|
+
properties: {
|
|
45
|
+
calendar_id: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
description: ListEventsSchema.shape.calendar_id.description,
|
|
48
|
+
},
|
|
49
|
+
time_min: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
description: ListEventsSchema.shape.time_min.description,
|
|
52
|
+
},
|
|
53
|
+
time_max: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
description: ListEventsSchema.shape.time_max.description,
|
|
56
|
+
},
|
|
57
|
+
max_results: {
|
|
58
|
+
type: 'number',
|
|
59
|
+
description: ListEventsSchema.shape.max_results.description,
|
|
60
|
+
},
|
|
61
|
+
query: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
description: ListEventsSchema.shape.query.description,
|
|
64
|
+
},
|
|
65
|
+
single_events: {
|
|
66
|
+
type: 'boolean',
|
|
67
|
+
description: ListEventsSchema.shape.single_events.description,
|
|
68
|
+
},
|
|
69
|
+
order_by: {
|
|
70
|
+
type: 'string',
|
|
71
|
+
enum: ['startTime', 'updated'],
|
|
72
|
+
description: ListEventsSchema.shape.order_by.description,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
handler: async (args) => {
|
|
77
|
+
try {
|
|
78
|
+
const parsed = ListEventsSchema.parse(args);
|
|
79
|
+
const client = clientFactory();
|
|
80
|
+
const result = await client.listEvents(parsed.calendar_id, {
|
|
81
|
+
timeMin: parsed.time_min,
|
|
82
|
+
timeMax: parsed.time_max,
|
|
83
|
+
maxResults: parsed.max_results,
|
|
84
|
+
q: parsed.query,
|
|
85
|
+
singleEvents: parsed.single_events,
|
|
86
|
+
orderBy: parsed.order_by,
|
|
87
|
+
});
|
|
88
|
+
const events = result.items || [];
|
|
89
|
+
if (events.length === 0) {
|
|
90
|
+
return {
|
|
91
|
+
content: [
|
|
92
|
+
{
|
|
93
|
+
type: 'text',
|
|
94
|
+
text: 'No events found matching the criteria.',
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
let output = `# Calendar Events (${events.length} found)\n\n`;
|
|
100
|
+
output += `**Calendar:** ${result.summary}\n`;
|
|
101
|
+
output += `**Time Zone:** ${result.timeZone}\n\n`;
|
|
102
|
+
for (const event of events) {
|
|
103
|
+
output += `## ${event.summary || '(No title)'}\n\n`;
|
|
104
|
+
output += `**Event ID:** ${event.id}\n`;
|
|
105
|
+
// Start time
|
|
106
|
+
if (event.start?.dateTime) {
|
|
107
|
+
output += `**Start:** ${new Date(event.start.dateTime).toLocaleString()}\n`;
|
|
108
|
+
}
|
|
109
|
+
else if (event.start?.date) {
|
|
110
|
+
output += `**Start:** ${event.start.date} (All day)\n`;
|
|
111
|
+
}
|
|
112
|
+
// End time
|
|
113
|
+
if (event.end?.dateTime) {
|
|
114
|
+
output += `**End:** ${new Date(event.end.dateTime).toLocaleString()}\n`;
|
|
115
|
+
}
|
|
116
|
+
else if (event.end?.date) {
|
|
117
|
+
output += `**End:** ${event.end.date} (All day)\n`;
|
|
118
|
+
}
|
|
119
|
+
// Location
|
|
120
|
+
if (event.location) {
|
|
121
|
+
output += `**Location:** ${event.location}\n`;
|
|
122
|
+
}
|
|
123
|
+
// Status
|
|
124
|
+
if (event.status) {
|
|
125
|
+
output += `**Status:** ${event.status}\n`;
|
|
126
|
+
}
|
|
127
|
+
// Organizer
|
|
128
|
+
if (event.organizer) {
|
|
129
|
+
output += `**Organizer:** ${event.organizer.displayName || event.organizer.email}\n`;
|
|
130
|
+
}
|
|
131
|
+
// Attendees
|
|
132
|
+
if (event.attendees && event.attendees.length > 0) {
|
|
133
|
+
output += `**Attendees:** ${event.attendees.length}\n`;
|
|
134
|
+
for (const attendee of event.attendees) {
|
|
135
|
+
const name = attendee.displayName || attendee.email;
|
|
136
|
+
const status = attendee.responseStatus || 'needsAction';
|
|
137
|
+
output += ` - ${name} (${status})\n`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Description
|
|
141
|
+
if (event.description) {
|
|
142
|
+
const truncated = event.description.length > 200
|
|
143
|
+
? event.description.substring(0, 200) + '...'
|
|
144
|
+
: event.description;
|
|
145
|
+
output += `**Description:** ${truncated}\n`;
|
|
146
|
+
}
|
|
147
|
+
// Link
|
|
148
|
+
if (event.htmlLink) {
|
|
149
|
+
output += `**Link:** ${event.htmlLink}\n`;
|
|
150
|
+
}
|
|
151
|
+
output += '\n---\n\n';
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
content: [
|
|
155
|
+
{
|
|
156
|
+
type: 'text',
|
|
157
|
+
text: output,
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
logError('list-events-tool', error);
|
|
164
|
+
return {
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
167
|
+
type: 'text',
|
|
168
|
+
text: `Error listing events: ${error instanceof Error ? error.message : String(error)}`,
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
isError: true,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { ClientFactory } from '../server.js';
|
|
4
|
+
export declare const QueryFreebusySchema: z.ZodObject<{
|
|
5
|
+
time_min: z.ZodString;
|
|
6
|
+
time_max: z.ZodString;
|
|
7
|
+
calendar_ids: z.ZodArray<z.ZodString, "many">;
|
|
8
|
+
timezone: z.ZodOptional<z.ZodString>;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
time_min: string;
|
|
11
|
+
time_max: string;
|
|
12
|
+
calendar_ids: string[];
|
|
13
|
+
timezone?: string | undefined;
|
|
14
|
+
}, {
|
|
15
|
+
time_min: string;
|
|
16
|
+
time_max: string;
|
|
17
|
+
calendar_ids: string[];
|
|
18
|
+
timezone?: string | undefined;
|
|
19
|
+
}>;
|
|
20
|
+
export declare function queryFreebusyTool(server: Server, clientFactory: ClientFactory): {
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: "object";
|
|
25
|
+
properties: {
|
|
26
|
+
time_min: {
|
|
27
|
+
type: string;
|
|
28
|
+
description: string | undefined;
|
|
29
|
+
};
|
|
30
|
+
time_max: {
|
|
31
|
+
type: string;
|
|
32
|
+
description: string | undefined;
|
|
33
|
+
};
|
|
34
|
+
calendar_ids: {
|
|
35
|
+
type: string;
|
|
36
|
+
items: {
|
|
37
|
+
type: string;
|
|
38
|
+
};
|
|
39
|
+
description: string | undefined;
|
|
40
|
+
};
|
|
41
|
+
timezone: {
|
|
42
|
+
type: string;
|
|
43
|
+
description: string | undefined;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
required: string[];
|
|
47
|
+
};
|
|
48
|
+
handler: (args: unknown) => Promise<{
|
|
49
|
+
content: {
|
|
50
|
+
type: string;
|
|
51
|
+
text: string;
|
|
52
|
+
}[];
|
|
53
|
+
isError?: undefined;
|
|
54
|
+
} | {
|
|
55
|
+
content: {
|
|
56
|
+
type: string;
|
|
57
|
+
text: string;
|
|
58
|
+
}[];
|
|
59
|
+
isError: boolean;
|
|
60
|
+
}>;
|
|
61
|
+
};
|