outlook-mcp
outlook-mcp
A local MCP server for a personal Outlook.com / Hotmail / Live account (not a work/school Microsoft 365 account). It talks to Microsoft Graph directly using an OAuth device-code login, so it runs entirely on your own machine — no third-party service sees your mail.
Tools it exposes: list_folders, inbox_summary, list_messages,
get_message, archive_message, move_message, delete_message,
mark_read, create_reply_draft, bulk variants bulk_mark_read,
bulk_move_messages, bulk_archive_messages, bulk_delete_messages
(all backed by Graph's $batch endpoint, chunked to 20 ops/call),
bulk_by_query (match + act in one call, ids never leave the server),
create_draft (compose a new draft), create_folder, find_newsletters
and unsubscribe.
Common workflows
Triage a big folder / clear old mail. Use
bulk_by_querywith asearchterm andreceived_beforeto sweep, e.g. delete old shipping notifications:action="delete", search="delivery", received_before="2026-06-01". It runsdry_runfirst (count + sample), then executes on the second call. Custom folders work by name —action="move", destination_folder="Receipts"resolves the name to its id (create it first withcreate_folderif needed).Unsubscribe from newsletters.
find_newsletterslists bulk senders and how each can be unsubscribed; callunsubscribe(ref, execute=True)per sender. RFC 8058 one-click senders are unsubscribed with a single POST (no browser);mailtosenders get an unsubscribe draft to send; link-only senders return a URL to open yourself (never auto-fetched). Then clear them withbulk_by_query(action="archive", only_newsletters=True)— theonly_newslettersfilter keeps just the bulk mail (List-Unsubscribe header) among the matches, so you can e.g. archive every newsletter older than a week. Note: unsubscribing confirms your address is live — do it for legitimate senders; for real spam, just delete/block.Draft an email.
create_draft(to, subject, body)saves to Drafts and never sends — you review and send from Outlook. (This server holdsMail.ReadWrite, which can create drafts but deliberately cannot send.)list_messagesalso takesfetch_all=Trueto page past the default 25-message limit (up tomax_results, default 500) instead of returning a single page.
Token efficiency
Graph message ids are ~150 characters of opaque base64 each, which dominates token cost when listing or bulk-triaging a mailbox. To keep tool traffic small:
Short refs. Listings return a compact
ref(e.g.m12) per message instead of the raw id. Every tool accepts a ref in place of an id and resolves it server-side, so you passm12, not a 150-char string. Refs are stable for the life of the server process; after a restart, just re-list.Lean listings.
list_messagesreturns only triage fields by default. For content, preferinclude_keywords=True(a short salient-word list per message — denser than a preview) and reach forget_messagefor a full read.include_preview=Truestill attaches the full plaintext preview when wanted.Plaintext, truncated bodies.
get_messageasks Graph for the plaintext body (not raw HTML) and truncates tomax_chars(default 4000);include_body=Falsegives metadata only.Compact bulk results. The
bulk_*tools return{"ok": <count>, "failed_count": <n>, "failed": [...]}— succeeded ids are counted, not echoed back.Filter-and-act — ids never touch the conversation.
bulk_by_querymatches messages by folder/filter_query/searchand applies one action (archive/delete/mark_read/mark_unread/move) to all of them server-side, returning just a count. This is the cheapest way to triage: no id list is ever streamed through the model. It runsdry_run=Trueby default (returns a count + small sample and does nothing) so a bad filter can't silently nuke the wrong mail — call again withdry_run=Falseto execute.
1. Create a free Azure app registration (one-time, ~10 min)
You need this to get a client_id. It costs nothing — no Azure subscription
or credit card required for this.
Go to https://portal.azure.com and sign in with your Microsoft account (your outlook.com account works fine as the sign-in).
Search for "App registrations" and click New registration.
Name it anything, e.g.
outlook-mcp-personal.Under Supported account types, choose: "Personal Microsoft accounts only" (or the "any organizational directory and personal Microsoft accounts" option — either works).
Leave Redirect URI blank for now. Click Register.
Copy the Application (client) ID shown on the Overview page — you'll need it below.
In the left nav, go to Authentication → Add a platform → Mobile and desktop applications → check the
https://login.microsoftonline.com/common/oauth2/nativeclientbox → Configure.Still on the Authentication page, scroll down to Advanced settings and set "Allow public client flows" to Yes. Save.
In the left nav, go to API permissions → Add a permission → Microsoft Graph → Delegated permissions → add
Mail.ReadWrite,MailboxSettings.Read,User.Read,offline_access(offline_access is usually pre-added). Click Add permissions. (No admin consent needed for a personal account signing into their own app.)
That's it — no client secret needed, this is a "public client."
2. Install
cd outlook-mcp
pip install -e .3. Configure
Set the client ID from step 1:
export OUTLOOK_MCP_CLIENT_ID="<your-application-client-id>"(Put this in your shell profile so it persists.)
4. Add to Claude Desktop / Cowork
Edit your Claude Desktop MCP config (Settings → Developer → Edit Config, or
directly at ~/Library/Application Support/Claude/claude_desktop_config.json
on macOS) and add:
{
"mcpServers": {
"outlook-personal": {
"command": "outlook-mcp",
"env": {
"OUTLOOK_MCP_CLIENT_ID": "<your-application-client-id>"
}
}
}
}Restart Claude Desktop. The first time you call a tool, the server prints a
device-login URL and code — open the URL in a browser, enter the code, sign
in with your outlook.com account, and approve. After that, tokens refresh
silently and you won't be prompted again (cached in
~/.outlook_mcp_token_cache.json, permissions 600).
Notes / limitations
delete_messageis a soft delete (moves to Deleted Items), matching how Graph's message DELETE actually behaves — nothing is purged permanently by this tool.create_reply_draftonly creates a draft; it never sends mail on its own.This is a personal single-user tool: the token cache is a plain JSON file on disk, meant for your own machine, not a shared/multi-user deployment.
If you ever want to turn this into a shareable connector, the gap to close is: move off device-code flow to a proper auth-code + PKCE flow with a redirect, host it somewhere, get Microsoft's app verification for the requested scopes, and register it in Claude's MCP directory.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ajs117/outlook-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server