outlook-desktop-mcp
by jr2804
README.md
# outlook-desktop-mcp
[](https://pypi.org/project/outlook-desktop-mcp/)
[](https://pypi.org/project/outlook-desktop-mcp/)
[](https://github.com/Aanerud/outlook-desktop-mcp)
**Turn your running Outlook Desktop into an MCP server.** No Microsoft Graph API, no Entra app registration, no OAuth tokens — just your local Outlook and the authentication you already have.
Any MCP client (Claude Code, Claude Desktop, etc.) can then send emails, manage your calendar, create tasks, handle attachments, and more — all through your existing Outlook session.
## Quick Start
**1. Install** (requires Python 3.13+):
```bash
pip install outlook-desktop-mcp
```
**2. Register with Claude Code:**
```bash
claude mcp add outlook-desktop -- outlook-desktop-mcp
```
**3. Open Outlook and start a Claude Code session.** That's it — tools are available immediately.
## How It Works — Platform Routing
When the server starts, it checks which operating system it is running on and takes one of two paths:
```text
outlook-desktop-mcp starts
|
entrypoint.py detects OS
(Platform.WINDOWS |
Platform.DARWIN)
|
set_backend(backend, platform)
/ \
backends/win/ backends/mac/
ComBackend AppleScriptBackend
OutlookBridge AppleScriptBridge
| |
33 tools registered 26 tools registered
| |
OUTLOOK.EXE via Microsoft Outlook
COM / STA thread via osascript
| |
Exchange / M365 Exchange / M365
```
**Both paths use your locally running Outlook app and its existing authenticated session.** No cloud credentials, no Graph API tokens — the server inherits whatever account Outlook is signed into.
### Why two backends?
Windows Outlook (Classic) exposes a rich COM automation interface — the Outlook Object Model (`MSOUTL.OLB`). This has been the standard way to programmatically control Outlook on Windows for over 20 years. It provides deep access to mail rules, categories, MAPI properties, and the full folder hierarchy.
Mac Outlook does not support COM. Instead, it exposes an AppleScript dictionary that can be driven via the `osascript` command. The AppleScript interface covers the core operations — email, calendar, tasks — but does not expose rules, categories, or certain advanced MAPI features. This is a limitation of what Microsoft chose to include in Outlook for Mac's scripting dictionary, not a limitation of this project.
The server uses a single `server.py` with a unified tool surface. Each tool is an `async def` decorated with `@mcp.tool()`. Tools that require platform-specific APIs are conditionally registered in `set_backend()` — only the backend methods they call differ. MCP clients see the same interface regardless of platform.
## Requirements
### Windows
- **Outlook Desktop (Classic)** — the `OUTLOOK.EXE` that comes with Microsoft 365 / Office. The new "modern" Outlook (`olk.exe`) does **not** support COM
- **Python 3.13+** (x64 or ARM64)
- **Outlook must be running** when the MCP server starts
Both x64 and ARM64 Windows are supported. On ARM64, all dependencies (`pywin32`, `mcp`, `pydantic-core`, `cryptography`, `cffi`, `rpds-py`) have prebuilt `win_arm64` wheels — see the [ARM64 install notes](#windows-arm64) below.
#### Outlook "Programmatic Access" security prompts
When the MCP server first touches Outlook's COM API, Outlook may show a dialog: *"A program is trying to access email address information stored in Outlook"* (the Object Model Guard / Programmatic Access prompt). This is Outlook's protection against malicious automation.
You have three options:
1. **Click "Allow access for 10 minutes"** every time you start a session. Fine for casual use.
2. **Get the antivirus status to "Valid"** in *File > Options > Trust Center > Trust Center Settings > Programmatic Access*. When that line reads `Valid`, the "Never warn me about suspicious activity" radio becomes selectable and the prompts go away. On most personal machines with current Defender this works out of the box.
3. **Apply the registry policy** in [`docs/suppress-outlook-oom-prompts.reg`](docs/suppress-outlook-oom-prompts.reg). From an **elevated** PowerShell or Command Prompt (Win+X → *Terminal (Admin)*), run:
```powershell
reg import "C:\path\to\outlook-desktop-mcp\docs\suppress-outlook-oom-prompts.reg"
```
Then fully quit Outlook (check Task Manager for stray `OUTLOOK.EXE` processes) and reopen it. This writes `AdminSecurityMode=3` and approves all `PromptOOM*` categories under `HKLM\Software\Policies\Microsoft\Office\16.0\Outlook\Security`, which Outlook honors regardless of AV status. On Intune/MDM-managed corporate devices, `HKCU\Software\Policies\...\Outlook` is locked and the HKLM keys may be overwritten on next policy sync — if `reg import` fails or the prompts come back, ask IT to push the equivalent settings via Group Policy.
> **Windows 11 ARM64 note:** Defender does not register with Outlook's `IOfficeAntiVirus` interface on ARM64, so Trust Center shows *"Antivirus status: Invalid"* and the *"Never warn me about suspicious activity"* radio stays greyed out **even when Outlook is launched as Administrator**. Option 2 is unavailable on ARM64; the `reg import` from option 3 is the only durable suppression path.
### macOS
- **Microsoft Outlook for Mac** — version 16.x or later
- **Python 3.13+**
- **Outlook must be running** when the MCP server starts
#### Required macOS permissions
The first time a tool runs, macOS will show **two permission prompts** that you must approve:
1. **Privacy & Automation** — a system dialog asks: *"python3 wants to control Microsoft Outlook"*. Click **Allow** to let the server send AppleScript commands to Outlook.
2. **Accessibility** — to read your Exchange/M365 inbox, the server uses macOS UI scripting (System Events). This requires Accessibility access for `python3`:
- Open **System Settings > Privacy & Security > Accessibility**
- Find **python3** in the list (it appears after the first prompt)
- Toggle it **on**
Without Accessibility enabled, calendar, tasks, and local folder tools will work, but listing Exchange inbox messages will return empty results.
Both permissions are one-time setup — macOS remembers them for future sessions.
## Available Tools by Platform
### Email
| Tool | Windows | macOS | Description |
|------|:-------:|:-----:|-------------|
| `send_email` | yes | yes | Send an email with To/CC/BCC, plain text or HTML body |
| `draft_email` | yes | yes | Save an email as a draft without sending (plain text or HTML body) |
| `list_emails` | yes | yes | List recent emails from any folder, with optional unread filter |
| `read_email` | yes | yes | Read full email content by entry ID or subject search |
| `search_emails` | yes | yes | Full-text search across email subjects and bodies |
| `reply_email` | yes | yes | Reply or reply-all, plain text or HTML body, preserving the conversation thread |
| `draft_reply_email` | yes | yes | Save a reply as a draft without sending (plain text or HTML body) |
| `mark_as_read` | yes | yes | Mark a specific email as read |
| `mark_as_unread` | yes | yes | Mark a specific email as unread |
| `move_email` | yes | yes | Move an email to Archive, Trash, or any folder |
| `list_folders` | yes | yes | Browse the folder hierarchy with item counts |
### Calendar
| Tool | Windows | macOS | Description |
|------|:-------:|:-----:|-------------|
| `list_events` | yes | yes | List upcoming events within a date range |
| `get_event` | yes | yes | Read full event details by entry ID |
| `create_event` | yes | yes | Create a personal calendar appointment |
| `create_meeting` | yes | yes | Create a meeting and send invitations to attendees |
| `update_event` | yes | yes | Modify an existing event's subject, time, location, etc. |
| `delete_event` | yes | yes | Delete an appointment or cancel a meeting |
| `respond_to_meeting` | yes | — | Accept, decline, or tentatively accept a meeting invite |
| `search_events` | yes | yes | Search calendar events by keyword within a date range |
### Tasks
| Tool | Windows | macOS | Description |
|------|:-------:|:-----:|-------------|
| `list_tasks` | yes | yes | List pending or completed tasks, sorted by due date |
| `get_task` | yes | yes | Read full task details including body and completion status |
| `create_task` | yes | yes | Create a new task with subject, due date, importance |
| `complete_task` | yes | yes | Mark a task as complete |
| `delete_task` | yes | yes | Remove a task |
### Attachments
| Tool | Windows | macOS | Description |
|------|:-------:|:-----:|-------------|
| `list_attachments` | yes | yes | List all attachments on an email or calendar event |
| `save_attachment` | yes | yes | Download an attachment to a local directory |
### Out of Office
| Tool | Windows | macOS | Description |
|------|:-------:|:-----:|-------------|
| `set_out_of_office` | yes | yes | Turn Out of Office auto-reply on or off |
| `get_out_of_office` | yes | — | Check whether Out of Office auto-reply is on or off |
### Categories, Rules, Accounts (Windows only)
These tools rely on COM-specific APIs (MAPI property accessors, the Rules object model, and the Categories collection) that Outlook for Mac does not expose through AppleScript.
| Tool | Windows | macOS | Description |
|------|:-------:|:-----:|-------------|
| `list_accounts` | yes | — | List all configured Outlook accounts |
| `list_categories` | yes | — | List all available color categories in Outlook |
| `set_category` | yes | — | Set or clear categories on any email, event, or task |
| `list_rules` | yes | — | List all mail rules with enabled/disabled status |
| `toggle_rule` | yes | — | Enable or disable a mail rule by name |
**Total: 33 tools on Windows, 26 tools on macOS.** (Some Windows-only capabilities — accounts, rules, categories, meeting response, OOF status query — are not exposed on macOS.)
## Architecture Details
### Windows: COM Backend (`backends/win/`)
The Windows backend lives in `backends/win/`. The bridge (`bridge.py`) runs all Outlook COM operations on a dedicated STA thread.
All Outlook COM operations run on a dedicated thread using the Single-Threaded Apartment (STA) model, as required by COM. The async MCP event loop dispatches tool calls to this thread via a queue and awaits results, keeping COM threading rules respected and the MCP protocol non-blocking.
```text
MCP tool call (async)
→ bridge.call(func, args)
→ queued to STA thread
→ func(outlook, namespace, args) executes on COM thread
→ result returned via threading.Event
→ JSON response back to MCP client
```
Each tool's inner function receives the live `Outlook.Application` and `MAPI.Namespace` COM objects and works directly with the Outlook Object Model — `GetItemFromID`, `CreateItem`, `Items.Restrict` with DASL filters, and so on.
### macOS: AppleScript Backend (`backends/mac/`)
The macOS backend lives in `backends/mac/`. The bridge (`bridge.py`) executes each tool call as a stateless `osascript` subprocess.
Each tool call builds an AppleScript string and executes it as a subprocess via `osascript`. There is no persistent connection — every call is stateless.
```text
MCP tool call (async)
→ build AppleScript string
→ asyncio.create_subprocess_exec("osascript", "-e", script)
→ parse stdout text into structured data
→ JSON response back to MCP client
```
Each tool constructs a single AppleScript that fetches all needed data in one `osascript` call (no per-message subprocess loops). Results come back as delimited text, which the server parses into the same JSON structure the Windows server produces.
**Key differences from Windows:**
- Entry IDs on macOS are **numeric** (e.g. `42`), not hex strings. They identify items within their folder context.
- Folder references use AppleScript's **locale-independent keywords** (`inbox`, `sent items`, `drafts`, `deleted items`) rather than localized folder names.
- Search uses AppleScript's `whose` clause (e.g. `messages whose subject contains "query"`) instead of DASL filters.
- User input is escaped for safe embedding in AppleScript strings to prevent script injection.
## Install from Source
### Windows (x64)
```bash
git clone https://github.com/Aanerud/outlook-desktop-mcp.git
cd outlook-desktop-mcp
python -m venv .venv
.venv\Scripts\activate
pip install uv
uv sync
python .venv\Scripts\pywin32_postinstall.py -install
```
Register from source using the launcher script:
```bash
claude mcp add outlook-desktop -- powershell.exe -Command "& 'C:\path\to\outlook-desktop-mcp\outlook-desktop-mcp.cmd' mcp"
```
### Windows (ARM64)
On ARM64, use `uv` for reliable dependency resolution with prebuilt wheels:
```powershell
git clone https://github.com/Aanerud/outlook-desktop-mcp.git
cd outlook-desktop-mcp
& "C:\Program Files\Python313-arm64\python.exe" -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install uv
uv sync
python .venv\Scripts\pywin32_postinstall.py -install
```
Register from source the same way as x64:
```bash
claude mcp add outlook-desktop -- powershell.exe -Command "& 'C:\path\to\outlook-desktop-mcp\outlook-desktop-mcp.cmd' mcp"
```
### macOS
```bash
git clone https://github.com/Aanerud/outlook-desktop-mcp.git
cd outlook-desktop-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install uv
uv sync
```
Register from source:
```bash
claude mcp add outlook-desktop -- /path/to/outlook-desktop-mcp/.venv/bin/python -m outlook_desktop_mcp
```
## Usage Examples
Once registered, just talk to Claude naturally:
- *"Show me my 10 most recent inbox emails"*
- *"Read the email from Taylor about MLADS"*
- *"Send an email to <alice@example.com> about the project update"*
- *"Draft an email to <bob@example.com> about the meeting agenda"*
- *"What's on my calendar this week?"*
- *"Create a meeting with <bob@example.com> tomorrow at 2pm for 30 minutes"*
- *"Save the attachment from that email to my Downloads folder"*
- *"Create a task to review the quarterly report, due Friday, high importance"*
- *"Mark that email as read and move it to archive"*
- *"Set Out of Office for next week"*
- *"Reply to that email and save it as a draft"*
Windows-only examples:
- *"What categories do I have? Set this email to 'Follow-up'"*
- *"List my mail rules"*
- *"Am I set as Out of Office?"*
- *"List my Outlook accounts"*
## Why Not Microsoft Graph?
| | Microsoft Graph | outlook-desktop-mcp |
|---|---|---|
| Entra app registration | Required | Not needed |
| Admin consent | Required for mail permissions | Not needed |
| OAuth token management | You handle refresh tokens | Not needed |
| Tenant configuration | Required | Not needed |
| Works offline / cached | No | Yes (reads from local cache) |
| Setup time | 30-60 minutes | 2 minutes |
| Auth requirement | **Your own OAuth flow** | **Outlook is open** |
## Project Structure
```text
outlook-desktop-mcp/
src/outlook_desktop_mcp/
entrypoint.py # Platform detection → routes to correct server
server.py # Unified MCP tool surface (33 Win / 26 Mac)
platform.py # Platform StrEnum + current_platform()
instructions.py # build_instructions(platform)
models.py # Pydantic response models
backends/
base/ # Backend ABC, BackendError, BridgeBase
win/ # Windows: COM bridge, ComBackend, errors, formatting, _types
mac/ # macOS: AppleScript bridge, AppleScriptBackend, helpers
tools/
_folder_constants.py # Outlook enums and constants (shared)
tests/
test_email_com.py # Email COM validation
test_email_mcp.py # Email MCP test
test_calendar_com.py # Calendar COM validation
test_calendar_mcp.py # Calendar MCP test
test_extras_com.py # Tasks/attachments/categories/rules/OOF COM test
test_extras_mcp.py # Tasks/attachments/categories/rules/OOF MCP test
outlook-desktop-mcp.cmd # Windows launcher script
pyproject.toml
```
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for the branching strategy and development setup.
**Versioning:** This project uses CalVer (`YYYY.M.N`) — the `VERSION` file is the single source of truth, read by `uv-dynamic-versioning` at build time. CI auto-increments the patch number on every push to `main`.
**Dev tasks:** The project uses [mise](https://mise.jdx.dev/) for task management. Run `mise install` to set up, then `mise run all` to lint, typecheck, and test.
## License
See [LICENSE](LICENSE) file.
This server cannot be deployed
Maintenance
ActivityActive
ResponsivenessNo issues