
Office Outlook Mcp
io.github.chrischall由 GitHub 帳號 chrischall 發布v0.8.4更新於 Nov 7, 2026
Outlook / Microsoft 365 for Claude — triage mail, reply, answer invites and book meetings, confirmed
概覽
讓 Claude 連接 Outlook / Microsoft 365 的郵件、行事曆、連絡人和工作,寄送與回覆前需先確認。
- 可以做什麼
- 讀取郵件、資料夾、附件、行事曆、行程、連絡人、人員、分類和工作
- 處理未讀郵件:回覆、轉寄、標記、分類、移動和標為已讀
- 回覆會議邀請、尋找空檔時間、查看行程,並建立或更新含 Teams 連結的行程
- 將郵件和行事曆批次匯出為 JSONL 或 Markdown 檔案
- 執行連線健康檢查
- 適用情境
- 適合讓助理從頭到尾處理 Outlook 收件匣:閱讀新郵件、回覆邀請、撰寫回覆草稿、安排會議,或在不開啟 Outlook 的情況下批次匯出信箱或行事曆。
- 執行需求
- 以 npm 套件形式在本機透過 stdio 執行(需要 Node.js)。擷取權杖需要 Chrome 中的 ContextMint Bridge 瀏覽器擴充功能與 fetchproxy 命令列工具,以及已登入的 Outlook Web 分頁和一次性的 6 位數配對碼;也可改用 OUTLOOK_ACCESS_TOKEN。其餘設定皆為選用。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 Office Outlook Mcp,將其新增到工作區。
- 為需要使用其工具的對話啟用該服務。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
其他 MCP 客戶端
參照 儲存庫 中的啟動說明。
README
office-outlook-mcp
MCP server for Outlook / Microsoft 365 — mail, folders, calendar, contacts and tasks, with confirmation-gated sending.
This project was developed and is maintained by AI (Claude Code). Use at your own discretion.
How it authenticates
Outlook Web holds its access token in memory and sends it as
Authorization: Bearer …. This server snapshots that header from your
signed-in browser tab through the fetchproxy
bridge, then talks to https://outlook.office.com/api/v2.0 with ordinary
server-side requests.
The browser is needed only to mint the token (~25h), never to make a call. That is the whole design: no Azure app registration, no admin consent, and no browser on the request path.
Tokens are cached at ~/.office-outlook-mcp/token.json (0600), so a restart
does not re-capture.
Install
Requires the ContextMint Bridge browser extension, installed from
its releases:
in Chrome, unzip the chrome build and load it unpacked
(chrome://extensions → Developer mode → Load unpacked). Safari is not
available yet — it will ship inside the ContextMint app, which has no public
download — so use Chrome for now.
ContextMint Bridge is the fetchproxy browser extension under its new name, from
the same maintainer — fetchproxy's own README
(#extension) points to it.
Its source is public at
nullnet-app/contextmint-bridge:
build it yourself, or check a release zip against the .sha256 file published
beside it (shasum -a 256 -c contextmint-bridge-chrome-<version>.zip.sha256).
The extension, fpx and this server are compatible by fetchproxy protocol
number (currently protocol 4), not by matching package versions. Keep the
bridge and fpx current — a mismatch fails with an error naming both protocol
numbers. Also install @fetchproxy/cli:
The first capture prints a 6-digit pair code to approve in the ContextMint Bridge popup; the grant persists.
Pairing
The server declares one host to the bridge — outlook.cloud.microsoft, or
OUTLOOK_WEB_HOST — and the same scope on every capture, so once approved it
neither re-pairs nor asks for wider grants. The bridge keeps one relay tab on
that host open (it reuses yours if you already have Outlook open).
Before 0.8.2 it also declared outlook.office.com. A signed-in tab there
redirects to outlook.cloud.microsoft, so the bridge opened another background
tab on every capture. Upgrading changes the declared domain set, so the bridge
asks to pair once more.
If you run the bridge in more than one browser or browser profile, each one is a
different extension identity, and the server trusts only the one it paired
with. Keep the bridge enabled in the browser you pair with (or re-pair
deliberately with FETCHPROXY_TRUST_NEW_EXTENSION=1 after switching browsers).
Install in opencode
opencode reads MCP servers from opencode.json (project) or
~/.config/opencode/opencode.json (global):
That one file serves opencode 2 and current opencode 1 — verified live on 2.0.11, 1.18.31 and 1.18.28, which all connect from it.
Two things worth knowing, both measured rather than assumed:
- Never carry both config shapes in one file. Older opencode 1 wants the
servers directly under
mcp("mcp": { "outlook": { … } }), and the temptation is to write both so either version finds one. Do not: given a sibling key besideservers, opencode 2 parses the file — it still shows up inopencode debug config— and then reports "No MCP servers configured". No error, no warning, every server silently gone. - An opencode 1 old enough to reject
serverssays so loudly (Configuration is invalid at …), so if you see that, switch that machine to the flat shape rather than combining them.
opencode 2 talks to a background service, so opencode reload before
opencode mcp list after editing config.
Configuration
Everything is optional — with nothing set, the server captures from the browser.
Tools
Read — outlook_list_folders, outlook_list_messages,
outlook_get_message, outlook_list_unread_messages, outlook_list_attachments,
outlook_get_attachment, outlook_list_events, outlook_get_event,
outlook_list_calendars, outlook_find_meeting_times,
outlook_get_schedule, outlook_get_profile,
outlook_get_mailbox_settings, outlook_list_contacts, outlook_list_people,
outlook_list_categories, outlook_list_tasks
Write (ask you to confirm first — see Confirmations) —
outlook_send_message, outlook_create_draft, outlook_mark_read,
outlook_update_message, outlook_move_message, outlook_create_event,
outlook_update_event, outlook_respond_to_invite, outlook_delete_event,
outlook_reply.
Some skip the confirmation because nothing reaches anyone else:
outlook_create_draft (the draft stays in Drafts until you send it from
Outlook), outlook_reply with draftOnly: true (the reply stays in Drafts),
outlook_respond_to_invite with sendResponse: false, and
outlook_delete_event on your own appointment with no attendees or on a
meeting the organizer already cancelled (in each, only your calendar
changes).
Meetings — outlook_find_meeting_times asks Outlook's Scheduling
Assistant for slots when everyone is free, and outlook_get_schedule shows
each person's busy blocks. Each suggested slot carries createEventArgs,
which outlook_create_event accepts unchanged. outlook_create_event
attaches a Microsoft Teams meeting by default and returns its join link; pass
teamsMeeting: false to leave it off. outlook_update_event never changes how
people join a meeting unless asked: pass teamsMeeting: true to add a Teams
meeting to an event that lacks one, and the confirmation says so in plain
words. Only the organizer
can update a meeting. outlook_respond_to_invite accepts, tentatively accepts
or declines an invite, by its message id or its event id.
outlook_delete_event deletes an event you organize — one occurrence by the
occurrence's id, or the whole series by its series master's id — and sends
any attendees a cancellation; for an invite you received it refuses and points
you at declining instead. outlook_list_events and outlook_get_event show
what that decision needs in every view: MyResponse, ResponseRequested,
IsOrganizer, IsCancelled, Type (SingleInstance, Occurrence, Exception or
SeriesMaster), ShowAs and IsAllDay, plus SeriesMasterId in the full view.
Export — outlook_export_messages (mail) and outlook_export_events
(calendar) write JSONL or Markdown files, optionally with attachments, and
return only where they are; see Bulk export.
Diagnostics — outlook_healthcheck
Every read tool takes view: compact | full | raw, defaulting to compact.
Three reads also take view: "brief" — the one-line-per-item rung mcp-host's
context groups ask in: outlook_list_events and outlook_list_tasks gain it
beside their existing values, and outlook_list_unread_messages gains an
optional view whose only value is brief (omit it for the triage batch).
Both additions are optional, so existing calls are unchanged. A brief answer
is the untrusted envelope described below, with the lines as data (one string
per item, title — <ISO date | no date> — <status>, at most 120 chars); it
fetches no message body or invite and carries no nextLink/next_from, so
when Outlook has another page the last line is the note -- more (a line
starting -- is a note, not an item) — call again without view: "brief"
to get the link and follow it.
Mutating tools write nothing until you confirm (bar the cases above)
— see Confirmations. The preview shows exactly what would be sent
(action, method, path and body). (outlook_create_event first reads the
mailbox time zone, so its preview can name the zone it would book in; that is
the one read a preview makes.)
Mail and event text is written by other people, so outlook_list_messages,
outlook_get_message, outlook_list_unread_messages, outlook_get_attachment,
outlook_list_events, outlook_get_event, outlook_find_meeting_times,
outlook_get_schedule, outlook_export_messages and outlook_export_events
wrap every result (all views) in an untrusted-content
envelope — untrusted_content: true plus a note telling the model to treat
the text as data, never instructions (a view: raw record that carries its
own untrusted_content/note key is nested under data, so it cannot
overwrite the envelope). outlook_list_tasks's brief view is wrapped the same
way. outlook_reply returns a sent reply's subject — the
original sender's text — inside the same envelope.
On a client that cannot show a confirmation prompt, the confirmToken is still
something the model passes back itself, so keep MCP_CONFIRM_MODE=ask-user
(the default) and approve each preview in chat — or use a client that asks you
before running non-read-only tools — as the real guard on outbound sends.
Triage loop
An agent can work through new mail end to end without opening Outlook:
- Read the batch —
outlook_list_unread_messagesreturns the unread messages in a folder (default inbox), newest first, each with its plain-text body, in one call. It does not mark anything read. Each item has akind:mail,meetingRequest,meetingCancelledormeetingResponse; requests and cancellations also carry the linkedevent(time, organizer, your current response) — or, when the meeting is no longer on your calendar (already declined, cancelled or deleted), ahintsaying there is nothing to respond to.toandcclist at most 20 addresses, withtoCount/ccCountwhen there are more. - Act on each item —
- an invite:
outlook_respond_to_invitewith the message id andaccept,tentativeordecline(and an optional comment). Once you respond, Outlook moves the invite message to Deleted Items under a new id, so an answered invite is already filed — skip steps 3 and 4 for it (its old id would 404); - a message that needs an answer:
outlook_reply(reply,replyAllorforward), ordraftOnly: trueto leave it in Drafts for you to review; - a request for a meeting:
outlook_find_meeting_timesfor the attendees, then pass the chosen slot'screateEventArgstooutlook_create_event; - an attachment to read:
outlook_get_attachment.
- an invite:
- File it —
outlook_update_messageflags, categorises (names fromoutlook_list_categories) and marks read up to 50 messages in one call;outlook_move_messagemoves one to another folder. - Mark it handled —
outlook_mark_read(orisRead: trueonoutlook_update_message) on the plain mail you handled, so the nextoutlook_list_unread_messagesreturns only what is still new. Answered invites are already out of the inbox (see step 2).
Replies, forwards, invite responses and new meetings reach other people, so
each asks you to confirm first — except a reply saved with draftOnly: true
(it stays in Drafts until you send it) and an invite response with
sendResponse: false (only your calendar changes; the organizer is not told).
Paging from a time
outlook_list_unread_messages and outlook_list_messages also page oldest first
from a point in time: pass from as an ISO 8601 UTC time
(2026-10-01T00:00:00Z). The result carries next_from — pass it back
unchanged as from for the next page — and has_more. To backfill, start
from an old time and keep passing next_from until has_more is false; then
keep polling with the last next_from to pick up new mail (an empty page
returns the same position). Messages that share a timestamp are never skipped
or repeated across pages, and marking mail read between pages loses nothing.
from cannot be combined with nextLink, sinceHours, search or skip.
Bulk export
outlook_export_messages exports mail and outlook_export_events exports
the calendar in bulk, thousands of records at a time, and none of their text
enters the conversation. The server pages the mailbox (or calendar) and writes
the records to files. The tool returns only a small manifest: where the files
are, their sizes, the record and attachment counts, the date span, and
has_more plus a cursor. Have the agent read the files with shell tools
(jq, grep, rg, head) rather than opening them whole.
- Mail arguments:
folder(a well-known name such asinboxorsentitems, or a folder id;allexports the whole mailbox; defaultinbox),sinceanduntil(ISO 8601),search(full-text, spans the mailbox). Mail is exported oldest first. - Event arguments:
sinceanduntil(ISO 8601, both required, at most two years apart) andcalendar(a calendar id; default, the default calendar). Events come from the calendar view, so a recurring series is expanded into its individual occurrences. Times are wall-clock times in the mailbox's time zone, named on each record astime_zone. - Both:
format(jsonl, the default, ormarkdown),attachments(defaultfalse),dest,cursor,max_items(default 5000, max 50000) andbudget_seconds(default 90, max 600). - Files:
part-0001.jsonlholds one JSON record per message (id, conversation id, subject, from, to/cc/bcc, dates, flags, categories, web link and the full plain-text body). Its first line is an_exportheader that names the source, account, arguments and an untrusted-content note. An event record carries the id, iCal UID, type and series id, subject, start, end, time zone, location, organizer, attendees with their responses, your response, show-as, categories, the join link and the full plain-text body.format: "markdown"writespart-0001.md, a readable transcript (one section per message or event), instead.manifest.jsonlists the counts and any errors, and the files: on disk every part's files (it is rewritten as each part finishes); in a blob zip the files in that zip. - Attachments: with
attachments: true, each file attachment of up to 50 MB is saved asattachments/<short id>/<name>(the name sanitized), and the record lists its attachments, each with its relativepath, or withskippedand the reason. Attached Outlook items (a forwarded email or event), cloud-file links (OneDrive/SharePoint), larger files and failed downloads are skipped. One bad attachment never fails the export. - Resuming: each call stops at
max_itemsorbudget_seconds, whichever comes first. Whilehas_moreis true, passcursorback unchanged. The next call continues the same export aspart-0002,part-0003and so on. With attachments on, the budget is checked between small groups of records, so a stop can fall inside a page; the cursor resumes at the next record. If Outlook refuses the credential (including while downloading an attachment), or keeps returning 429 after three Retry-After waits, the call keeps what it already wrote and returns the cursor. Re-authenticate, then resume.
Where the files go depends on how the server runs:
On disk, <dir> is the per-call dest, else OUTLOOK_OUTPUT_DIR, else
~/Downloads/office-outlook-mcp. A dest must be inside OUTLOOK_OUTPUT_DIR
when that is set. Otherwise it must be inside ~/Downloads, ~/Documents or
~/Desktop. A resumed cursor's directory is checked against the same roots
again.
Confirmations
Every write asks you to confirm before anything is sent or changed (except a
new draft, a draft-only reply and a silent invite response — see
Tools). A client
that can show a confirmation prompt (Claude Code) shows one. On a client that
cannot, the first call does nothing and returns a confirmation-required
preview plus a confirmToken; only a repeat call with that token performs the
write. The token is single-use, expires, and is bound to the exact arguments —
change anything between the two calls and it is refused (DRAFT_CHANGED) with
a fresh preview.
Things worth knowing
- Fields are PascalCase (
ReceivedDateTime,IsRead). This is the Outlook REST API, not Graph — a Graph snippet silently returns nothing. outlook_get_messagereturns a plain-text body by default, measured 9.4× smaller than the HTML; passhtml: truefor the original.outlook_list_eventsuses the calendar view, which expands recurring series./me/eventsdoes not, which is why it is not exposed as-is.- Time zones are Windows names (
Eastern Standard Time), not IANA. searchandunreadOnlycannot be combined; the API rejects$searchalongside$filter, so the tool refuses before making a doomed request.- Moving a message assigns it a new id.
outlook_list_eventsreturns times in the mailbox's own time zone unlesstimeZoneoverrides it. The API itself defaults to UTC, which silently reads as a four-hour error on an Eastern mailbox.- Contact phone fields are
MobilePhone1, not Graph'sMobilePhone. The v2.0 Contact type rejects the Graph name with a 400. outlook_get_attachmentdecodes a text-like file up to 5 MB and returns an image up to 5 MB inline. Anything else, up to 50 MB, comes back as the file: a signed download link valid for 1 hour when hosted (mcp-host setsMCP_BLOB_BASE_URLandMCP_BLOB_SIGNING_KEY), or a new0600file underOUTLOOK_OUTPUT_DIR(else~/Downloads/office-outlook-mcp) on a local install. A hosted server never writes to its own disk; nobody in your session could open the path.
Upgrading
To the next release
outlook_get_attachment used to return PDFs, Office files and anything over
5 MB as metadata only, so the file was unreachable. It now hands the bytes over.
The existing fields (name, contentType, size, kind, inlined, text,
textTruncated, hint) are unchanged. New fields appear when bytes are
delivered:
via:image,resource,linkordisk.downloadUrlanddownloadExpiresAt: hosted. Treat the link as a secret; anyone holding it can download until it expires.path: local, where the file was saved.deliveryNote: why that route was chosen, when it was not the obvious one.
A local install with a ~/Downloads folder (or OUTLOOK_OUTPUT_DIR set) now
saves such attachments to disk instead of returning metadata only. Without
either, the old metadata-only answer remains. The hint text on that path now
names OUTLOOK_OUTPUT_DIR.
The lightweight alternative
If you only need Outlook access from Claude Code on this machine, the
skills/outlook-fpx access skill in this repo does the same reads with fpx +
curl and no server at all. The MCP earns its keep when you want typed tools,
confirmation-gated writes, or reach from claude.ai.
Development
Request shapes are pinned in docs/OUTLOOK-API.md, all
verified against a live mailbox.
License
MIT
來源:README.md,提交 67d909e
工具
0版本歷史
23- v0.8.4最新Nov 7, 2026
- v0.8.1Nov 4, 2026
- v0.7.1Oct 21, 2026
- v0.7.0Oct 20, 2026
- v0.6.2Oct 17, 2026
- v0.6.1Oct 16, 2026
- v0.6.0Oct 14, 2026
- v0.5.2Oct 11, 2026
- v0.5.1Oct 11, 2026
- v0.5.0Oct 10, 2026
- v0.4.1Oct 10, 2026
- v0.4.0Oct 8, 2026
- v0.3.5Oct 7, 2026
- v0.3.4Oct 5, 2026
- v0.3.3Oct 3, 2026
- v0.3.2Sep 28, 2026
- v0.3.1Sep 25, 2026
- v0.3.0Sep 24, 2026
- v0.2.0Sep 23, 2026
- v0.1.5Sep 23, 2026
- v0.1.4Sep 21, 2026
- v0.1.3Sep 21, 2026
- v0.1.1Sep 21, 2026
評分
已滿足適用檢查項的 40%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通25 分
請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。
- 已宣告認證方式10 分
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分
請在儲存庫或註冊中心條目中宣告授權條款。