A Model Context Protocol server bridging Claude to Microsoft Outlook + Teams (email, calendar, contacts, tasks, files, Teams meeting recordings + transcripts), deployed on Cloudflare Workers.
Fork this repo, deploy to your own Cloudflare account, register a Microsoft Azure AD app, point Claude.ai at your worker, and Claude can read and write your Microsoft 365 data through natural language.
Built on @bashco/mcp-toolkit — OAuth, per-client bearer tokens, rate limiting, structured logging, typed tool dispatch are all handled by the shared library.
- Mail: list emails, read email, search, reply, forward, delete, send, move between folders, create draft, update draft, send draft, schedule send
- Calendar: list events, list event occurrences, create, update, delete, cancel event, respond to event
- Contacts: list, create contact
- Tasks: list task lists, list tasks, create task
- Files: list files, share file
- Teams meetings: list recent recordings (the discovery starting point — finds meetings that have content in the past N days, no inputs needed), find online meeting, list meeting recordings, list meeting transcripts, get transcript content. Each per-meeting tool accepts any of
meeting_id,calendar_event_id, orjoin_url— so scheduled meetings (resolved via event), ad-hoc / Meet-now calls (resolved via join URL pasted from the Teams chat), and direct id lookups all work. - Conversation: get conversation (full thread)
- Settings: get mailbox settings, set out-of-office
Full live catalogue at the tools/list MCP endpoint after deploy.
Two layers:
- Claude.ai ↔ your worker — standard MCP OAuth 2.0 + PKCE flow. Each Claude client gets a unique bearer token; your
MCP_APPROVAL_CODEis what you paste at/authorizeonce to mint that bearer. - Your worker ↔ Microsoft Graph — proxied OAuth. You authorise Microsoft once by visiting
/oauth/starton your deployed worker; refresh tokens are stored encrypted at rest in Cloudflare KV. Refresh happens automatically.
- A Cloudflare account (free plan works)
- Wrangler CLI installed and logged in (
wrangler login) - Node.js 22+
- A Microsoft account (personal, work, or school) with access to register Azure AD apps at entra.microsoft.com
git clone https://github.com/<your-username>/outlook-mcp
cd outlook-mcp
npm installwrangler kv:namespace create OAUTH_KVWrangler prints something like:
🌀 Creating namespace with title "outlook-mcp-OAUTH_KV"
✨ Success! Add the following to your configuration file:
[[kv_namespaces]]
binding = "OAUTH_KV"
id = "abc123def456..."
Edit wrangler.jsonc and replace the existing id under kv_namespaces with what wrangler just printed.
-
Go to entra.microsoft.com → Identity → Applications → App registrations → New registration
-
Name: anything (e.g. "Claude Outlook MCP")
-
Supported account types:
- "Accounts in this organizational directory only" if you want to restrict to one tenant
- "Accounts in any organizational directory and personal Microsoft accounts" for the broadest support
-
Redirect URI: leave blank for now — you'll come back after Step 6
-
Click Register
-
From the app's Overview page, record:
- Application (client) ID → this is your
MICROSOFT_CLIENT_ID - Directory (tenant) ID → this is your
MICROSOFT_TENANT_ID(or use the stringcommonfor multi-tenant + personal account support)
- Application (client) ID → this is your
-
API permissions → Add the following Microsoft Graph delegated permissions:
Mail.ReadWrite,Mail.SendCalendars.ReadWriteContacts.ReadWriteTasks.ReadWriteFiles.Read.All(orFiles.ReadWrite.Allif you want write-capable file tools)User.Readoffline_access(required for refresh tokens)MailboxSettings.ReadWriteSites.Read.AllOnlineMeetings.ReadOnlineMeetingRecording.Read.All— admin consent requiredOnlineMeetingTranscript.Read.All— admin consent required
After adding the two
.Read.Allpermissions, click "Grant admin consent for [tenant name]" on the API permissions page. Without admin consent, the meeting recording / transcript tools will 403. -
Certificates & secrets → New client secret → record the value (in 1Password). This is your
MICROSOFT_CLIENT_SECRET. You can only see it once — copy immediately.
Edit wrangler.jsonc and replace both:
vars.MICROSOFT_CLIENT_ID— with the Application ID from Step 3.6vars.MICROSOFT_TENANT_ID— with the Directory ID from Step 3.6 (orcommon)
Generate a fresh approval code:
openssl rand -base64 32Store it in a password manager, then push to Cloudflare:
wrangler secret put MCP_APPROVAL_CODE # paste the value from above
wrangler secret put MICROSOFT_CLIENT_SECRET # from Step 3.8| Secret | Purpose |
|---|---|
MCP_APPROVAL_CODE |
One-time code you paste at /authorize to mint a Claude bearer. Also used as the encryption secret for upstream Microsoft tokens at rest — rotating it invalidates stored tokens and forces clean Microsoft re-auth. |
MICROSOFT_CLIENT_SECRET |
Your Azure AD app's client secret. |
npm run deployWrangler prints your worker URL — something like https://outlook-mcp.<your-account>.workers.dev. Save it.
Two updates needed:
a) Edit wrangler.jsonc — under vars, replace WORKER_URL with the URL from Step 6.
b) In the Azure AD app (entra.microsoft.com → your app → Authentication → Add a platform → Web), set the redirect URI to <your-worker-url>/oauth/callback. Without this, Microsoft will reject the OAuth flow.
Then redeploy:
npm run deployIn your browser, visit <your-worker-url>/oauth/start. Paste your MCP_APPROVAL_CODE. You'll be redirected to Microsoft to sign in and grant the scopes from Step 3.7. After consenting, your encrypted upstream tokens land in OAUTH_KV. Refresh happens automatically thereafter.
You can confirm the connection by visiting <your-worker-url>/oauth/status — should say connected: true.
- In Claude.ai, go to Settings → Integrations → Add MCP server
- Server URL:
<your-worker-url>/mcp - Claude.ai redirects you to your worker's
/authorizepage - Paste your
MCP_APPROVAL_CODEand confirm - You're connected — Claude now has the 33 Outlook + Teams tools available
cp .dev.vars.example .dev.vars # fill in MCP_APPROVAL_CODE + MICROSOFT_CLIENT_SECRET; .dev.vars is gitignored
npm test # 48 tests via vitest with workers pool
npm run typecheck # tsc --noEmit
npm run dev # wrangler dev — local at http://localhost:8787GET /.well-known/oauth-authorization-server— OAuth metadata (public)GET /.well-known/oauth-protected-resource— Resource metadata (public)GET /authorize— Approval-code paste page (public)POST /approve— Approval-code submission (rate-limited)POST /token— OAuth token exchange (rate-limited)POST /register— Dynamic client registration per RFC 7591 (rate-limited)GET /oauth/start— Begin Microsoft OAuth flow (gated by MCP_APPROVAL_CODE)GET /oauth/callback— Microsoft OAuth redirect targetGET /oauth/status— Check connection state (gated by MCP_APPROVAL_CODE)POST /mcp— JSON-RPC tool dispatch (bearer-protected, rate-limited)
- Cloudflare Workers (compatibility_date
2025-04-28,nodejs_compat) - TypeScript (strict)
- Hono v4
- Zod v4
- Vitest with
@cloudflare/vitest-pool-workers(48 tests) @bashco/mcp-toolkit— shared OAuth/crypto/rate-limit/dispatch plumbing
- Two-pass HTML sanitiser with entity normalisation on outbound email previews
- SSRF guard with 32-bit-IP normalisation on outbound HTTP
- Microsoft Graph odata error envelope parsing for structured error returns
- Per-domain tool files under
src/tools/(mail, calendar, contacts, tasks, files, meetings, settings) for auditability
.github/workflows/deploy.yml runs vitest run on every push to main, then deploys to Cloudflare. To enable on your fork, set two repository secrets:
CLOUDFLARE_API_TOKEN— create at dash.cloudflare.com/profile/api-tokens (use the "Edit Cloudflare Workers" template)CLOUDFLARE_ACCOUNT_ID— find at the bottom-right of your Cloudflare dashboard
Issues and PRs welcome at github.com/doublebash/outlook-mcp.
For changes to the underlying OAuth/crypto/rate-limit code, the toolkit lives at github.com/doublebash/mcp-toolkit — file issues there.
Found a vulnerability? Please don't open a public issue. Open a private security advisory on GitHub.
MIT — Copyright (c) 2026 Bashar Basheer.