mcpField
M
microsoft-outlook

microsoft-outlook

github.com/acedatacloud生产力17260 次安装2 天前更新
代码仓库

关于 microsoft-outlook

通过 `curl + jq` 调用 Microsoft Graph 管理 Outlook 邮件和日历的 Shell 技能,适合需要处理 Microsoft 365 邮箱与日程的 Agent。它涵盖邮件搜索、草稿和附件操作、日历与空闲状态查询,并提示在发送或修改等重要操作前确认。

主要功能

  1. 01读取、筛选和搜索邮件
  2. 02创建和管理邮件草稿
  3. 03处理邮件与附件
  4. 04查看和更新日历事件
  5. 05查询空闲状态和邮箱设置

适用场景

  1. 01查找未读邮件或特定发件人的邮件
  2. 02发送前检查邮件草稿
  3. 03查看包含重复事件的每日安排
  4. 04为多人查找合适的会议时间

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/acedatacloud/skills --skill microsoft-outlook

安装后在 Claude Code 中输入/microsoft-outlook确认已加载。

详细介绍

通过 curl + jq 调用 Microsoft Graph,管理 Outlook / Microsoft 365 中的邮件和日历。用户的 OAuth bearer token 位于 $MICROSOFT_OUTLOOK_TOKEN;每次调用都需要在 Authorization 标头中传入 Bearer $MICROSOFT_OUTLOOK_TOKEN。该 token 已包含用户在安装时同意授予的作用域:Mail.Read、Mail.ReadWrite、Mail.Send、MailboxSettings.Read、MailboxSettings.ReadWrite、Calendars.Read、Calendars.ReadWrite 中的任意作用域,以及对应的 *.Shared 变体。邮件和日历整合到同一个连接器(和同一个 OAuth 授权)中,因为 Microsoft Graph 将它们视为同一邮箱的相邻功能——在技能层拆分它们没有意义。

Graph API 返回 JSON;失败时会显示为 {"error": {"code": "...", "message": "..."}}——请将该错误原样展示给用户。

始终从 /me 开始,确认连接可用,并了解当前操作的是哪个邮箱。处理日历时,还要获取 mailboxSettings.timeZone,以正确显示日期。


邮件 — 操作示例

验证身份验证(始终先运行)
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  https://graph.microsoft.com/v1.0/me \
  | jq '{displayName, mail, userPrincipalName}'
列出最近的邮件
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/messages?\$top=10&\$select=id,subject,from,receivedDateTime,isRead,hasAttachments&\$orderby=receivedDateTime desc" \
  | jq '.value[] | {subject, from: .from.emailAddress.address, received: .receivedDateTime, unread: (.isRead | not)}'

筛选条件:将筛选条件附加到 URL,使用 &(对空格进行 URL 编码)。

需求附加内容
仅未读邮件&$filter=isRead eq false
包含附件&$filter=hasAttachments eq true
来自特定发件人&$filter=from/emailAddress/address eq '[email protected]'
日期范围&$filter=receivedDateTime ge 2026-04-01T00:00:00Z and receivedDateTime lt 2026-05-01T00:00:00Z
组合筛选在筛选条件之间使用 and / or
搜索邮件(对主题和正文进行全文搜索)
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  --data-urlencode '$search="quarterly report"' \
  --data-urlencode '$top=10' \
  --data-urlencode '$select=id,subject,from,receivedDateTime' \
  --get https://graph.microsoft.com/v1.0/me/messages

$search 不能与 $filter 或 $orderby 在同一查询中组合使用——请选择其中一个。$search 会按相关性对结果排序。

读取邮件正文
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}?\$select=subject,body,from,toRecipients,receivedDateTime" \
  | jq '{subject, from: .from.emailAddress.address, received: .receivedDateTime, body: .body.content}'

body.contentType 通常为 "HTML"。如果要获取原始 HTML,请使用 jq -r .body.content。

发送电子邮件

⚠️ 始终使用“草稿 → 确认 → 发送”流程。绝不要直接调用 /me/sendMail——它会立即发送,且无法撤回。

# Step 1: create draft
DRAFT=$(curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -nc \
        --arg subj "Project update" \
        --arg body "<p>Wanted to share the latest numbers.</p>" \
        --arg to "[email protected]" \
        '{subject:$subj, body:{contentType:"HTML", content:$body}, toRecipients:[{emailAddress:{address:$to}}]}')" \
  https://graph.microsoft.com/v1.0/me/messages)
