import { z } from "zod";
import { GraphClient } from "../graph-client.js";
export const createEventSchema = z.object({
subject: z.string().describe("Event subject/title"),
body: z.string().optional().describe("Event body content (HTML supported)"),
startDateTime: z.string().describe("Start date/time in ISO 8601 format (e.g., 2025-03-15T09:00:00)"),
startTimeZone: z.string().optional().default("UTC").describe("Time zone for start time (e.g., America/New_York, UTC)"),
endDateTime: z.string().describe("End date/time in ISO 8601 format (e.g., 2025-03-15T10:00:00)"),
endTimeZone: z.string().optional().default("UTC").describe("Time zone for end time (e.g., America/New_York, UTC)"),
location: z.string().optional().describe("Event location display name"),
attendees: z
.array(
z.object({
email: z.string().describe("Attendee email address"),
name: z.string().optional().describe("Attendee display name"),
type: z
.enum(["required", "optional", "resource"])
.optional()
.default("required")
.describe("Attendee type"),
})
)
.optional()
.describe("List of attendees"),
isAllDay: z.boolean().optional().default(false).describe("Whether this is an all-day event"),
isOnlineMeeting: z.boolean().optional().default(false).describe("Whether to create an online meeting (Teams)"),
showAs: z
.enum(["free", "tentative", "busy", "oof", "workingElsewhere", "unknown"])
.optional()
.default("busy")
.describe("How to show the time on the calendar"),
importance: z
.enum(["low", "normal", "high"])
.optional()
.default("normal")
.describe("Event importance level"),
sensitivity: z
.enum(["normal", "personal", "private", "confidential"])
.optional()
.default("normal")
.describe("Event sensitivity level"),
categories: z.array(z.string()).optional().describe("Event categories/labels"),
reminderMinutesBeforeStart: z.number().optional().default(15).describe("Reminder in minutes before start"),
calendarId: z.string().optional().describe("Calendar ID. If omitted, uses the default calendar."),
recurrence: z
.object({
pattern: z.object({
type: z
.enum(["daily", "weekly", "absoluteMonthly", "relativeMonthly", "absoluteYearly", "relativeYearly"])
.describe("Recurrence pattern type"),
interval: z.number().describe("Interval between occurrences"),
daysOfWeek: z
.array(z.enum(["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"]))
.optional()
.describe("Days of the week (for weekly recurrence)"),
dayOfMonth: z.number().optional().describe("Day of month (for monthly recurrence)"),
month: z.number().optional().describe("Month (for yearly recurrence)"),
index: z
.enum(["first", "second", "third", "fourth", "last"])
.optional()
.describe("Week index (for relative monthly/yearly)"),
}),
range: z.object({
type: z.enum(["endDate", "noEnd", "numbered"]).describe("Range type"),
startDate: z.string().describe("Start date of recurrence (YYYY-MM-DD)"),
endDate: z.string().optional().describe("End date of recurrence (YYYY-MM-DD, for endDate type)"),
numberOfOccurrences: z.number().optional().describe("Number of occurrences (for numbered type)"),
}),
})
.optional()
.describe("Recurrence pattern for recurring events"),
});
export const createEventTool = {
name: "create-event",
description:
"Create a new calendar event. Supports attendees, online meetings (Teams), recurrence patterns, reminders, and all event properties.",
inputSchema: {
type: "object" as const,
properties: {
subject: { type: "string", description: "Event subject/title" },
body: { type: "string", description: "Event body content (HTML supported)" },
startDateTime: {
type: "string",
description: "Start date/time in ISO 8601 format (e.g., 2025-03-15T09:00:00)",
},
startTimeZone: {
type: "string",
description: "Time zone for start time (e.g., America/New_York, UTC)",
},
endDateTime: {
type: "string",
description: "End date/time in ISO 8601 format (e.g., 2025-03-15T10:00:00)",
},
endTimeZone: {
type: "string",
description: "Time zone for end time (e.g., America/New_York, UTC)",
},
location: { type: "string", description: "Event location display name" },
attendees: {
type: "array",
description: "List of attendees",
items: {
type: "object",
properties: {
email: { type: "string", description: "Attendee email address" },
name: { type: "string", description: "Attendee display name" },
type: {
type: "string",
enum: ["required", "optional", "resource"],
description: "Attendee type",
},
},
required: ["email"],
},
},
isAllDay: { type: "boolean", description: "Whether this is an all-day event" },
isOnlineMeeting: {
type: "boolean",
description: "Whether to create an online meeting (Teams)",
},
showAs: {
type: "string",
enum: ["free", "tentative", "busy", "oof", "workingElsewhere", "unknown"],
description: "How to show the time on the calendar",
},
importance: {
type: "string",
enum: ["low", "normal", "high"],
description: "Event importance level",
},
sensitivity: {
type: "string",
enum: ["normal", "personal", "private", "confidential"],
description: "Event sensitivity level",
},
categories: {
type: "array",
items: { type: "string" },
description: "Event categories/labels",
},
reminderMinutesBeforeStart: {
type: "number",
description: "Reminder in minutes before start",
},
calendarId: {
type: "string",
description: "Calendar ID. If omitted, uses the default calendar.",
},
recurrence: {
type: "object",
description: "Recurrence pattern for recurring events",
properties: {
pattern: {
type: "object",
properties: {
type: {
type: "string",
enum: ["daily", "weekly", "absoluteMonthly", "relativeMonthly", "absoluteYearly", "relativeYearly"],
},
interval: { type: "number" },
daysOfWeek: { type: "array", items: { type: "string" } },
dayOfMonth: { type: "number" },
month: { type: "number" },
index: { type: "string", enum: ["first", "second", "third", "fourth", "last"] },
},
required: ["type", "interval"],
},
range: {
type: "object",
properties: {
type: { type: "string", enum: ["endDate", "noEnd", "numbered"] },
startDate: { type: "string" },
endDate: { type: "string" },
numberOfOccurrences: { type: "number" },
},
required: ["type", "startDate"],
},
},
required: ["pattern", "range"],
},
},
required: ["subject", "startDateTime", "endDateTime"],
},
};
export async function handleCreateEvent(
graph: GraphClient,
args: z.infer<typeof createEventSchema>
): Promise<string> {
const basePath = args.calendarId
? `/me/calendars/${args.calendarId}/events`
: "/me/events";
const eventBody: Record<string, unknown> = {
subject: args.subject,
start: {
dateTime: args.startDateTime,
timeZone: args.startTimeZone || "UTC",
},
end: {
dateTime: args.endDateTime,
timeZone: args.endTimeZone || "UTC",
},
isAllDay: args.isAllDay || false,
showAs: args.showAs || "busy",
importance: args.importance || "normal",
sensitivity: args.sensitivity || "normal",
isReminderOn: true,
reminderMinutesBeforeStart: args.reminderMinutesBeforeStart ?? 15,
};
if (args.body) {
eventBody.body = {
contentType: "HTML",
content: args.body,
};
}
if (args.location) {
eventBody.location = {
displayName: args.location,
};
}
if (args.attendees && args.attendees.length > 0) {
eventBody.attendees = args.attendees.map((a) => ({
emailAddress: {
address: a.email,
name: a.name || a.email,
},
type: a.type || "required",
}));
}
if (args.isOnlineMeeting) {
eventBody.isOnlineMeeting = true;
eventBody.onlineMeetingProvider = "teamsForBusiness";
}
if (args.categories) {
eventBody.categories = args.categories;
}
if (args.recurrence) {
eventBody.recurrence = args.recurrence;
}
const event = await graph.post<Record<string, unknown>>(basePath, eventBody);
return JSON.stringify(event, null, 2);
}