Outlook MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Outlook MCP Serversearch my inbox for emails from Sarah about the budget"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Outlook MCP Server
Self-hosted MCP server that exposes a personal Outlook.com account (mail + calendar, via Microsoft Graph) to Claude.ai (or any MCP client) via a standard OAuth 2.0 flow — no pre-generated credentials, and no tokens persisted to disk (they live only in memory — see How it works).
Personal accounts only. This deliberately uses Microsoft's consumers-tenant
endpoint, which structurally excludes work/school (Azure AD) accounts — only
outlook.com / hotmail.com / live.com-style personal Microsoft accounts can ever sign in.
Sibling project to gmail-mcp-proxy, which does the same thing for Gmail — see Outlook vs Gmail below for where the two diverge.
Quick Links
Related MCP server: Outlook MCP Server
How it works
Claude.ai ──[OAuth]──► This server ──[OAuth]──► Microsoft
Claude.ai ──[MCP]────► This server ──[Graph API]──► Outlook mail / calendarThe server acts as an OAuth proxy: it presents itself as an OAuth 2.0 authorization server to Claude, and internally delegates authentication to Microsoft. After the user grants access, Microsoft Graph tokens are stored server-side (in memory) and injected per-request. A short-lived JWT is issued to Claude as the bearer token.
Multiple accounts — add the server twice in Claude with different alias URLs
(/personal/mcp, /family/mcp). Each session is isolated; authenticate each with a
different Microsoft account.
Tools
Tool | Description |
| Outlook account profile |
| Search with Graph's |
| Full message with decoded body and attachment metadata |
| All messages in a conversation (Outlook's equivalent of a Gmail thread), oldest first |
| Download an attachment's bytes (base64) by |
| Send, or reply within a conversation via |
| Save a draft |
| List drafts |
| Send an existing draft |
| Replace the content of an existing draft |
| Permanently delete a draft |
| Top-level folders by default; pass |
| Create a folder, optionally nested |
| Rename a folder |
| Permanently delete a folder |
| Re-parent a folder (and its contents/subfolders) under another folder |
| The mailbox's master category list (color-coded tags) |
| Add/remove categories on a message |
| Move a message to another folder |
| Move a message to Junk Email |
| Move a message to Deleted Items |
| All calendars |
| Events in a time window (defaults to now–+30 days) |
| Search events by keyword |
| Single event by ID |
Calendar is read-only in this version, and there are no Contacts tools — both are easy additions on Graph, deliberately deferred; see Outlook vs Gmail.
Prerequisites
A GitHub account with this repo forked (or cloned) into it
An Azure account (free) — used only to register an OAuth app, no billing needed
A place to run the server with HTTPS — self-hosting (Python 3.12+ or Docker) behind Tailscale or your own reverse proxy is the documented path; see Setup
Configuration
Variable | Description |
| Azure App registration Application (client) ID |
| Azure App registration client secret — expires within 24 months, see Notes |
| Secret for signing session JWTs (any random string) |
| Public base URL, no trailing slash, e.g. |
| Optional. Comma-separated allowlist of OAuth redirect URIs |
| Optional. Python logging level ( |
| Optional. Comma-separated list of connector aliases (e.g. |
| Optional. Total attempts (1–5) for an outbound Graph API call (read or write) before giving up on a retryable status (429/5xx). Defaults to |
| Optional. Max attachment size (1–25MB, decoded) |
| Optional. Port the server listens on. Defaults to |
Read-only accounts
To connect an account you want Claude to only ever read from — never send, delete, or
modify — add its alias to READ_ONLY_ALIASES, e.g. READ_ONLY_ALIASES=family for a
connector added at /family/mcp. That account's Microsoft OAuth grant will only ever
request Mail.Read/Calendars.Read — no write scope is ever issued for it, so even a
bug in this server can't make it send or delete anything; Graph rejects it regardless.
The server also refuses write tool calls itself with a clear error, as a second layer.
Enforcement is server-side and unconditional — which alias a request comes in
through is derived from the URL path on every single request, both at /authorize
(when deciding which Microsoft OAuth scopes to request) and on every /mcp call
afterward, not something the client asserts. A restricted alias stays restricted
even if its bearer token is ever presented to a different connector's endpoint.
Development
pip install -r requirements-dev.txt
pytest # test suite
ruff check . # lint
mypy # type check (config in pyproject.toml)Tests mock all Graph API calls (via respx) and cover the pure-logic helpers (PKCE,
alias parsing, recipient parsing) plus tool behavior that's easy to get wrong — the
consumers-tenant-only OAuth requirement, refresh-token rotation, read-only enforcement,
and move_message's new-id semantics. No live Microsoft credentials needed to run them.
Outlook vs Gmail
Where this diverges from the Gmail sibling project:
Harder / more work:
Categories need read-modify-write (no atomic add/remove on Graph), vs. Gmail's atomic label add/remove.
Calendar listing needs two different endpoints (
calendarViewfor time-range/recurrence-expansion vs.events/$search), vs. Gmail's single consistent surface.move_messagereturns a new message id — anything holding the old id afterward will fail. Gmail message ids never change.move_folder, by contrast, keeps the folder's original id — confirmed by live testing; onlyparentFolderIdchanges.Folder hierarchy is a real tree (
parentFolderId), vs. Gmail's flat/-named labels.Two account-registration facts must be exactly right, with no forgiving fallback: the
consumers-tenant-only endpoint, and "Personal Microsoft accounts only" in Azure Portal — get either wrong and work/school accounts could authenticate.The Azure client secret expires within 24 months (Google's doesn't) — needs a calendar reminder, not a code fix.
Refresh tokens roll on every use and expire after ~90 days of account inactivity — a different failure mode than Google's, and the rotated token must always be re-stored.
Easier / genuine advantages:
read_messageis simpler —body.content/body.contentTypearrive as ready JSON, no MIME-tree walking or base64url decoding.Attachment ids are stable across repeated reads of the same message — no Gmail-style partId/attachmentId mismatch bug to work around.
Reply is a native Graph action (
/reply) — no manualIn-Reply-To/ReferencesMIME header construction.$searchon messages returns full fields per hit already, with no separate enrichment round-trip needed (Gmail'ssearch_emailsneeds one).Graph explicitly documents and honors
Retry-After— a more reliable throttling signal than Gmail's docs provide.Calendar write, free/busy,
findMeetingTimes, and a full Contacts API are all natively easy on Graph — deliberately deferred here, not technical gaps.
Notes
Sessions are stored in memory — a server restart requires re-authentication in Claude.ai
Runs as a single process — don't scale to multiple replicas or
uvicorn --workers N. Session/state stores are per-process in-memory, so a request landing on a different process than the one that authenticated it would fail as if unauthenticated.Microsoft access tokens are refreshed automatically using the stored refresh token, which Microsoft frequently rotates on every use — the server always stores whatever comes back.
Personal Microsoft account refresh tokens roll on a ~90-day inactivity window — an account genuinely unused for that long needs the same re-auth as after a restart.
The Azure app registration's client secret expires within 24 months. Rotate it in Azure Portal → your app → Certificates & secrets before then, update
MS_CLIENT_SECRET, and restart the service.The server issues 30-day JWTs; Claude re-authenticates when they expire.
This server cannot be deployed
Related MCP Connectors
Permissioned access to Outlook, OneDrive and Teams via the user's own Microsoft account
Manage Microsoft 365 email, calendar, contacts and inbox rules via the Graph API with OAuth 2.0.
Connect any mailbox to Claude, ChatGPT & AI: read, send, reply, schedule & search emails.
Email infrastructure for AI agents — send, receive, search, and reply to email over MCP.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceConnects Claude with Microsoft Outlook through the Graph API, providing comprehensive email, calendar, folder management, and rules creation capabilities.19-
- AlicenseNot gradedqualityCmaintenanceConnects Claude to Microsoft Outlook through the Microsoft Graph API, enabling email management (list, search, read, send) and calendar operations (list, create, accept, decline, delete events) via OAuth 2.0 authentication.1MIT
- AlicenseAqualityAmaintenanceLets Claude read email and read/write calendar events for personal Microsoft accounts (Hotmail/Outlook/Live) via Microsoft Graph API, filling the gap where the official connector only supports work/school accounts.6MIT
- AlicenseAqualityCmaintenanceA local MCP server that connects Claude Desktop to a personal Hotmail/Outlook.com mailbox via Microsoft Graph API, enabling email management, rule handling, and composing messages.25MIT