MCP server for Microsoft Outlook — email, calendar, folders, and inbox rules via Microsoft Graph API
Find a file
2026-06-21 20:53:30 -04:00
auth Initial commit: Outlook MCP Server v1.0.0 2026-06-21 19:39:31 -04:00
calendar fix(outlook-mcp): calendar list uses configured timezone and merge duplicate select fields 2026-06-21 20:53:30 -04:00
email fix(outlook-mcp): P0 audit fixes 2026-06-21 20:28:11 -04:00
folder Initial commit: Outlook MCP Server v1.0.0 2026-06-21 19:39:31 -04:00
rules fix(outlook-mcp): P0 audit fixes 2026-06-21 20:28:11 -04:00
tools Initial commit: Outlook MCP Server v1.0.0 2026-06-21 19:39:31 -04:00
utils fix(outlook-mcp): calendar list uses configured timezone and merge duplicate select fields 2026-06-21 20:53:30 -04:00
.env.example v1.0.1: Fix accept-event registration, edit-rule-sequence import, timezone config 2026-06-21 19:43:54 -04:00
.gitignore Initial commit: Outlook MCP Server v1.0.0 2026-06-21 19:39:31 -04:00
config.js fix(outlook-mcp): calendar list uses configured timezone and merge duplicate select fields 2026-06-21 20:53:30 -04:00
index.js Initial commit: Outlook MCP Server v1.0.0 2026-06-21 19:39:31 -04:00
package-lock.json Initial commit: Outlook MCP Server v1.0.0 2026-06-21 19:39:31 -04:00
package.json v1.0.1: Fix accept-event registration, edit-rule-sequence import, timezone config 2026-06-21 19:43:54 -04:00
README.md v1.0.1: Fix accept-event registration, edit-rule-sequence import, timezone config 2026-06-21 19:43:54 -04:00

Outlook MCP Server

A Model Context Protocol server that exposes Microsoft Outlook email, calendar, folders, and inbox rules through the Microsoft Graph API.

Built for use with AI agents (Claude, Hermes, etc.) that support the MCP standard — gives your assistant the ability to read, search, and send email, manage calendar events, organize folders, and automate inbox rules.

Features

  • Email: List, search, read, send, and reconstruct full conversation threads with quoted-reply stripping and signature deduplication
  • Calendar: List, create, decline, cancel, and delete events
  • Folders: List (flat or hierarchical), create, and move emails between folders
  • Inbox Rules: List, create, and reorder execution priority of inbox rules
  • Authentication: MSAL device code flow with persistent token cache — no browser redirect needed
  • Test Mode: Mock data layer for development without a real Microsoft account

Quick Start

Prerequisites

  • Node.js ≥ 14
  • A Microsoft Azure App Registration (see Azure Setup below)

Install

git clone <repo-url> outlook-mcp
cd outlook-mcp
npm install

Configure

cp .env.example .env
# Edit .env and set MS_CLIENT_ID

Run

npm start

The server communicates over stdio (standard MCP transport). Your MCP client (Claude Desktop, Hermes, etc.) launches it as a subprocess.

Authenticate

When you first use the server, call the authenticate tool. It will return a URL and a short code:

1. Open: https://microsoft.com/devicelogin
2. Enter code: ABC123XYZ

You have 15 minutes to complete sign-in.

Visit the URL on any device, enter the code, and sign in with your Microsoft account. Tokens are cached to disk (~/.outlook-mcp-tokens.json) and silently refreshed by MSAL — you won't need to re-authenticate until the refresh token expires.

To check auth status without side effects, use check-auth-status (not authenticate).

Azure App Registration