DRAFT_ID=$(echo "$DRAFT" | jq -r .id)

# Step 2: present the draft to the user — subject, recipients, body preview
echo "$DRAFT" | jq '{subject, to: .toRecipients[0].emailAddress.address, body: .body.content}'

# Step 3: ONLY after user confirms — send (returns 202 No Content)
curl -sS -X POST -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/messages/${DRAFT_ID}/send" \
  -w "HTTP %{http_code}\n"

抄送 / 密送:在同一请求中加入 ccRecipients / bccRecipients 数组,格式与 toRecipients 相同。

回复 / 全部回复 / 转发

⚠️ 发送前,先向用户展示草稿内容和收件人。

# Quick reply (sends immediately on /reply — for explicit user-confirmed flow)
curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"comment":"Thanks for the update!"}' \
  "https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/reply"

# Or: createReply → review → /send (preferred for non-trivial replies)
DRAFT=$(curl -sS -X POST -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/createReply")
DRAFT_ID=$(echo "$DRAFT" | jq -r .id)
# PATCH body if needed, then /send

# Forward
curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -nc --arg to "[email protected]" \
        '{comment:"FYI", toRecipients:[{emailAddress:{address:$to}}]}')" \
  "https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/forward"
标记为已读 / 未读
curl -sS -X PATCH \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"isRead": true}' \
  "https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}"
列出文件夹 + 读取指定文件夹
# Well-known folder names: Inbox, Drafts, SentItems, DeletedItems, Archive, JunkEmail
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/mailFolders('SentItems')/messages?\$top=5&\$select=subject,toRecipients,sentDateTime" \
  | jq '.value[] | {subject, sent: .sentDateTime}'
列出 + 下载附件
# Metadata
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/attachments?\$select=id,name,size,contentType" \
  | jq '.value[] | {id, name, size}'

# Download a single attachment
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/attachments/${ATT_ID}/\$value" \
  -o "$SKILL_DIR/tmp/attachment.bin"
邮箱设置(时区、签名、自动回复)
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/mailboxSettings"

设置外出自动回复:

⚠️ 更改前先征得用户确认——自动回复会针对每封收到的邮件发送。

curl -sS -X PATCH \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"automaticRepliesSetting":{
        "status":"scheduled",
        "scheduledStartDateTime":{"dateTime":"2026-05-10T09:00:00","timeZone":"China Standard Time"},
        "scheduledEndDateTime":{"dateTime":"2026-05-15T18:00:00","timeZone":"China Standard Time"},
        "internalReplyMessage":"<p>I'm out this week, back Monday.</p>"}}' \
  "https://graph.microsoft.com/v1.0/me/mailboxSettings"

需要 MailboxSettings.ReadWrite 作用域。

删除邮件

⚠️ 始终先获取主题,并向用户确认。

# 1) show what's about to be deleted
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}?\$select=subject,from,receivedDateTime" \
  | jq '"Delete \"\(.subject)\" from \(.from.emailAddress.address) (\(.receivedDateTime))?"'

# 2) after user confirms (moves to Deleted Items, returns 204)
curl -sS -X DELETE -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}" \
  -w "HTTP %{http_code}\n"

日历 — 操作示例

获取用户时区(开始处理任何日历任务时运行一次)
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/mailboxSettings" \
  | jq '.timeZone'
# → e.g. "China Standard Time"

在每次日历调用的 Prefer: outlook.timezone 标头中传入该时区,这样返回的 start.dateTime / end.dateTime 就会按用户的本地时间显示:

TZ_HEADER='Prefer: outlook.timezone="China Standard Time"'
今日安排(calendarView)

calendarView 会将时间窗口内的重复系列展开为单独的实例,正适合查看日程安排。普通的 /events 只会返回重复系列的主事件。

START=$(date -u +'%Y-%m-%dT00:00:00Z')
END=$(date -u -v+1d +'%Y-%m-%dT00:00:00Z')   # macOS; use -d on Linux
curl -sS \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Prefer: outlook.timezone=\"China Standard Time\"" \
  --data-urlencode "startDateTime=$START" \
  --data-urlencode "endDateTime=$END" \
  --data-urlencode '$select=id,subject,start,end,location,attendees,onlineMeeting,isCancelled' \
  --data-urlencode '$orderby=start/dateTime' \
  --get https://graph.microsoft.com/v1.0/me/calendarView \
  | jq '.value[] | {subject, start: .start.dateTime, end: .end.dateTime, location: .location.displayName, attendees: [.attendees[].emailAddress.address]}'
