---
name: calendar-lookup
description: How to query calendar events and meetings correctly — tool priority and API pitfalls.
metadata:
  activation:
    keywords: [calendar, events, meeting today, next meeting, outlook calendar, tomorrow schedule, upcoming meetings, 今日日程, 明日日程, 会议查询]
    patterns:
      - "(?i)\\b(what|any|show|list|check|get|retrieve|fetch|find|look\\s*up)\\s+(my\\s+)?(meetings?|events?|calendar|schedule|agenda)\\b"
      - "(?i)\\b(meeting|event|schedule|agenda|calendar)s?\\s+(today|tomorrow|this week|next week|on\\s+\\w+day)\\b"
      - "(?i)(日程|会议|开会|几点开会|今天有什么会|明天.*日程|后天.*日程|明日安排)"
  requires:
    tools: [MCP_calendar]
---

# Calendar Lookup

Use this skill to query calendar events and meetings.

## Tool priority

**Co-primary — pick based on query shape, or run both in parallel**:

- `MCP_m365copilot_copilot_chat` — natural-language queries, cross-mail-and-calendar questions, summarization/decision-oriented ("what was decided in last week's budget review"), fuzzy references, or when the user wants a narrative answer.
- `MCP_calendar_ListEvents` — structured event metadata: attendee list, organizer, response tracking, exact times with explicit timezone, recurrence, specific time windows. Authoritative Graph data. Use when you need specific fields, need to chain into create/update, or when Substrate indexing lag could hurt (very recent changes).

**Secondary — mail context**:

- `MCP_mail_SearchMessagesQueryParameters` — OData/KQL mail search (fast, no indexing delay). Invite emails, cancellations, updates, webinar registrations, recap/minutes emails with attachments.
- `MCP_mail_SearchMessages` — natural-language mail search (Copilot-backed). Use only when the query is intent-based and can't be expressed as OData/KQL. Slow (up to 30s) and has indexing delay.

**Avoid**:

- `MCP_calendar_ListCalendarView` — returns false empties; use `ListEvents`.

## Core rules

1. Get user timezone first: `MCP_calendar_GetUserDateAndTimeZoneSettings(userIdentifier="me")`.
2. Pad the query window by ~1 day on each side when the user gave a date (not a precise time range) — avoids losing events near the boundary when times round across timezones.
3. Convert returned times to user timezone before replying.

## Read workflows

### Today / tomorrow / a date

Pick based on the user's intent (or run both in parallel and merge):

- **Natural-language / summary** ("what do I have today", "any conflicts tomorrow"):
  `MCP_m365copilot_copilot_chat` — quick narrative.
- **Structured listing / authoritative data** (need attendee roster, organizer, response status, exact times):
  1. Get mailbox timezone via `MCP_calendar_GetUserDateAndTimeZoneSettings`.
  2. Query `MCP_calendar_ListEvents` with:
     - `startDateTime = "YYYY-MM-DDT00:00:00"`
     - `endDateTime = "YYYY-MM-DDT23:59:59"`
     - `timeZone = mailbox timezone`
     - `select = "id,subject,start,end,location,organizer,isAllDay,showAs,type,recurrence"`

### Find a meeting by title

- **Fuzzy / NL title match**: `MCP_m365copilot_copilot_chat`.
- **Structured / exact filter**: `MCP_calendar_ListEvents` with `meetingTitle = partial title` and a reasonable time window.
- **When mail context matters** (invite body, registration confirmation, recap): add `MCP_mail_SearchMessagesQueryParameters`.

### Find meeting invite / reminder / recap

Prefer `MCP_mail_SearchMessagesQueryParameters` with OData/KQL. `queryParameters` must start with `?`:
- Invitations: `?$search="subject:Invitation"` (add `subject:会议邀请` for Chinese senders)
- Cancellations / updates: `?$search="subject:Canceled OR subject:Updated"`
- Webinar registrations / recaps with decks: `?$filter=hasAttachments eq true and receivedDateTime ge 2026-04-16T00:00:00Z&$top=25` (use an ISO-8601 UTC `YYYY-MM-DDTHH:MM:SSZ` cutoff)
- From known platforms: `?$search="from:noreply@events.microsoft.com"`

`$search` cannot be combined with `$filter`/`$orderBy`/`$skip`. If you need both keyword AND a time window, run two separate queries or filter the other axis client-side.

Filterable fields: `subject`, `from/emailAddress/address`, `sender/emailAddress/address`, `receivedDateTime`, `isRead`, `isDraft`, `hasAttachments`, `importance`, `parentFolderId`, `categories`. Recipient collections (`toRecipients`/`ccRecipients`/`bccRecipients`) are NOT `$filter`-able — use `?$search="to:..."` instead.

Use `MCP_mail_SearchMessages` (natural-language) only for intent-style queries that OData can't express (e.g. "emails needing my response about last week's meetings").

## Safe creation rule

1. Create the event with calendar APIs.
2. Immediately read it back.
3. Verify subject, start/end time, and timezone interpretation.
4. Only confirm to the user after verification.

## Safe modify/delete rule

1. Find the target with `MCP_m365copilot_copilot_chat` and/or a narrow `MCP_calendar_ListEvents` query.
2. If recurring, be cautious — exact instance resolution may be unreliable.
3. If exact target is unclear, use `clarify` once before taking action.

## Pitfalls

Calendar:
- `attendeeEmails` is not a strict filter — don't rely on it alone.
- `meetingTitle` is a partial-match filter only.
- Returned event times may come back in UTC even when another timezone is requested.
- Recurring instance lookup is unreliable — `ListEvents` may only expose the master.

Mail:
- `MCP_mail_SearchMessagesQueryParameters` does NOT support `meetingMessageType` (that property lives on the `EventMessage` Graph subtype and isn't exposed here). Filter by subject keywords instead.
- `MCP_mail_SearchMessages` (natural-language) may miss very recent messages due to Substrate indexing lag — prefer the OData path or `ListEvents` for "in the last few minutes" queries.

