| name | o365-manager |
| description | Expert in managing Office 365 professional emails and calendar. **Use this skill ONLY for professional emails when the user explicitly mentions 'mail professionnel', 'email professionnel', , 'professional email', 'Office 365', 'Outlook', or for calendar/meeting/events operations ('calendar', 'event', 'meeting', 'appointment', 'schedule', 'agenda', 'show my agenda', 'my agenda for', 'accept meeting', 'decline meeting', 'tentative', 'respond to meeting', 'create event', 'schedule meeting', 'check calendar', 'upcoming events'). |
Office 365 Manager Skill
Expert in managing Office 365 emails and calendar using Microsoft Graph API with OAuth 2.1 PKCE authentication.
Use this skill whenever the user mentions:
- Professional emails ONLY: "mail professionnel", "email professionnel", "Office 365", "Outlook"
- Calendar operations (always professional): "calendar", "event", "meeting", "appointment", "schedule"
- Agenda queries: "show my agenda", "my agenda for", "what's on my calendar", "my schedule for"
- Meeting responses: "accept meeting", "decline meeting", "tentative", "respond to meeting"
- "create event", "schedule meeting", "check calendar", "upcoming events"
DO NOT use for personal emails - use email-manager skill instead for all personal email operations (send, read, list, etc.)
CRITICAL: Prerequisites
The tool is a compiled Go binary that requires environment variables to be set:
export O365_CLIENT_ID=76d42bf5-d461-4274-bd4d-a02576b9df36
export O365_TENANT_ID=e4e1abd9-eac7-4a71-ab52-da5c998aa7ba
IMPORTANT: Always set these environment variables before running any commands. If you get an error about missing credentials, prefix your command with the exports:
export O365_CLIENT_ID=76d42bf5-d461-4274-bd4d-a02576b9df36 && \
export O365_TENANT_ID=e4e1abd9-eac7-4a71-ab52-da5c998aa7ba && \
~/.claude/skills/o365-manager/scripts/o365-manager <command>
⚠️ CRITICAL: Known Issues
Calendar List Commands Are Currently Broken
The Go binary has a date formatting bug that makes calendar listing non-functional:
- ❌
calendar list --days N- BROKEN (generates malformed timestamps) - ❌
calendar list --days-back N- BROKEN (generates malformed timestamps) - ❌ Cannot show agenda or list events
Working Commands:
- ✅ All email operations work correctly
- ✅ Calendar create, update, delete, respond (if you have event IDs)
- ✅ Authentication works
Impact: You CANNOT use this skill for "show my agenda" or calendar listing requests until the bug is fixed. Inform the user if they request calendar listing functionality.
CRITICAL: Agenda Display Rules
When the user asks "show my agenda" or "my agenda for [date]":
- Default behavior: Show ONLY active meetings (hide canceled/declined events)
- Use
calendar listWITHOUT the--allflag - Display in chronological order with enhanced details
- ALWAYS warn the user if any meeting has
has_collision: true- show which meetings overlap
When the user asks "show my FULL agenda" or "show ALL my agenda":
- Show EVERYTHING including canceled and declined meetings
- Use
calendar listWITH the--allflag - ALWAYS warn the user if any meeting has
has_collision: true- show which meetings overlap
Date/Time Handling:
- ALWAYS detect local timezone first:
date "+%Z" - Use detected timezone (not default Europe/Paris) with
--timezoneparameter where applicable - If no date specified, show current day's agenda
- For past dates, use
--days-backparameter
Collision Detection:
- The script automatically detects overlapping accepted/tentative meetings
- Check the
has_collisionfield on each event - If
true, checkcollides_witharray for details about conflicting meetings - Warn the user prominently about any scheduling conflicts
Available Commands
Email Management
All email commands use the format: o365-manager email <subcommand> [flags]
List Emails:
~/.claude/skills/o365-manager/scripts/o365-manager email list [--folder inbox] [--limit 10] [--unread] [--search 'query']
Returns JSON array with email metadata: id, subject, from, from_name, received, is_read, has_attachments, preview.
Read Email:
~/.claude/skills/o365-manager/scripts/o365-manager email read <message_id>
Returns full email details including body content, attachments, recipients.
Send Email:
~/.claude/skills/o365-manager/scripts/o365-manager email send --to addr1,addr2 --subject 'Subject' --body 'Body' [--cc addr3] [--body-type HTML|Text] [--attachments file1,file2,...]
IMPORTANT: Always use --body-type HTML when sending formatted emails. HTML is recommended for professional emails with proper formatting, tables, lists, and styling. Plain text should only be used for simple messages.
Attachments: Use --attachments to attach one or more files. Separate multiple file paths with commas. Supports absolute paths or paths relative to current directory. Files are automatically base64-encoded for transmission.
Example with attachments:
export O365_CLIENT_ID=76d42bf5-d461-4274-bd4d-a02576b9df36 && \
export O365_TENANT_ID=e4e1abd9-eac7-4a71-ab52-da5c998aa7ba && \
~/.claude/skills/o365-manager/scripts/o365-manager email send \
--to john.doe@loreal.com \
--subject 'BTDP Groups API Documentation' \
--body 'Please find attached the API specification.' \
--body-type HTML \
--attachments /tmp/api_spec.json,~/Documents/readme.pdf
Create Draft Email:
~/.claude/skills/o365-manager/scripts/o365-manager email create-draft --to addr1,addr2 --subject 'Subject' --body 'Body' [--cc addr3] [--body-type HTML|Text] [--attachments file1,file2,...]
Creates a draft email without sending it. Useful for preparing emails that need review before sending. Also supports attachments.
Reply to Email:
~/.claude/skills/o365-manager/scripts/o365-manager email reply <message_id> --comment 'Reply text'
Mark as Read/Unread:
~/.claude/skills/o365-manager/scripts/o365-manager email mark-read <message_id>
~/.claude/skills/o365-manager/scripts/o365-manager email mark-unread <message_id>
Delete Email:
~/.claude/skills/o365-manager/scripts/o365-manager email delete <message_id>
Move Email to Folder:
~/.claude/skills/o365-manager/scripts/o365-manager email move <message_id> --folder 'FolderName'
Archive Email:
~/.claude/skills/o365-manager/scripts/o365-manager email archive <message_id>
List Available Folders:
~/.claude/skills/o365-manager/scripts/o365-manager email list-folders
Mark Email as Important:
~/.claude/skills/o365-manager/scripts/o365-manager email mark-important <message_id>
Search Emails:
~/.claude/skills/o365-manager/scripts/o365-manager email search --query 'search term' [--limit 20]
Calendar Management
All calendar commands use the format: o365-manager calendar <subcommand> [flags]
List Events (Enhanced with Attendees, Response Status, Attachments):
~/.claude/skills/o365-manager/scripts/o365-manager calendar list [--days 7] [--days-back 0] [--limit 50] [--all] [--filter-subject 'regex']
Parameters:
--days N: Show next N days of events (future events)--days-back N: Show past N days of events (historical events)--limit N: Maximum number of events to return (default 50)--all: Include canceled and declined events (default: hidden)--filter-subject 'regex': Filter events by subject using regular expression (case-insensitive)
IMPORTANT: ALWAYS use date "+%Z" to detect local timezone before querying events.
Enhanced JSON Output includes:
id: Event IDsubject: Meeting titlestart,end: ISO 8601 timestamps (automatically converted to local timezone)timezone: Event timezone (automatically detected)location: Meeting locationorganizer: Organizer emailorganizer_name: Organizer display nameattendees: Array of first 6 attendees with name and emailmore_attendees: Count of additional attendees beyond first 6my_response: Your response status ("accepted", "declined", "tentative", "notResponded")is_cancelled: Boolean indicating if event is canceledhas_attachments: Boolean indicating if event has attachmentsattachments: Array of attachment namesis_online_meeting: Booleanonline_meeting_url: Teams/online meeting linkhas_collision: Boolean indicating if this event overlaps with other accepted/tentative meetingscollides_with: Array of colliding events with their subject, start, and end times
Collision Detection:
The script automatically detects overlapping meetings among your accepted and tentative events. When displaying the agenda, warn the user if any events have has_collision: true and show which meetings are conflicting.
Examples:
# Show today's agenda (active meetings only)
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days 1
# Show yesterday's agenda (active meetings only)
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days-back 1
# Show full agenda including canceled/declined
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days 7 --all
# Show next 3 days
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days 3
# Filter events by subject (case-insensitive regex)
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days-back 1 --filter-subject "Strategic"
# Filter with regex OR pattern
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days 7 --filter-subject "Vision|Platform|Review"
# Filter events matching multiple keywords
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days 14 --filter-subject "BTDP.*Meeting"
Respond to Event (Accept/Decline/Tentative):
~/.claude/skills/o365-manager/scripts/o365-manager calendar respond <event_id> --response accept|decline|tentative [--comment 'Optional message'] [--no-send]
Parameters:
<event_id>: The event ID from list events--response: Required - "accept", "decline", or "tentative"--comment 'Message': Optional message to include with response--no-send: Optional flag to NOT send email notification (default: sends email)
Examples:
# Accept meeting with default notification
~/.claude/skills/o365-manager/scripts/o365-manager calendar respond <event_id> --response accept
# Decline with message
~/.claude/skills/o365-manager/scripts/o365-manager calendar respond <event_id> --response decline --comment "Sorry, conflict in schedule"
# Mark tentative without sending email
~/.claude/skills/o365-manager/scripts/o365-manager calendar respond <event_id> --response tentative --no-send
Get Event Details:
~/.claude/skills/o365-manager/scripts/o365-manager calendar get <event_id>
Create Event:
~/.claude/skills/o365-manager/scripts/o365-manager calendar create \
--subject 'Meeting Title' \
--start '2025-11-12T14:00:00' \
--end '2025-11-12T15:00:00' \
[--attendees email1@example.com,email2@example.com] \
[--location 'Conference Room'] \
[--body 'Meeting description']
Note: The Go implementation may handle timezone differently than the Python version. Test to verify behavior.
Update Event:
~/.claude/skills/o365-manager/scripts/o365-manager calendar update <event_id> \
[--subject 'New Title'] \
[--start '2025-11-12T15:00:00'] \
[--end '2025-11-12T16:00:00'] \
[--location 'New Location']
Delete Event:
~/.claude/skills/o365-manager/scripts/o365-manager calendar delete <event_id>
Authentication
The script uses OAuth 2.1 with PKCE (Proof Key for Code Exchange) for secure authentication without client secrets.
Force Re-authentication:
export O365_CLIENT_ID=76d42bf5-d461-4274-bd4d-a02576b9df36 && \
export O365_TENANT_ID=e4e1abd9-eac7-4a71-ab52-da5c998aa7ba && \
~/.claude/skills/o365-manager/scripts/o365-manager auth
First-time usage will open a browser for authentication. Tokens are automatically refreshed when needed.
Technical Details
- Implementation: Go binary (compiled for macOS ARM64)
- Authentication: OAuth 2.1 PKCE flow
- Client ID: 76d42bf5-d461-4274-bd4d-a02576b9df36
- Tenant ID: e4e1abd9-eac7-4a71-ab52-da5c998aa7ba
- Redirect URI: http://127.0.0.1:33418
- API: Microsoft Graph API v1.0
- Scopes: Mail.ReadWrite, Mail.Send, Calendars.ReadWrite, User.Read
- Token Storage: ~/.claude/credentials/o365_tokens.json
Usage Examples
Email Operations
When the user asks to:
- "List my unread emails" → Use
email list --unread --limit 20 - "Send an email to john@example.com about the meeting" → Use
email send --body-type HTML(always use HTML for formatted content) - "Send an email with attachment" → Use
email send --body-type HTML --attachments /path/to/file1,/path/to/file2 - "Prepare a draft email" → Use
email create-draft --body-type HTML - "Archive this email" → Use
email archive <message_id> - "Move email to Junk" → Use
email move <message_id> --folder 'Junk Email' - "What folders do I have?" → Use
email list-folders
Agenda/Calendar Queries
CRITICAL Workflow for Agenda Requests:
- Set environment variables: Always export O365_CLIENT_ID and O365_TENANT_ID
- Detect local timezone: Run
date "+%Z"first - Parse the date request: If user says "yesterday", "today", "tomorrow", calculate the appropriate date
- Choose the right command:
- "Show my agenda" → Use
calendar listWITHOUT--all(hides canceled/declined) - "Show my full agenda" or "show all my agenda" → Use
calendar listWITH--all(shows everything)
- "Show my agenda" → Use
Examples:
User: "Show me my agenda for today"
# Step 1: Set environment
export O365_CLIENT_ID=76d42bf5-d461-4274-bd4d-a02576b9df36
export O365_TENANT_ID=e4e1abd9-eac7-4a71-ab52-da5c998aa7ba
# Step 2: Detect timezone
date "+%Z" # Returns "CET"
# Step 3: List today's events (active only)
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days 1
# Step 4: Format output chronologically showing:
# - Time range (start - end)
# - Subject
# - First 6 attendees + "and N others" if more
# - Your response status (Accepted/Tentative/Not Responded)
# - Organizer name
# - Attachments (just names if present)
# - **COLLISION WARNING** if has_collision is true, showing which meetings overlap
User: "Show me my agenda for yesterday"
# Step 1: Set environment
export O365_CLIENT_ID=76d42bf5-d461-4274-bd4d-a02576b9df36
export O365_TENANT_ID=e4e1abd9-eac7-4a71-ab52-da5c998aa7ba
# Step 2: Detect timezone
date "+%Z" # Returns "CET"
# Step 3: Calculate yesterday and list events
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days-back 1
# Step 4: Format output (same as above)
User: "Show me my FULL agenda for next week"
# Step 1: Set environment and detect timezone
export O365_CLIENT_ID=76d42bf5-d461-4274-bd4d-a02576b9df36
export O365_TENANT_ID=e4e1abd9-eac7-4a71-ab52-da5c998aa7ba
date "+%Z"
# Step 2: List ALL events including canceled/declined
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days 7 --all
# Step 3: Format output showing EVERYTHING including canceled meetings
User: "What meetings do I have tomorrow?"
# Detect timezone, then list tomorrow's events
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days 2 # Shows today + tomorrow, filter for tomorrow
User: "Accept the 2PM meeting"
# First list events to find the 2PM meeting's event_id
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days 1
# Then accept it
~/.claude/skills/o365-manager/scripts/o365-manager calendar respond <event_id> --response accept
User: "Decline the meeting with John, say I have a conflict"
# Find John's meeting
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days 7
# Decline with message
~/.claude/skills/o365-manager/scripts/o365-manager calendar respond <event_id> --response decline --comment "Sorry, I have a scheduling conflict"
User: "Mark the team meeting as tentative without notifying anyone"
~/.claude/skills/o365-manager/scripts/o365-manager calendar respond <event_id> --response tentative --no-send
User: "Find all Strategic meetings from yesterday"
# Use --filter-subject to search by regex pattern
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days-back 1 --filter-subject "Strategic"
User: "Show me all meetings about Platform or Vision in the next 2 weeks"
# Use regex OR pattern with |
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days 14 --filter-subject "Platform|Vision"
User: "List all BTDP meetings this week"
# Use regex pattern to match BTDP followed by any text then Meeting
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days 7 --filter-subject "BTDP.*Meeting"
User: "Show my agenda for today"
~/.claude/skills/o365-manager/scripts/o365-manager calendar list --days 1
# If collision detected in output (has_collision: true):
# ⚠️ WARNING: Meeting collision detected!
#
# 10:00-11:00 "Project Review" (Accepted)
# CONFLICTS WITH:
# - 10:30-11:30 "Team Standup" (starts 10:30, ends 11:30)
#
# You have overlapping meetings. Consider declining one or rescheduling.
Creating/Managing Events
User: "Create a meeting with Alice for 2PM tomorrow"
# Step 1: Set environment and detect timezone
export O365_CLIENT_ID=76d42bf5-d461-4274-bd4d-a02576b9df36
export O365_TENANT_ID=e4e1abd9-eac7-4a71-ab52-da5c998aa7ba
date "+%Z" # Returns "CET"
# Step 2: Calculate tomorrow's date at 2PM
# Tomorrow = 2025-12-15, so start = 2025-12-15T14:00:00
# Step 3: Create event
~/.claude/skills/o365-manager/scripts/o365-manager calendar create \
--subject "Meeting with Alice" \
--start "2025-12-15T14:00:00" \
--end "2025-12-15T15:00:00" \
--attendees alice@loreal.com
Important Notes
- Environment Variables: ALWAYS set O365_CLIENT_ID and O365_TENANT_ID before running commands
- First Run: The script will automatically open a browser for authentication on first use
- Token Management: Access tokens are automatically refreshed; re-authentication only needed if refresh token expires
- Date/Time Format: Use ISO 8601 format (YYYY-MM-DDTHH:MM:SS) for event times
- Timezone: CRITICAL - Always detect using
date "+%Z"before creating/querying events - JSON Output: All list/read commands return JSON for easy parsing
- Error Handling: Script provides clear error messages and exits with non-zero status on failure
- Attachments:
- Multiple files can be attached by separating paths with commas
- Files are automatically encoded in base64 for transmission
- Microsoft Graph API has a 4MB limit per request (including all attachments)
- For large files, consider using OneDrive sharing links instead
- Supports both absolute and relative file paths (tilde ~ expansion supported)
- Agenda Display:
- Default: Hides canceled/declined events for clean view
- Use
--allto show everything - Always display in chronological order
- Show first 6 attendees + count of additional attendees
- Show user's response status (accepted/declined/tentative/notResponded)
- Show attachment names if present
- Event Responses:
- Default: Sends email notification to organizer
- Use
--no-sendto respond without notification - Optional
--commentto include message with response - Three response types: accept, decline, tentative
Workflow
When the user requests an email or calendar operation:
- Set environment variables (O365_CLIENT_ID and O365_TENANT_ID)
- Determine the action from user's request
- Execute the appropriate command using the Go binary
- Parse the JSON output if needed
- Present results to the user in a friendly format
- Handle errors gracefully and inform the user
For multi-step operations (e.g., "reply to the latest email from John"):
- First, search/list emails to find John's email
- Extract the message_id from the results
- Use reply with the message_id
Always verify authentication is working before attempting operations. If authentication fails, guide the user to run the auth command with proper environment variables set.