Skip to main content
Glama

outlook-mcp

An MCP server for Microsoft 365 Outlook — mail, calendar and contacts — that talks to the same internal JSON API the Outlook web app (OWA) uses: https://outlook.cloud.microsoft/owa/service.svc.

It does not use Microsoft Graph, EWS SOAP or a browser. Every tool call is a plain HTTPS request with a bearer token, so it works anywhere Node runs and needs no app registration or admin consent beyond what Outlook on the web already has.

  • 40 tools: read, search, send, reply, forward, drafts, attachments, folders, flags, categories, calendar events and invitations, contacts, directory lookup

  • Authentication through the Azure CLI by default (tokens are refreshed automatically), or any token source you provide

  • Read-only mode for safe exploration

  • Compact, normalized JSON output (UTC times, SMTP addresses, text bodies by default)

Caveat: the OWA service API is undocumented and owned by Microsoft. It has been stable for years and is what several open-source clients use, but it can change without notice. See docs/OWA-API.md for exactly what was verified and how.

Requirements

  • Node.js 20 or newer — Windows, macOS and Linux are all supported

  • A Microsoft 365 (work or school) mailbox

  • One of:

    • the Azure CLI, signed in with az login (recommended), or

    • some other way to obtain an access token for https://outlook.office.com

Related MCP server: outlook-web-mcp-server

Install

From GitHub (with the GitHub CLI)

gh repo clone uukelele/outlook-mcp
cd outlook-mcp
npm install
npm run build
az login --allow-no-subscriptions   # work/school accounts often have no Azure subscription
npm run check                       # verifies auth and prints your mailbox identity

gh itself: winget install GitHub.cli on Windows, brew install gh on macOS, or your Linux package manager — then gh auth login once.

From a local copy

npm install
npm run build
az login            # if not already signed in
npm run check

npm run check prints something like:

{ "ok": true, "auth": "azure-cli", "mailbox": "you@contoso.com", "displayName": "You", "timeZone": "GMT Standard Time", "inbox": { "total": 2535, "unread": 119 }, "readOnly": false }

Platform notes

The server has no native dependencies and runs anywhere Node 20+ runs, including Windows ARM64. A few platform details are handled for you:

  • Windows: the Azure CLI is az.cmd; the server invokes it through the shell automatically. download_attachment accepts C:\... paths, and OUTLOOK_TOKEN_COMMAND runs through cmd.exe.

  • Time zones: user-facing zones are IANA names (e.g. Europe/London), resolved via Node's bundled ICU, so Intl works the same on every platform. The mailbox's own zone stays a Windows zone ID, as Exchange expects.

  • Line endings: the repo pins LF via .gitattributes, and the test scripts use cross-env, so npm test / npm run test:live work in cmd and PowerShell as well as POSIX shells.

  • macOS: install the Azure CLI with brew install azure-cli if you don't have it.

Add it to an MCP client

OpenCode (~/.config/opencode/opencode.jsonc on macOS/Linux, %USERPROFILE%\.config\opencode\opencode.jsonc on Windows; v2 config):

{
  "mcp": {
    "servers": {
      "outlook": {
        "type": "local",
        "command": ["node", "/path/to/outlook-mcp/dist/index.js"],
        "disabled": false
      }
    }
  }
}

Windows path form: "command": ["node", "C:\\path\\to\\outlook-mcp\\dist\\index.js"].