This server uses MSAL device code flow with a public client app registration.

  1. Go to Azure Portal → App RegistrationsNew registration
  2. Name it (e.g. "Outlook MCP")
  3. Supported account types: Accounts in any organizational directory and personal Microsoft accounts (multi-tenant)
  4. Redirect URI: leave blank (device code flow doesn't need one)
  5. After creation, go to Authentication → scroll to Advanced settings → enable Allow public client flows
  6. Go to API permissions → add Delegated Microsoft Graph permissions:
    • Mail.Read
    • Mail.ReadWrite
    • Mail.Send
    • User.Read
    • Calendars.Read
    • Calendars.ReadWrite
    • MailboxSettings.ReadWrite
    • offline_access
  7. Copy the Application (client) ID — that's your MS_CLIENT_ID

No client secret is needed for public client apps.

Configuration

All configuration is via environment variables:

Variable Required Default Description
MS_CLIENT_ID Yes Azure app registration client ID
MS_CLIENT_SECRET No Only for confidential client apps; leave blank for public client
USE_TEST_MODE No false Use mock data instead of real API calls
DEBUG_MODE No false Verbose logging (MSAL, API calls, working directory)
OUTLOOK_TOKEN_STORE_PATH No ~/.outlook-mcp-tokens.json Token cache file path (override for multi-instance setups)
MS_TIMEZONE No Eastern Standard Time Default timezone for calendar event creation (IANA or Windows timezone name)

MCP Client Configuration

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "outlook": {
      "command": "node",
      "args": ["/path/to/outlook-mcp/index.js"],
      "env": {
        "MS_CLIENT_ID": "your-client-id-here"
      }
    }
  }
}

Hermes Agent

Add to config.yaml under mcp_servers:

mcp_servers:
  outlook-mcp:
    command: node
    args:
    - /path/to/outlook-mcp/index.js
    env:
      MS_CLIENT_ID: your-client-id-here

Multi-Account (Multiple Instances)

The server is single-account by design — getCachedAccount() always returns the first cached account. To use multiple accounts (e.g. work + personal), run separate instances with isolated token caches:

mcp_servers:
  outlook-mcp-work:
    command: node
    args: [/path/to/outlook-mcp/index.js]
    env:
      MS_CLIENT_ID: your-client-id
  outlook-mcp-personal:
    command: node
    args: [/path/to/outlook-mcp/index.js]
    env:
      MS_CLIENT_ID: your-client-id
      OUTLOOK_TOKEN_STORE_PATH: /path/to/.outlook-mcp-tokens-personal.json

Each instance gets its own MSAL cache — no account-selection conflicts.

Tools Reference

The server exposes 21 tools across five categories.

Authentication (3 tools)

Tool Description
about Returns server name and version
authenticate Starts MSAL device code flow — returns a URL and code for the user to complete sign-in
check-auth-status Checks if a valid cached token exists (no side effects)

Email (6 tools)

Tool Key Parameters Description
list-emails folder, count, dateFrom, dateTo, dateRange Lists emails from a folder with date filtering. Results include conversationId.
search-emails query, from, to, subject, hasAttachments, unreadOnly, count Progressive search with KQL fallback strategies. Results include conversationId.
read-email id Reads a single email with full body (auto-cleaned HTML → text)
read-emails ids (max 10) Reads multiple emails concurrently
send-email to, cc, bcc, subject, body, importance, saveToSentItems Sends an email (plain text or HTML)
get-email-thread conversationId (preferred) or ids (max 20) Fetches a complete conversation across all folders (inbox + sent), strips quoted replies, deduplicates signatures per sender

Date filtering supports both ISO dates (2024-06-15) and relative ranges (today, yesterday, last7days, last30days, thisweek, lastweek, thismonth, lastmonth).

Search strategy: The server uses a progressive fallback approach because Graph API doesn't allow $search with $orderby or $filter. It tries: (1) combined KQL search with client-side boolean filtering, (2) individual term searches, (3) boolean-filter-only with $filter + $orderby, (4) fallback to recent emails.

Calendar (6 tools)

Tool Key Parameters Description
list-events count, startDate, endDate Lists events in a date range (defaults to upcoming from now)
create-event subject, start, end, attendees, body Creates a calendar event (times in ISO 8601, timezone configurable via MS_TIMEZONE)
accept-event eventId, comment Accepts a meeting invitation
decline-event eventId, comment Declines a meeting invitation
cancel-event eventId, comment Cancels an event you organized
delete-event eventId Deletes an event from your calendar

Folders (3 tools)

Tool Key Parameters Description
list-folders includeItemCounts, includeChildren Lists mail folders (flat list or hierarchical tree)
create-folder name, parentFolder Creates a new mail folder (optionally nested under a parent)
move-emails emailIds, targetFolder, sourceFolder Moves emails to a target folder by name

Inbox Rules (3 tools)

Tool Key Parameters Description
list-rules includeDetails Lists inbox rules sorted by execution order (sequence number)
create-rule name, fromAddresses, containsSubject, hasAttachments, moveToFolder, markAsRead, isEnabled, sequence Creates a new inbox rule
edit-rule-sequence ruleName, sequence Changes the execution order of an existing rule

Architecture

