Skip to main content
Glama
uaixo

MCP Server for Multiple Outlook Accounts

by uaixo

MCP Server for Multiple Outlook Accounts

A local, single-connector Model Context Protocol server that lets an AI assistant operate several Outlook / Microsoft 365 mailboxes at once โ€” searching, reading, drafting, sending, and organising mail โ€” through one safety-annotated tool surface, with OAuth tokens that never leave your machine.

This is the Outlook / Microsoft Graph provider variant of a provider-neutral specification.


Project status: feature-complete (offline build)

Built with TypeScript 6.0.3; build, typecheck, tests (179), and format all green. All eight capabilities are implemented and the offline build is feature-complete. What exists today:

  • โœ… Architecture design + requirements traceability matrix (doc/).

  • โœ… Auth core: Entra credential-source discovery, MSAL public-client (consent + silent refresh), secure token store (0600/0700, atomic + cross-process-locked), account-selection registry.

  • โœ… Account-management CLI: outlook-mcp-auth connect | list | remove.

  • โœ… Microsoft Graph client: thin fetch wrapper with a per-request timeout, bounded jittered retry (with the no-duplicate-send policy), and actionable error mapping.

  • โœ… Read tools: list_accounts (C1), search_conversations (C2), read_conversation (C3) โ€” with Gmail-style search-operator translation and bounded, truncating output.

  • โœ… Write tools: create_draft (C4) and send_message (C5) โ€” recipient parsing (Display Name <addr>), header-injection stripping, allow-listed/TOCTOU-safe attachments, local outgoing-size validation, and reply threading. Small attachments ride inline; large ones (> ~3 MB) upload to the draft via a Graph upload session, so send becomes create-draft โ†’ upload โ†’ send with the final /send under the nonDuplicable retry policy โ€” a retry can never double-deliver.

  • โœ… Organise tools: list_labels (C6), create_label (C7), and organize_mail (C8) โ€” the label-decomposition fan-out that maps one neutral organise request to the right mix of Graph category-PATCH / read-state / move calls (archive, trash, junk โ€” mutually exclusive), applied per message across a conversation under a bounded concurrency limit, reporting the union of resulting labels. list_labels enumerates folders recursively (full paths).

  • โœ… Hardening + onboarding: a secret-redaction boundary so tokens/credentials can never reach the logs (NFR-SEC-6), and the operator onboarding guide (Entra app registration, unverified-app consent, the connect CLI).

Remaining: the live acceptance runs (real browser consent + real Graph calls) that the operator performs against an Entra app registration + Outlook mailbox. See doc/architecture.md ยง13.

Tests mock Microsoft Graph and MSAL. Live ยง13 acceptance โ€” real browser consent and Graph calls โ€” requires an Entra app registration + Outlook mailboxes and is run locally by the operator.


Capabilities (spec ยง5)

Tool

Purpose

Destructive?

Status

list_accounts

List connected mailboxes

No

โœ… live

search_conversations

Search a mailbox (paged)

No

โœ… live

read_conversation

Read a full conversation

No

โœ… live

create_draft

Compose a draft (not sent)

No

โœ… live

send_message

Send immediately

Yes

โœ… live

list_labels

List categories + folders

No

โœ… live

create_label

Create a category/folder

No

โœ… live

organize_mail

Tag / move / read-state

Yes

โœ… live

Plus an out-of-band account-management CLI (outlook-mcp-auth connect | list | remove).


Development

Requires Node.js โ‰ฅ 18 (developed on Node 22).

npm install
npm run typecheck      # tsc --noEmit
npm run build          # compile to dist/
npm test               # vitest (Graph/MSAL mocked)
npm run format:check   # prettier

Configuration

All operational knobs are environment variables (see .env.example):

Env var

Meaning

Default

OUTLOOK_MCP_DATA_DIR

tokens + app-registration configs

~/.outlook-mcp

OUTLOOK_OAUTH_CREDENTIALS

pin one app registration (disables discovery)

unset

OUTLOOK_MCP_ATTACHMENTS_DIR

allow-list for path attachments

unset (disabled)

OUTLOOK_MCP_LOCK_TIMEOUT_MS

token-store lock wait

12000

OUTLOOK_MCP_REQUEST_TIMEOUT_MS

per-Graph-call timeout

30000

Connecting a mailbox

For the full walkthrough โ€” Entra app registration, the unverified-app consent policy, and troubleshooting โ€” see doc/ONBOARDING.md. The short version:

  1. Register a public-client app in Entra ID with the redirect URI http://localhost and the delegated scopes Mail.ReadWrite, Mail.Send, User.Read, offline_access.

  2. Drop a credentials*.json into the data dir (or point OUTLOOK_OAUTH_CREDENTIALS at it):

    { "clientId": "<application-client-id>", "tenant": "common" }

    Multiple credentials*.json files are auto-discovered, so accounts under different app registrations each refresh with the client that authorised them.

  3. Connect, list, and remove mailboxes:

    outlook-mcp-auth connect            # opens the browser for consent
    outlook-mcp-auth connect --source acme   # pick a specific app registration
    outlook-mcp-auth list
    outlook-mcp-auth remove user@example.com

Security posture

OAuth tokens are stored only on the local machine (file mode 600 in a 700 data dir). Reading local files by path for attachments is disabled by default and only allowed from an explicit allow-list. The server never logs tokens, credentials, or message content. See doc/architecture.md ยง9.

License

MIT โ€” see LICENSE.