Claude Desktop / other clients (mcpServers format; the config file is claude_desktop_config.json — see your client's docs):

{
  "mcpServers": {
    "outlook": { "command": "node", "args": ["/path/to/outlook-mcp/dist/index.js"] }
  }
}

Add "environment" / "env" entries for any settings below, e.g. "OUTLOOK_READ_ONLY": "1".

Authentication

The server needs a bearer token whose audience is https://outlook.office.com. Microsoft Graph tokens do not work (the server detects them and says so). Sources, in the order they are tried when OUTLOOK_AUTH is unset:

Source

Setting

Refresh

Static token

OUTLOOK_ACCESS_TOKEN

No — restart with a new token when it expires (~1 h)

Token file

OUTLOOK_TOKEN_FILE (raw token or JSON with accessToken/access_token)

Re-read when expired or rejected

Command

OUTLOOK_TOKEN_COMMAND (prints a token or JSON)

Re-run when expired or rejected

OWA cookie (experimental, untested)

OUTLOOK_COOKIE (+ OUTLOOK_CANARY)

No

Azure CLI (default)

OUTLOOK_AZ_TENANT optional

Automatic

The Azure CLI source runs az account get-access-token --resource https://outlook.office.com. Tokens are cached in memory until five minutes before expiry and refreshed once automatically if the service rejects one. Tokens are never logged or written to disk.

Configuration

Variable

Default

Purpose

OUTLOOK_AUTH

auto

Force a source: azure-cli, token, token-file, command, cookie

OUTLOOK_READ_ONLY

off

1 registers only the 15 read-only tools

OUTLOOK_TIMEZONE

mailbox setting

Windows time zone ID (e.g. GMT Standard Time) for times given without an offset

OUTLOOK_MAX_DOWNLOAD_MB

25

Maximum attachment size for downloads and uploads

OUTLOOK_REQUEST_TIMEOUT_MS

30000

Per-request timeout

OUTLOOK_ORIGIN

https://outlook.cloud.microsoft

OWA origin

OUTLOOK_ATTACHMENTS_ORIGIN

https://attachments.office.net

Attachment download host

OUTLOOK_ANCHOR_MAILBOX

from token (PUID:…@tenant)

Override the X-AnchorMailbox routing header

OUTLOOK_DEBUG

off

1 logs each request (action and status only) to stderr

Tools

IDs returned by one tool can be passed to any other. Folders can be given as a well-known name (inbox, sent, drafts, trash, junk, archive, …), a display name, a path like Inbox/Projects, or an ID.

Mailbox — get_mailbox_info, list_categories

Folders — list_folders, create_folder, rename_folder, move_folder, delete_folder

Reading mail

Tool

Notes

list_messages

Folder listing, newest first; filter = unread / flagged / toOrCcMe; paging via offset/nextOffset

search_messages

Text, sender, recipient, subject, date range, category, unread/flagged/attachments. See Search below

get_message

Full message as text (default) or sanitized HTML, recipients, attachment list. Does not mark read unless asked

get_thread

Whole conversation, oldest first, each message showing only its new text

Writing mail — send_message, create_draft, update_draft, send_draft, reply_message, forward_message

reply_message and forward_message send immediately unless send: false, which saves a draft and returns its ID. The original message is quoted automatically.

Triage (batched, with per-item results) — mark_messages_read, flag_messages, categorize_messages, move_messages, copy_messages, delete_messages

Attachments — list_attachments, download_attachment, add_attachment, remove_attachment

download_attachment returns text files as text, images as images and other files as an embedded resource; pass savePath to write the file to disk instead. Attachments can be added from a local path, base64 or plain text.

Calendar — list_calendars, list_events, get_event, create_event, update_event, delete_event, respond_to_event

Contacts & people — list_contacts, get_contact, create_contact, update_contact, delete_contact, search_people (organization directory and personal contacts)

Behavior worth knowing

  • Times. Results are UTC. Inputs with an offset (2026-11-02T14:00:00Z, +01:00) are exact. Inputs without one are local time — in the IANA timeZone argument if given, otherwise in the mailbox's time zone, resolved by Exchange itself (daylight saving included).

  • Search scans the newest messages of each folder (scanLimit, default 1000 per folder) and matches subject, sender, recipient display names and the ~250-character preview. It does not search full bodies or attachment contents. Server-side search was investigated and is not reachable with these tokens — see docs/OWA-API.md. Results say when a folder was only partly scanned; add after to search further back cheaply.

  • Meeting invitations. Creating an event with attendees always emails invitations — Exchange ignores "don't send" for new meetings. update_event and delete_event notify attendees unless notifyAttendees: false.

  • Sending uses Exchange's normal submission path and keeps a copy in Sent Items, exactly like Outlook on the web.

  • Safety. Tools carry MCP annotations (readOnlyHint, destructiveHint, openWorldHint) so clients can ask for confirmation. The server instructions tell the model to confirm before contacting other people and to prefer drafts. OUTLOOK_READ_ONLY=1 removes every tool that changes anything.

Development

npm run build        # compile to dist/
npm test             # 73 offline tests (fake OWA endpoint, no network)
npm run test:live    # 9 live tests against your mailbox — read-only by construction

The offline tests check payloads against the request shapes that were verified on the live service, cover the client's retry/auth/error handling, and run the full MCP protocol over an in-memory transport. The live suite starts the server in read-only mode, so write tools do not exist during the run.

src/
  index.ts            CLI entry (stdio server, --check, --help)
  server.ts           wires config → auth → client → services → tools
  config.ts           environment settings
  auth/               token sources (Azure CLI, command, file, static, cookie) and JWT helpers
  owa/client.ts       HTTP client: headers, envelope, refresh-on-401, throttling, error mapping
  owa/builders.ts     request payload builders (__type ordering, folder refs, recipients)
  owa/normalize.ts    raw OWA objects → compact JSON
  owa/time.ts         date/time parsing and time zone handling
  services/           mail, folders, attachments, calendar, contacts, mailbox
  tools/              MCP tool definitions
docs/OWA-API.md       protocol notes and verification record

Troubleshooting

Symptom

Fix

Azure CLI could not issue an Outlook token

Run az login — with a work/school account that has no Azure subscription, use az login --allow-no-subscriptions

az not found on Windows

Install with winget install Microsoft.AzureCLI (or the MSI), then reopen the terminal so az.cmd is on PATH

token is for Microsoft Graph

Request the token with --resource https://outlook.office.com

Outlook rejected the credentials … (HTTP 401)

Token expired or lacks mailbox access; with a static token, supply a fresh one

returned an HTML page instead of JSON

Not authenticated (often cookie mode); use a bearer token

… (the service rejected the request shape)

A payload the service does not accept; please report it with OUTLOOK_DEBUG=1 output

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI assistants to read, send, and manage Outlook mail locally on Windows via COM/MAPI, without cloud APIs.
    467 npm
    1
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Enables interaction with Outlook Web through its internal API, supporting email search, folder listing, email retrieval, read state changes, calendar events, and reply drafts without Microsoft Graph or COM.
    7
    1
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Reads and searches your local Microsoft Outlook desktop mailbox via COM, supporting folder listing, message/thread retrieval, attachment saving, bulk export, and draft creation without sending, requiring no OAuth or admin consent.
    -
  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to read, search, send, schedule, manage, and analyze mail across multiple IMAP/SMTP accounts, including drafts, provider-aware labels, real-time IDLE triage, verification-code capture, and calendar extraction. Runs over stdio for local clients or Streamable HTTP for networked access, with a guided setup wizard and OAuth2 support.
    -