index.js                    # Main entry point — MCP server, tool registration, request routing
config.js                   # Server config, Graph API endpoints, field selections, pagination limits
.env.example                # Template for environment variables

auth/
  index.js                  # ensureAuthenticated() — the gatekeeper all tools call
  token-manager.js          # MSAL PublicClientApplication, device code flow, persistent cache plugin
  tools.js                  # authenticate, check-auth-status, about tool handlers

email/
  index.js                  # Tool definitions (schemas) for email tools
  list.js                   # list-emails handler
  search.js                 # search-emails handler with progressive KQL fallback
  read.js                   # read-email handler
  read-multiple.js          # read-emails handler (concurrent fetch)
  send.js                   # send-email handler
  folder-utils.js           # resolveFolderPath(), getFolderIdByName(), getAllFolders() — shared utilities

calendar/
  index.js                  # Tool definitions for calendar tools
  list.js                   # list-events handler
  create.js                 # create-event handler
  decline.js                # decline-event handler
  cancel.js                 # cancel-event handler
  delete.js                 # delete-event handler
  accept.js                 # accept-event handler

folder/
  index.js                  # Tool definitions for folder tools
  list.js                   # list-folders handler (flat + hierarchical formatting)
  create.js                 # create-folder handler
  move.js                   # move-emails handler

rules/
  index.js                  # Tool definitions for rules tools + edit-rule-sequence handler
  list.js                   # list-rules handler + getInboxRules() utility
  create.js                 # create-rule handler

tools/
  get-email-thread.js       # get-email-thread tool — conversationId-based thread fetcher

utils/
  graph-api.js              # callGraphAPI() — HTTPS client for Microsoft Graph
  bodyParser.js             # HTML → text conversion, boilerplate/banner/legal block stripping, signature handling
  threadBuilder.js          # Quote-boundary detection, unique-content extraction, chronological thread formatting
  odata-helpers.js          # OData filter building, date parsing, relative date ranges
  mock-data.js              # Test mode mock responses

Key Design Decisions

Token persistence: MSAL's cache plugin serializes/deserializes the token cache to disk on every access, so tokens survive process restarts. The cache file is plain JSON but contains MSAL's internal format — don't edit it manually.

Body cleaning: All email bodies pass through bodyParser.cleanBody() which: (1) converts HTML to text, (2) decodes HTML entities, (3) strips external-email caution banners, (4) strips legal boilerplate (DISCLAIMER / CONFIDENTIALITY NOTICE blocks), (5) optionally strips signature blocks (Outlook id="Signature" divs or text delimiters).

Thread reconstruction: get-email-thread fetches all messages matching a conversationId across all folders (inbox + sent items), sorts chronologically in memory (because $filter + $orderby on conversationId causes a Graph API 400), strips quoted replies using pattern matching (Outlook-style "From:/Sent:" headers, Gmail-style "On [date] [name] wrote:", >-prefixed lines, forwarded message blocks), and deduplicates signatures per sender (first occurrence keeps signature, repeats strip it).

Progressive search: Graph API doesn't allow $search with $orderby or $filter. The search handler tries multiple strategies in order and applies boolean filters (unread, attachments) client-side when needed.

No external HTTP framework: graph-api.js uses Node's native https module directly — no axios, no node-fetch, no dependencies beyond MSAL and the MCP SDK.

Development

Test Mode

npm run test-mode

Sets USE_TEST_MODE=true — the server returns mock data for all API calls instead of hitting Graph API. Useful for testing tool schemas and response formatting without a real Microsoft account.

MCP Inspector

npm run inspect

Launches the MCP Inspector — a web UI for interactively testing MCP tools.

Security Notes

  • Token cache: The .outlook-mcp-tokens.json file contains access and refresh tokens. It's in .gitignore by default. Protect it like a password.
  • No client secret in code: For public client apps, no secret is needed. If you use a confidential client app, set MS_CLIENT_SECRET in the environment (not in the code).
  • Scopes are hardcoded: The OAuth scopes are defined in config.js and include read/write for mail and calendar. Adjust if you need fewer permissions.
  • .gitignore covers: node_modules/, .env*, *.token.json, *.pem, *.key, .outlook-mcp-tokens.json

Known Issues

  1. authenticate force parameter is a no-op — The force parameter is declared in the tool schema but never read by the handler. Calling authenticate(force=false) will still start a fresh device code flow. Use check-auth-status to check auth state without side effects.

License

MIT