Outlook / Microsoft 365 for Claude — mail, calendar, contacts and tasks, with confirm-gated sending
About
Outlook / Microsoft 365 for Claude — mail, calendar, contacts and tasks, with confirm-gated sending
Security Report
Valid MCP server (2 strong, 1 medium validity signals). No known CVEs in dependencies. Package registry verified. Imported from the Official MCP Registry. Trust signals: trusted author (109/110 approved); 4 highly-trusted packages.
5 files analyzed · 1 issue found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Required
This plugin requests these system permissions. Most are normal for its category.
What You'll Need
Set these up before or after installing:
Environment variable: OUTLOOK_ACCESS_TOKEN
Environment variable: OUTLOOK_DISABLE_FETCHPROXY
Environment variable: OUTLOOK_API_BASE
Environment variable: OUTLOOK_WS_PORT
Environment variable: OUTLOOK_CAPTURE_TIMEOUT
Environment variable: OUTLOOK_TOKEN_CACHE
Environment variable: OUTLOOK_TOKEN_FILE
How to Install
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-chrischall-office-outlook-mcp": {
"env": {
"OUTLOOK_WS_PORT": "your-outlook-ws-port-here",
"OUTLOOK_API_BASE": "your-outlook-api-base-here",
"OUTLOOK_TOKEN_FILE": "your-outlook-token-file-here",
"OUTLOOK_TOKEN_CACHE": "your-outlook-token-cache-here",
"OUTLOOK_ACCESS_TOKEN": "your-outlook-access-token-here",
"OUTLOOK_CAPTURE_TIMEOUT": "your-outlook-capture-timeout-here",
"OUTLOOK_DISABLE_FETCHPROXY": "your-outlook-disable-fetchproxy-here"
},
"args": [
"-y",
"@chrischall/office-outlook-mcp"
],
"command": "npx"
}
}
}Documentation
View on GitHubFrom the project's GitHub 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
npm i -g @chrischall/office-outlook-mcp
Requires the Transporter Chrome extension and @fetchproxy/cli, on a
matching major version:
npm i -g @fetchproxy/cli
The first capture prints a 6-digit pair code to approve in the Transporter popup; the grant persists.
Install in opencode
opencode reads MCP servers from opencode.json (project) or
~/.config/opencode/opencode.json (global):
{
"mcp": {
"servers": {
"outlook": {
"type": "local",
"command": ["npx", "-y", "@chrischall/office-outlook-mcp"]
}
}
}
}
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.
| variable | purpose |
|---|---|
OUTLOOK_ACCESS_TOKEN | Pre-obtained bearer token. Overrides the bridge. Power users / CI only; expires in ~25h and cannot be auto-refreshed. |
OUTLOOK_DISABLE_FETCHPROXY | 1 to disable browser capture, requiring OUTLOOK_ACCESS_TOKEN. |
OUTLOOK_API_BASE | Override the REST base URL. Defaults to https://outlook.office.com/api/v2.0. |
OUTLOOK_WS_PORT | fetchproxy concentrator port. Defaults to 37149, the fleet-wide shared port. |
OUTLOOK_CAPTURE_TIMEOUT | Seconds to wait for the tab to make a readable request. Bounds the WHOLE capture, both declared hosts included. Defaults to 30. |
OUTLOOK_TOKEN_CACHE | false to stop caching the token between runs. |
OUTLOOK_TOKEN_FILE | Path to the cached token. Defaults to ~/.office-outlook-mcp/token.json. |
Tools
Read — outlook_list_folders, outlook_list_messages,
outlook_get_message, outlook_list_attachments, outlook_list_events,
outlook_get_event, outlook_list_calendars, outlook_get_profile,
outlook_get_mailbox_settings, outlook_list_contacts, outlook_list_people,
outlook_list_tasks
Write (all ask you to confirm first — see Confirmations) — outlook_send_mail,
outlook_create_draft, outlook_mark_read, outlook_move_message,
outlook_create_event
Diagnostics — outlook_healthcheck
Every read tool takes view: compact | full | raw, defaulting to compact.
Mutating tools write nothing until you confirm — 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_events and outlook_get_event 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.
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.
Confirmations
Every write asks you to confirm before anything is sent or changed. 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.
| variable | default | |
|---|---|---|
MCP_CONFIRM_MODE | ask-user | What a write does on a client that cannot show a confirmation prompt (claude.ai, Claude Desktop). ask-user: two steps — the first call does nothing and returns a preview plus a token, and the model must get your approval in chat before calling again with it. auto: the same two steps, but the model may use the token after reviewing the preview itself. refuse: writes are refused on such clients. A client that can show prompts (Claude Code) always gets the real prompt. An unrecognised value is treated as refuse. |
MCP_CONFIRM_TTL_SECONDS | 600 | How long a token stays valid. |
MCP_CONFIRM_SECRET | random per process | Signing key; set it only if tokens must survive a server restart. |
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.
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
npm install
npm run build
npm test # typecheck + suite
npm run test:coverage # CI's gate
Request shapes are pinned in docs/OUTLOOK-API.md, all
verified against a live mailbox.
License
MIT
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Git
Freeby Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
Fetch
Freeby Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
Paperclip
Freeby Paperclipai · Developer Tools
Trending hip-hop artist momentum scores across four cultural dimensions.
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
mcp-creator-python
Freeby mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.