Local MCP server for classic Outlook desktop on Windows (COM, no Azure app required).
Project description
outlook-classic-mcp
A local MCP (Model Context Protocol) server that exposes the classic Outlook desktop client — mail, folders, calendar, contacts, tasks, color categories, mail rules, and Out-of-Office status — to any MCP-aware agent (Claude Desktop, Claude Code, Cursor, Cline, Continue, Windsurf).
It talks to Outlook's COM API on Windows via pywin32, the same path
macros and Office add-ins use. Authentication piggybacks on whatever
account Outlook is already signed into — no Azure / Entra app
registration, no Microsoft Graph API, no OAuth tokens.
Requirements
- Windows 10 or 11
- Outlook desktop (Classic) — the
OUTLOOK.EXEshipped with Microsoft 365 / Office. The "new Outlook" (olk.exe) is not supported (no COM surface). - Python 3.10+ (the installer fetches Python 3.11 via
uvif you don't already have one).
You do not need to open Outlook before starting the server — the server auto-launches Outlook on its first COM call.
Install
From source (recommended for now)
git clone https://github.com/anasahmed07/Outlook-Classic-MCP.git
cd Outlook-Classic-MCP
install.bat
install.bat will:
- Install
uv(Astral's Python installer) if it isn't present. - Create
.venv\with Python 3.11. - Install the package in editable mode (
pip install -e .). - Pre-warm the pywin32 typelib cache.
- Launch the smart client installer (next section).
From PyPI (once published)
pip install outlook-classic-mcp
python -m outlook_mcp.scripts.install_to_clients
Smart client installer
scripts/install_to_clients.py detects which MCP clients are
installed on your machine and shows a checkbox menu:
Select which clients to register outlook-mcp with:
[ ] 1. Claude Desktop C:\Users\you\AppData\Roaming\Claude\claude_desktop_config.json
[ ] 2. Claude Code (via `claude` CLI)
[ ] 3. Cursor C:\Users\you\.cursor\mcp.json
Type a number to toggle, 'a' to select all, 'n' for none,
'enter' to confirm, 'q' to quit without changes.
For each toggled client it deep-merges
mcpServers.outlook = {"command": ".venv/Scripts/python.exe", "args": ["-m", "outlook_mcp"]}
into the right config (or runs claude mcp add for Claude Code).
Existing files are snapshotted to <file>.bak first. Re-running is
idempotent — it updates the entry instead of duplicating it.
Supported clients: Claude Desktop, Claude Code, Cursor, Cline, Continue, Windsurf.
Tools
30 tools across 9 categories, all prefixed outlook_*.
| Category | Tools |
|---|---|
list_mails, search_mails, get_mail, send_mail, reply_mail, forward_mail, move_mail, delete_mail, mark_mail, save_attachments |
|
| Folders | list_folders, create_folder |
| Calendar | list_events, get_event, create_event, update_event, delete_event, respond_event |
| Contacts | list_contacts, search_contacts, get_contact |
| Tasks | list_tasks, create_task, complete_task |
| Categories | list_categories, set_category |
| Rules | list_rules, toggle_rule |
| Out-of-Office | get_out_of_office |
| Account | whoami — sanity check; shows the bound mailbox |
Conventions
Folder references can be:
- A well-known name:
inbox,sent,drafts,deleted,outbox,junk,calendar,contacts,tasks,notes - A slash path:
Inbox/Projects/Acme - A path qualified by store name:
Mailbox - you@example.com/Inbox/Projects/Acme
Use outlook_list_folders to discover paths.
Dates / times are ISO-8601 strings (2026-04-25T14:30:00).
Without a timezone they're treated as local time (what Outlook stores).
Item IDs are Outlook EntryID strings. Read tools return them
on every item; pass them back to detail / edit / delete tools.
Response format — most read tools accept response_format:
markdown(default) — pretty rendered outputjson— full structured data
Errors are raised, so the MCP host marks the response
isError: true. Error messages try to suggest a corrective next step.
Filesystem paths for attachments= and output_dir= must be
absolute and under the user profile (default sandbox). Set
OUTLOOK_MCP_ALLOW_ANY_PATH=1 to disable the sandbox if you legitimately
need to read or write outside %USERPROFILE%.
Architecture
+-------------------+
stdio <---> | FastMCP server | <-- one per process
+---------+---------+
|
await bridge.call(...)
|
v
+-------------------+
| OutlookBridge | persistent STA thread
| - one Dispatch | single Outlook.Application
| - work queue | handle, reused by every call
+---------+---------+
|
Outlook COM (auto-launches OUTLOOK.EXE if needed)
The MCP event loop never blocks on COM, and COM only ever sees the one STA thread it needs. This is faster than per-call dispatch and the Outlook process stays warm across calls.
Development
.venv\Scripts\activate
pip install -e .[dev]
pytest
Smoke test the running server with the MCP inspector:
npx @modelcontextprotocol/inspector .venv\Scripts\python.exe -m outlook_mcp
The inspector mangles backslashes on Windows — use forward-slash paths if you hit "ENOENT" errors.
Publish to PyPI:
publish.bat
(TWINE_USERNAME=__token__, TWINE_PASSWORD=<pypi-token>.)
Notes & caveats
- The first call after a cold start takes a few seconds — Outlook's COM surface boots up. After that, calls are fast (one Dispatch handle is reused).
- Outlook auto-launch relies on standard COM behavior. On tightly-locked-down machines (UAC, group policy blocking COM activation), open Outlook manually and try again.
- Send / reply / forward / delete may trigger Outlook's "Programmatic Access" security prompts on some corporate machines. If your IT policy blocks programmatic send entirely, write tools will fail — read tools still work.
- Some properties (e.g.
SenderEmailAddressfor Exchange addresses) come back asEX:/O=...distinguished names rather than SMTP. Usefrom_addresssubstring matching instead of exact equality. - Toggling mail rules modifies live rules immediately — there is no
staging buffer. Confirm the rule name with
outlook_list_rulesbefore callingoutlook_toggle_rule. - This server is local-only. Do not expose it over a network.
Troubleshooting
"Outlook COM thread did not become ready" — Outlook didn't auto-launch. Open it manually, sign in, then restart the MCP client so it re-spawns the server.
Inspector shows ENOENT for the python path — known Windows
quirk; use forward slashes (C:/Users/you/...) instead of
backslashes.
Send / reply gets blocked silently — Outlook → File → Options → Trust Center → Programmatic Access. The setting that works while you're using the server is "Never warn me about suspicious activity (not recommended)" — or have IT add the Python interpreter as a trusted publisher.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file outlook_classic_mcp-0.2.0.tar.gz.
File metadata
- Download URL: outlook_classic_mcp-0.2.0.tar.gz
- Upload date:
- Size: 27.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80178c8bbd5df95b7768aabb22e53534098f53eaf9d38d5f8fedc1e8c74e48d4
|
|
| MD5 |
70b378401c86aaaf17bb6eeaf1c4a56b
|
|
| BLAKE2b-256 |
e798abc91390ed9ba1957357a607c978f4eff04e257b836dee1ee3b27eafabe5
|
Provenance
The following attestation bundles were made for outlook_classic_mcp-0.2.0.tar.gz:
Publisher:
publish.yml on anasahmed07/Outlook-Classic-MCP
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
outlook_classic_mcp-0.2.0.tar.gz -
Subject digest:
80178c8bbd5df95b7768aabb22e53534098f53eaf9d38d5f8fedc1e8c74e48d4 - Sigstore transparency entry: 1391654861
- Sigstore integration time:
-
Permalink:
anasahmed07/Outlook-Classic-MCP@29b63206bf7f79051ee1eefe277c3c48e36d18e6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/anasahmed07
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@29b63206bf7f79051ee1eefe277c3c48e36d18e6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file outlook_classic_mcp-0.2.0-py3-none-any.whl.
File metadata
- Download URL: outlook_classic_mcp-0.2.0-py3-none-any.whl
- Upload date:
- Size: 37.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bc604d25683284a014859a55a63eea9b52ac5fbed33f201edf4cc386e552895c
|
|
| MD5 |
4fab3bacda07ebb9bff11c30d16137b3
|
|
| BLAKE2b-256 |
71d6394a0659e5f199a2956289eb7ab179da0f50e9f31c24f093b52f34b1822f
|
Provenance
The following attestation bundles were made for outlook_classic_mcp-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on anasahmed07/Outlook-Classic-MCP
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
outlook_classic_mcp-0.2.0-py3-none-any.whl -
Subject digest:
bc604d25683284a014859a55a63eea9b52ac5fbed33f201edf4cc386e552895c - Sigstore transparency entry: 1391654871
- Sigstore integration time:
-
Permalink:
anasahmed07/Outlook-Classic-MCP@29b63206bf7f79051ee1eefe277c3c48e36d18e6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/anasahmed07
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@29b63206bf7f79051ee1eefe277c3c48e36d18e6 -
Trigger Event:
push
-
Statement type: