DEV Community

Joseph Solomon
Joseph Solomon

Posted on

Claude Code to Outlook via pywin32 — no MCP, no permission, no problem.

I work on a locked-down enterprise Windows machine. Can't register an Azure app. Can't install browser extensions. Managed device — IT controls what's approved.

I still needed Claude Code to read and draft my emails.

The fix turned out to be simpler than I expected.


What COM automation actually is

Outlook Desktop on Windows exposes a COM interface. It's been there since the 90s. Any program running on the same machine can connect to it, provided Outlook is open and signed in.

That's the whole authentication model: if Outlook is running, you're in.

pywin32 wraps this interface in Python:

import win32com.client
outlook = win32com.client.Dispatch("Outlook.Application")
inbox = outlook.GetNamespace("MAPI").GetDefaultFolder(6)
Enter fullscreen mode Exit fullscreen mode

One import. One dispatch call. You're talking to Outlook and you have access to almost anything you can do with a mouse and keyboard.


The bridge pattern

Claude wrote a single Python file — outlook_bridge.py — that exposes Outlook commands as CLI subcommands and returns JSON.

Claude Code calls it via Bash:

python outlook_bridge.py list --count 10 --pretty
python outlook_bridge.py search "renewal" --folder inbox
python outlook_bridge.py reply <entry_id> --body "Noted, will revert." --draft
Enter fullscreen mode Exit fullscreen mode

Output:

[
  {
    "Subject": "Q2 renewal — action required",
    "SenderName": "Alice Tan",
    "ReceivedTime": "2026-06-12T09:14:00",
    "Unread": true,
    "BodyPreview": "Following up on the renewal terms we discussed..."
  }
]
Enter fullscreen mode Exit fullscreen mode

Claude processes the JSON, reasons over it, and calls the next command in the chain. Read a thread for context → draft a reply → hand it back for review. All without leaving the Claude Code conversation.


Why not MCP?

I considered it. MCP is worth it when you need a long-running server process and cross-client compatibility.

For this use case, it wasn't worth it — for three reasons.

COM object lifetime. A long-running MCP server has to maintain the COM connection across calls, handle Outlook restarts, and manage session state. A direct Bash call has none of that: Claude shells out, Python grabs the running Outlook process, JSON comes back, process exits cleanly.

Rate limits. MCP servers that wrap an API (like Microsoft Graph) inherit that API's throttling. Broad-spectrum queries — pulling a large folder, searching across multiple mailboxes — hit those limits fast. The bridge talks directly to the local COM interface. No API in the middle, no rate limit.

Token efficiency. The bridge returns exactly what I define: a JSON object with the fields I want. MCP tool responses carry protocol overhead on top of the payload. For high-frequency operations inside a single conversation, that adds up.

One Python file. No daemon. No protocol overhead.

If the constraints above don't apply to your setup, an MCP server is a reasonable alternative. For a local single-user workflow on Windows, this is the shorter path.


Why not Microsoft Graph?

Graph works cross-platform and headless. The trade-off: Azure app registration, OAuth consent flow, ongoing token refresh. On a managed enterprise device, that registration process may not be available to you.

COM requires none of it. You're riding the session that's already open on your machine.


The permission setup

One entry in .claude/settings.json:

{
  "permissions": {
    "allow": ["Bash(python *outlook_bridge.py*:*)"]
  }
}
Enter fullscreen mode Exit fullscreen mode

Claude can call the bridge and nothing else. No open-ended Bash access required.

send, reply, and forward all work. My default is --draft — Claude saves to Drafts and I review before sending. Accidental sends are still a real risk when you're chaining operations inside a conversation. The CLAUDE_INTEGRATION.md in the repo has a CLAUDE.md snippet that sets draft-by-default as a standing instruction.


What it covers

  • list — recent emails in any folder
  • search — by keyword, sender, subject
  • read — full body + attachment list
  • send / reply / reply-all / forward — with --draft support
  • move / delete
  • cal-list — calendar events in a date range, including shared calendars

Limits

Worth stating plainly:

  • Windows only. COM is a Windows interface.
  • Classic Outlook Desktop App only — not the new Outlook web wrapper, not OWA, not Mac.
  • Outlook must be open and signed in when the script runs.
  • Single machine, single Outlook profile. Not for headless or multi-mailbox server scenarios.

If any of those constraints rule you out, use Microsoft Graph.


The repo

github.com/ChiefStarKid/claude-outlook-bridge

AGENTS.md and llms.txt are in there for anyone who wants to wire this into a coding agent automatically.

Top comments (0)