本周事件(周一至周日)
START=$(date -u -v-Mon +'%Y-%m-%dT00:00:00Z' 2>/dev/null || date -u -d 'last monday' +'%Y-%m-%dT00:00:00Z')
END=$(date -u -v+7d -v-Mon +'%Y-%m-%dT00:00:00Z' 2>/dev/null || date -u -d 'next monday' +'%Y-%m-%dT00:00:00Z')
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Prefer: outlook.timezone=\"China Standard Time\"" \
  --data-urlencode "startDateTime=$START" \
  --data-urlencode "endDateTime=$END" \
  --data-urlencode '$select=subject,start,end' \
  --data-urlencode '$orderby=start/dateTime' \
  --get https://graph.microsoft.com/v1.0/me/calendarView
查找空闲 / 忙碌时段(getSchedule)

这是查找适合多人的时间段的最佳方式。它会返回指定时间窗口内以 30 分钟为单位的空闲 / 忙碌 / 暂定时段。

curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -nc '{
        schedules: ["me", "[email protected]", "[email protected]"],
        startTime: {dateTime: "2026-05-05T09:00:00", timeZone: "China Standard Time"},
        endTime:   {dateTime: "2026-05-05T18:00:00", timeZone: "China Standard Time"},
        availabilityViewInterval: 30
      }')" \
  https://graph.microsoft.com/v1.0/me/calendar/getSchedule \
  | jq '.value[] | {who: .scheduleId, view: .availabilityView}'
# availabilityView is a string of digits: 0=free 1=tentative 2=busy 3=oof 4=workingElsewhere
读取单个事件(包括与会者和正文)
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Prefer: outlook.timezone=\"China Standard Time\"" \
  "https://graph.microsoft.com/v1.0/me/events/${EVENT_ID}?\$select=subject,start,end,location,attendees,body,organizer,onlineMeeting" \
  | jq '{subject, start: .start.dateTime, attendees: [.attendees[] | {addr: .emailAddress.address, response: .status.response}], body: .body.content}'
创建事件

⚠️ 创建前,始终向用户展示主题 / 时间 / 与会者——事件一经 POST,邀请就会自动发出。

PAYLOAD=$(jq -nc \
  --arg subj "Project sync" \
  --arg body "<p>Quarterly review.</p>" \
  --arg start "2026-05-06T10:00:00" \
  --arg end   "2026-05-06T10:30:00" \
  --arg tz    "China Standard Time" \
  --arg loc   "Meeting room 3F" \
  --arg a1    "[email protected]" \
  '{
    subject: $subj,
    body:    {contentType:"HTML", content:$body},
    start:   {dateTime:$start, timeZone:$tz},
    end:     {dateTime:$end,   timeZone:$tz},
    location:{displayName:$loc},
    attendees:[{emailAddress:{address:$a1}, type:"required"}],
    isOnlineMeeting: true,
    onlineMeetingProvider: "teamsForBusiness"
  }')
curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$PAYLOAD" \
  https://graph.microsoft.com/v1.0/me/events \
  | jq '{id, subject, start: .start.dateTime, joinUrl: .onlineMeeting.joinUrl}'

isOnlineMeeting: true + onlineMeetingProvider: "teamsForBusiness" 会自动生成 Teams 会议链接。创建线下活动时,请移除这两个字段。

更新 / 重新安排(PATCH)

⚠️ 更新会向所有与会者发送“已更新”通知。请先征得确认。

curl -sS -X PATCH \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -nc \
        --arg start "2026-05-06T14:00:00" \
        --arg end   "2026-05-06T14:30:00" \
        --arg tz    "China Standard Time" \
        '{start:{dateTime:$start, timeZone:$tz}, end:{dateTime:$end, timeZone:$tz}}')" \
  "https://graph.microsoft.com/v1.0/me/events/${EVENT_ID}"
取消会议(发送取消通知)

⚠️ 请与用户确认——所有参会者都会收到通知。

curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"comment":"Need to reschedule, sorry."}' \
  "https://graph.microsoft.com/v1.0/me/events/${EVENT_ID}/cancel" \
  -w "HTTP %{http_code}\n"
