outlook-mcp-readonly
by r-rohit
README.md
# outlook-mcp-readonly
**A read-only Outlook MCP server for Windows.** Gives Claude Desktop, Claude
Code, or any [Model Context Protocol](https://modelcontextprotocol.io) (MCP)
client safe, local access to your **classic desktop Outlook** mailbox and
calendar — no Azure/Entra app registration, no OAuth consent screen, no
cloud relay, and no tool anywhere in this codebase capable of sending,
deleting, or modifying anything in your inbox.
[](#requirements)
[](#requirements)
[](pyproject.toml)
[](LICENSE)
If you searched for an **Outlook MCP server**, an **MCP server for Outlook
email**, or a way to connect **Claude to Outlook on Windows** without
handing over write access to your mailbox — this is that project.
## Table of contents
- [Why read-only, specifically](#why-read-only-specifically)
- [Requirements](#requirements)
- [Tools exposed](#tools-exposed)
- [Installation](#installation)
- [Configure Claude Desktop](#configure-claude-desktop)
- [Configure Claude Code (CLI)](#configure-claude-code-cli)
- [Verify it's working](#verify-its-working)
- [How it works](#how-it-works)
- [Troubleshooting / exceptions](#troubleshooting--exceptions)
- [Limitations](#limitations)
- [FAQ](#faq)
- [Contributing](#contributing)
- [License](#license)
## Why read-only, specifically
Most community Outlook MCP servers bundle send/delete/move tools because
they're built for full inbox automation. This one deliberately does not.
There is **no tool in this codebase capable of sending, deleting, moving, or
otherwise modifying anything in your mailbox.** That's not a config flag you
can flip on — the code to do those things simply isn't written.
If you want to verify that yourself before trusting it with your inbox:
```bash
grep -n "\.Send(\|\.Delete(\|\.Move(\|\.Save(\|UnRead = \|\.Display(" src/outlook_readonly_mcp/*.py
```
Every match should be inside a comment or docstring, not a live call. That's
intentional — it's meant to be a two-minute audit for anyone reviewing this
before pointing it at their real mailbox.
The only thing this server writes anywhere is `save_attachment`, which saves
an attachment's bytes to a folder on **your local disk** — it doesn't touch
Outlook's data at all.
## Requirements
This project is intentionally narrow-scoped. Read this section before
installing — most setup issues come from one of these constraints.
- **Windows only.** There is no macOS or Linux support, and none is planned —
the server depends on Windows COM automation (`pywin32`), which doesn't
exist on other platforms.
- **Classic desktop Outlook only** (`OUTLOOK.EXE`, sometimes called
"Outlook (classic)" or "Outlook for Windows — legacy"). The new,
Store-distributed **"New Outlook" (`olk.exe`) is not supported** — it has
no COM object model to automate. If your Outlook shows a **"Try the new
Outlook"** toggle in the top-right corner, make sure it's switched **off**.
- **Python 3.10+**, installed on the same Windows machine.
- **Outlook must already be running and signed in.** This server attaches to
your existing, already-authenticated Outlook session — it does not manage
its own login, so there's no username/password/OAuth flow to configure.
- **Both Outlook and your MCP client (Claude Desktop or Claude Code) need to
be open at the same time.** There's no background or always-on mode.
## Tools exposed
| Tool | What it does |
|---|---|
| `list_folders` | Lists all mail folders with item/unread counts |
| `list_emails` | Lists recent email metadata (subject, sender, date, unread flag) in a folder |
| `search_emails` | Searches subject (and optionally body) for text |
| `get_email` | Full details of one email by EntryID, including body and attachment list |
| `list_attachments` | Attachment filenames/sizes for an email |
| `save_attachment` | Saves one attachment to a local folder (disk write only, not an Outlook write) |
| `get_unread_summary` | Unread count + newest unread subjects for a folder — good for a daily check-in |
| `list_calendar_events` | Calendar events (including recurring instances) in a date window |
## Installation
Run these from **Command Prompt (`cmd.exe`) or PowerShell** on Windows:
```bat
git clone https://github.com/r-rohit/outlook-mcp-readonly.git
cd outlook-mcp-readonly
python -m venv .venv
```
Activate the virtual environment — the command differs by shell:
```bat
:: cmd.exe
.venv\Scripts\activate.bat
```
```powershell
# PowerShell
.venv\Scripts\Activate.ps1
```
> If PowerShell refuses to run the activation script with a "running
> scripts is disabled on this system" error, run
> `Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass` first, then
> re-run the activation command.
Then install the package:
```bat
pip install -e .
```
If `pip install` succeeds but Outlook automation still fails with a COM
registration error, run pywin32's post-install script once (this registers
the COM support DLLs that `pip` doesn't always wire up automatically):
```bat
python .venv\Scripts\pywin32_postinstall.py -install
```
## Configure Claude Desktop
Edit `%APPDATA%\Claude\claude_desktop_config.json`:
```json
{
"mcpServers": {
"outlook-readonly": {
"command": "C:\\path\\to\\outlook-mcp-readonly\\.venv\\Scripts\\python.exe",
"args": ["-m", "outlook_readonly_mcp.server"]
}
}
}
```
Restart Claude Desktop. With Outlook also open, try asking:
> "What unread emails do I have in my inbox, and do any of them mention a deadline?"
> "What's on my calendar for the rest of this week?"
## Configure Claude Code (CLI)
```bash
claude mcp add outlook-readonly -- C:\path\to\outlook-mcp-readonly\.venv\Scripts\python.exe -m outlook_readonly_mcp.server
```
> **PowerShell gotcha:** `claude mcp add ... -- <command> <args>` is known to
> silently mis-parse arguments after `--` when run from PowerShell (an
> upstream Claude Code CLI bug on Windows). If the server fails to connect
> after adding it this way, use one of these workarounds instead:
> - Run the `claude mcp add` command from `cmd.exe` instead of PowerShell, or
> - Edit `%USERPROFILE%\.claude.json` directly and add the server under the
> top-level `mcpServers` key using the same `command`/`args` shape as the
> Claude Desktop config above.
## Verify it's working
- **Claude Desktop:** open a new chat and confirm the tool icon shows
`outlook-readonly` connected, or just ask an Outlook-related question.
- **Claude Code:** run `/mcp` inside a session — `outlook-readonly` should
show as connected.
## How it works
```
Claude Desktop / Claude Code → this MCP server (local Python, stdio)
→ win32com (pywin32) → OUTLOOK.EXE (already running) → your mailbox
```
Everything runs on your machine. No network calls are made by this server
itself (Outlook's own sync with Exchange/M365 happens independently, as it
always does).
## Troubleshooting / exceptions
| Symptom / error message | Cause | Fix |
|---|---|---|
| `pywin32 is not installed or this isn't Windows.` | Running on macOS/Linux, or `pywin32` failed to install. | This server only runs on Windows. Re-run `pip install -e .` on a Windows machine. |
| `Could not attach to Outlook via COM. Make sure classic desktop Outlook (OUTLOOK.EXE) is installed and can be launched.` | Outlook isn't running, isn't installed, or you're on the new Outlook (`olk.exe`). | Launch classic Outlook and sign in first. If you see a "Try the new Outlook" toggle, switch it **off**, then restart Outlook. |
| `Folder '<path>' not found (missing segment '<part>').` | The `folder_path` argument doesn't match your mailbox's actual folder names/nesting. | Call `list_folders` first to get the exact `/`-separated path, then pass that path verbatim. |
| COM registration / `class not registered` errors on first run | `pywin32` installed via `pip` but its COM DLLs weren't registered. | Run `python .venv\Scripts\pywin32_postinstall.py -install` (see [Installation](#installation)). |
| Server shows as disconnected in Claude Desktop/Code | Outlook isn't open, or the `command`/`args` path in your MCP config is wrong. | Confirm Outlook is running, and that the `python.exe` path points at `.venv\Scripts\python.exe` inside this project. |
| Claude Code CLI silently drops arguments after `--` on Windows | Known PowerShell parsing bug in the Claude Code CLI. | See the PowerShell gotcha note under [Configure Claude Code (CLI)](#configure-claude-code-cli). |
| Antivirus/EDR blocks the COM automation call | Some corporate endpoint security tools flag COM automation of Office apps by default. | Ask your IT/security team to allow COM automation for this script, or run it under a policy exception — this repo can't work around endpoint security by design. |
## Limitations
- Windows + classic Outlook only (no macOS/Linux, no "new Outlook").
- Only works while both Outlook and your MCP client (Claude Desktop or
Claude Code) are open — there's no background/always-on piece, by design
(matches the read-only, on-demand scope of this project).
- Reads the default mail store only; additional shared mailboxes or
secondary accounts added to the same Outlook profile aren't explicitly
tested.
- No contacts/tasks tools yet (mail + calendar only) — contributions welcome.
## FAQ
**Does this work with the new Outlook (`olk.exe`) or Outlook on the web (OWA)?**
No. Neither exposes the COM object model this server relies on. You need
classic desktop Outlook.
**Does this work on macOS or Linux?**
No — Windows only. `pywin32`/COM automation of Outlook is a Windows-only
mechanism.
**Can Claude send, delete, or reply to emails with this?**
No. There is no such tool in this codebase, and that's intentional — see
[Why read-only, specifically](#why-read-only-specifically).
**Do I need to register an Azure/Entra app or set up OAuth?**
No. This server talks to Outlook via local COM automation, not the Microsoft
Graph API, so there's no cloud app registration, client secret, or OAuth
consent screen involved.
**Does it work with Exchange/Microsoft 365 accounts, or only local PST files?**
Either — it reads through whatever account(s) your already-signed-in
Outlook client has configured, regardless of whether the mailbox is on
Exchange/M365 or a local PST/OST.
## Contributing
Issues and PRs welcome. If you're adding a tool, it must not call `.Send()`,
`.Delete()`, `.Move()`, `.Save()`, or set `.UnRead`/`.Display()` on any
Outlook item — that's the one hard rule of this project. Anything that only
reads COM properties (or writes to local disk, like `save_attachment`) is
fair game.
## License
MIT — see [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues