find_free_slots
Find available meeting times in your Outlook calendar by specifying date range, duration, and work hours.
Instructions
Find available time slots in the calendar
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| startDate | Yes | Start date (ISO 8601 format) | |
| endDate | No | End date (ISO 8601 format, optional, defaults to 7 days from start) | |
| duration | No | Duration in minutes (optional, defaults to 30) | |
| workDayStart | No | Work day start hour (0-23) (optional, defaults to 9) | |
| workDayEnd | No | Work day end hour (0-23) (optional, defaults to 17) | |
| calendar | No | Calendar name (optional) |
Implementation Reference
- src/outlook-manager.ts:875-990 (handler)The primary handler function for the 'find_free_slots' tool. It constructs a PowerShell script to interact with Outlook via COM, retrieves busy events in the specified date range, calculates free slots of the desired duration during work hours (default 9-17), skips weekends, and returns an array of available {Start, End} slots.async findFreeSlots(options: { startDate: Date; endDate?: Date; duration?: number; workDayStart?: number; workDayEnd?: number; calendar?: string; }): Promise<any[]> { try { const endDate = options.endDate || new Date(options.startDate.getTime() + 7 * 24 * 60 * 60 * 1000); const duration = options.duration || 30; const workDayStart = options.workDayStart || 9; const workDayEnd = options.workDayEnd || 17; const calendarName = options.calendar || ''; const script = ` try { Add-Type -AssemblyName "Microsoft.Office.Interop.Outlook" -ErrorAction Stop $outlook = New-Object -ComObject Outlook.Application -ErrorAction Stop $namespace = $outlook.GetNamespace("MAPI") # Get calendar ${calendarName ? ` $calendar = $null foreach ($folder in $namespace.Folders) { if ($folder.Name -eq "${calendarName.replace(/"/g, '""')}") { $calendar = $folder.Folders("Calendar") break } } if (-not $calendar) { throw "Calendar not found: ${calendarName.replace(/"/g, '""')}" } ` : ` $calendar = $namespace.GetDefaultFolder(9) `} # Get all events in date range $startDate = [DateTime]"${options.startDate.toISOString()}" $endDate = [DateTime]"${endDate.toISOString()}" $filter = "[Start] >= '$($startDate.ToString('g'))' AND [End] <= '$($endDate.AddDays(1).ToString('g'))'" $items = $calendar.Items.Restrict($filter) # Build busy slots array $busySlots = @() foreach ($item in $items) { if ($item.BusyStatus -eq 2 -or $item.BusyStatus -eq 3) { # Busy or OutOfOffice $busySlots += [PSCustomObject]@{ Start = $item.Start End = $item.End } } } # Find free slots $freeSlots = @() $currentDate = $startDate.Date while ($currentDate -le $endDate.Date) { # Skip weekends $dayOfWeek = $currentDate.DayOfWeek if ($dayOfWeek -ne [DayOfWeek]::Saturday -and $dayOfWeek -ne [DayOfWeek]::Sunday) { $slotStart = $currentDate.AddHours(${workDayStart}) $workDayEndTime = $currentDate.AddHours(${workDayEnd}) while ($slotStart.AddMinutes(${duration}) -le $workDayEndTime) { $slotEnd = $slotStart.AddMinutes(${duration}) # Check if slot is free $isFree = $true foreach ($busy in $busySlots) { if ($slotStart -lt $busy.End -and $slotEnd -gt $busy.Start) { $isFree = $false break } } if ($isFree) { $freeSlots += [PSCustomObject]@{ Start = $slotStart.ToString("yyyy-MM-ddTHH:mm:ss") End = $slotEnd.ToString("yyyy-MM-ddTHH:mm:ss") } } $slotStart = $slotStart.AddMinutes(30) } } $currentDate = $currentDate.AddDays(1) } Write-Output ($freeSlots | ConvertTo-Json -Compress) } catch { Write-Output ([PSCustomObject]@{ Error = $_.Exception.Message } | ConvertTo-Json -Compress) } `; const result = await this.executePowerShell(script); const cleanResult = result.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, '').trim(); if (!cleanResult || cleanResult === '' || cleanResult === '[]') { return []; } const data = JSON.parse(cleanResult); if (data.Error) { throw new Error(data.Error); } return Array.isArray(data) ? data : [data]; } catch (error) { throw new Error(`Failed to find free slots: ${error instanceof Error ? error.message : String(error)}`); } }
- src/index.ts:397-428 (schema)The input schema and description for the 'find_free_slots' tool, defining parameters and validation as part of the MCP tools list.name: "find_free_slots", description: "Find available time slots in the calendar", inputSchema: { type: "object", properties: { startDate: { type: "string", description: "Start date (ISO 8601 format)" }, endDate: { type: "string", description: "End date (ISO 8601 format, optional, defaults to 7 days from start)" }, duration: { type: "number", description: "Duration in minutes (optional, defaults to 30)" }, workDayStart: { type: "number", description: "Work day start hour (0-23) (optional, defaults to 9)" }, workDayEnd: { type: "number", description: "Work day end hour (0-23) (optional, defaults to 17)" }, calendar: { type: "string", description: "Calendar name (optional)" } }, required: ["startDate"] }
- src/index.ts:779-800 (registration)MCP server request handler case that dispatches 'find_free_slots' tool calls to the outlookManager implementation and formats the response.case 'find_free_slots': { const freeSlots = await outlookManager.findFreeSlots({ startDate: new Date((args as any)?.startDate), endDate: (args as any)?.endDate ? new Date((args as any)?.endDate) : undefined, duration: (args as any)?.duration, workDayStart: (args as any)?.workDayStart, workDayEnd: (args as any)?.workDayEnd, calendar: (args as any)?.calendar }); return { content: [ { type: 'text', text: `🆓 **Free Time Slots**\nTotal: ${freeSlots.length} slots found\n\n` + freeSlots.slice(0, 20).map((slot, index) => `${index + 1}. ${slot.Start} - ${slot.End}` ).join('\n') + (freeSlots.length > 20 ? `\n\n... and ${freeSlots.length - 20} more slots` : '') }, ], }; }