接受 / 拒绝 / 暂定接受收到的邀请
curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"comment":"See you there", "sendResponse":true}' \
  "https://graph.microsoft.com/v1.0/me/events/${EVENT_ID}/accept"

# Or /decline, /tentativelyAccept
读取共享日历

需要 Calendars.Read.Shared。

curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/users/${USER_UPN}/calendarView?startDateTime=${START}&endDateTime=${END}&\$select=subject,start,end" \
  -G
使用时区
字段含义
start.dateTime / end.dateTime本地挂钟时间。
start.timeZone / end.timeZone类似 IANA 的名称("Pacific Standard Time"、"China Standard Time"、"UTC")。
Prefer: outlook.timezone="..." 请求标头将所有返回的 dateTime 值转换为此时区。

读取时始终设置 Prefer: outlook.timezone,这样 JSON 会使用用户预期的时区,而不是 UTC。

重复事件

使用 calendarView(它会为你展开各次事件),不要使用 ?$expand=。 要创建重复事件,请包含 recurrence:

{
  "recurrence": {
    "pattern":  {"type":"weekly", "interval":1, "daysOfWeek":["monday","wednesday"]},
    "range":    {"type":"endDate", "startDate":"2026-05-06", "endDate":"2026-08-06"}
  }
}

要修改系列中的单次事件,请 PATCH 该次事件的 id(由 calendarView 返回),而不是系列主事件。


OData 快速参考(邮件 + 日历)

参数邮件示例日历示例
$selectid,subject,from,receivedDateTime,isReadsubject,start,end,location,attendees
$filterisRead eq falsestart/dateTime ge '2026-05-01T00:00:00'
$orderbyreceivedDateTime descstart/dateTime
$top浏览用 10,搜索用 25浏览用 10
$search"keyword"(仅限邮件——不能与 $filter / $orderby 组合)不适用
$expandattachmentsattendees、attachments

使用 curl 时,通过 --data-urlencode "$key=$value" --get 避免对 $ 和空格进行 shell 引号转义。

规则

  • 始终传入 $select——默认情况下每项会返回 30 多个字段。
  • 浏览用 $top=10,搜索用 $top=25。除非用户要求,否则不要翻页超过 50 项。
  • 邮件仅使用 HTML 正文。 contentType: "Text" 会压缩空白字符。
  • 对于任何日程 /“我的日历上有什么”问题,都使用 calendarView。 /events 只会返回重复事件的主事件。
  • 读取日历时设置 Prefer: outlook.timezone;否则 dateTime 会以 UTC 返回。
  • 如果在路径中使用邮件 / 事件 / 附件 ID,请进行 URL 编码——ID 可能包含 +、/、=。使用 jq -sRr @uri。
  • 日期计算:macOS 上可使用 date -u -v+1d,Linux 上可使用 date -u -d 'tomorrow'。

关键:执行破坏性操作 / 通知操作前须征得用户同意

已发送的邮件无法撤回。日历写入会向参会者发送邮件。已删除的邮件可能无法恢复。 流程:准备 → 呈现 → 执行。

操作准备步骤向用户展示
发送邮件POST /me/messages(草稿)主题、收件人、正文预览
回复 / 转发createReply / createForward引用片段 + 你的回复文本
删除邮件先获取 subject“要从 {sender} 删除“{subject}”吗?”
外出办公先展示当前设置新日程 + 消息预览
创建事件构建负载主题、时间、参会者、是否开启在线会议
更新事件与当前内容对比差异将更改的内容、会收到通知的参会者人数
取消事件先获取事件主题、时间、参会者人数
接受 / 拒绝邀请先获取事件事件主题 + 组织者
批量操作列出受影响的对象数量 + 示例

绝不要调用 /me/sendMail——它会立即发送,且无法撤销。始终先创建草稿 → 确认 → /send。

错误

  • 401 InvalidAuthenticationToken → 令牌已过期;用户必须重新安装连接器。
  • 403 ErrorAccessDenied → 缺少写入权限范围(例如,尝试使用未获授权的 Mail.Send,或创建 / 取消事件时缺少 Calendars.ReadWrite);请用户重新安装并勾选写入权限范围。
  • 429 TooManyRequests → 遵循 Retry-After 标头。
  • 404 ErrorItemNotFound → 邮件 / 事件 id 错误(或已被删除 / 取消)。

Reference

评论

支持 Markdown