Skip to main content
Glama
OWA Exchange MCP Server

OWA Exchange MCP Server

by nhype

OWA Exchange MCP Server

MCP (Model Context Protocol) server for any Microsoft Exchange / OWA (Outlook Web Access) deployment. Gives LLM agents access to email, calendar, directory search, folders, availability, and meeting analytics via 30 tools.

Works with any on-premise or hosted Exchange server that exposes OWA.

Quick Start

# Copy and edit the MCP config with your OWA URL
cp .mcp.json.example .mcp.json

# One-time: set up encrypted credentials
python3 login.py --setup

# Login (opens headless browser, 2FA approval required)
python3 login.py

# Install the MCP server
pip install -e .

Related MCP server: outlook-mcp

Install

Add to your MCP client config. Replace https://owa.example.com with your OWA URL.

Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "exchange": {
      "command": "uvx",
      "args": ["exchange-mcp-server"],
      "env": {
        "EXCHANGE_OWA_URL": "https://owa.example.com"
      }
    }
  }
}

Cursor (.cursor/mcp.json):

{
  "mcpServers": {
    "exchange": {
      "command": "uvx",
      "args": ["exchange-mcp-server"],
      "env": {
        "EXCHANGE_OWA_URL": "https://owa.example.com"
      }
    }
  }
}

Claude Code (.mcp.json):

{
  "mcpServers": {
    "exchange": {
      "command": "uvx",
      "args": ["exchange-mcp-server"],
      "env": {
        "EXCHANGE_OWA_URL": "https://owa.example.com"
      }
    }
  }
}

Configuration

Variable

Required

Description

EXCHANGE_OWA_URL

Yes

Base URL of your OWA instance

EXCHANGE_COOKIE_FILE

No

Path to session cookies file (default: session-cookies.txt)

Login

The login tool handles credential setup and authentication within the MCP session — no separate terminal needed.

First time (setup + login):

login(master_password="...", username="user@example.com", password="...")

Subsequent logins (decrypts stored credentials):

login(master_password="...")

Option B: Via CLI

python3 login.py --setup   # First time: save encrypted credentials
python3 login.py            # Login with 2FA

Both methods:

  1. Open a headless browser to your OWA URL

  2. Submit credentials

  3. Wait for 2FA approval (up to 90 seconds)

  4. Save encrypted session cookies to session-cookies.txt

Credentials and session cookies are encrypted at rest with AES-256 (PBKDF2 key derivation, 480k iterations).

Tools (30)

Email (10)

Tool

Description

get_emails

List emails from a folder with filtering

get_email

Get full email content by ID

send_email

Send a new email

reply_email

Reply to an email

forward_email

Forward an email

delete_email

Delete an email

move_email

Move email to another folder

mark_email_read

Mark email as read/unread

download_attachments

Download file attachments from an email

get_email_links

Extract hyperlinks from an email body

Calendar (7)

Tool

Description

get_calendar_events

Get events in a date range (supports recurring expansion)

create_meeting

Create a meeting with attendees

update_meeting

Update an existing meeting

cancel_meeting

Cancel a meeting and notify attendees

respond_to_meeting

Accept, decline, or tentatively accept

download_event_attachments

Download file attachments from a calendar event

get_event_links

Extract hyperlinks from an event description

Directory (1)

Tool

Description

find_person

Search people in Active Directory

Folders (7)

Tool

Description

get_folders

List mail folders with unread counts

create_folder

Create a new mail folder

rename_folder

Rename an existing folder

empty_folder

Empty all items from a folder

delete_folder

Delete a mail folder

move_folder

Move a folder to a different parent

check_session

Check if the OWA session is authenticated

Availability (2)

Tool

Description

find_free_time

Find free slots in your calendar

find_meeting_time

Find common free slots for multiple people

Analytics (2)

Tool

Description

get_meeting_stats

Meeting count statistics for multiple people

get_meeting_contacts

Connection matrix — who you meet with most

Auth (1)

Tool

Description

login

Authenticate to OWA (credential setup + 2FA login)

Files

login.py                  # Browser-based 2FA login (standalone CLI)
exchange_mcp/
  server.py               # FastMCP server entry point
  owa_client.py           # OWA HTTP client
  auth.py                 # Async login logic (shared by MCP tool)
  tools/
    email.py              # Email tools
    calendar.py           # Calendar tools
    people.py             # Directory search
    folders.py            # Folder management & session check
    availability.py       # Free time / meeting time
    analytics.py          # Meeting stats & contacts
    auth.py               # Login tool
pyproject.toml            # Package config

Warning

Every Exchange / OWA deployment has its own authentication setup — some require 2FA (push notifications, TOTP, SMS), others use single-factor login or SSO. The login logic in this project (login.py and exchange_mcp/auth.py) is written for a specific 2FA flow (mobile push approval). If your OWA server uses a different 2FA method or no 2FA at all, you will need to modify or remove the login logic to match your environment.

Security

  • Credentials and session cookies encrypted with AES-256-Fernet

  • Master password never stored

  • PBKDF2 with 480,000 iterations for key derivation

  • Credential and cookie files have 0600 permissions

  • Cookies decrypted into memory only — never written to disk as plaintext (via MCP tool)

  • Session cookies never transmitted except to your OWA server

Available Tools

30 tools
cancel_meetingA

Cancel (delete) a calendar meeting and notify attendees.

Args: item_id: The ItemId of the meeting to cancel. message: Optional cancellation message to attendees.

Returns: JSON object with cancellation result.

Inputs: item_id (required), message. Returns: result

check_sessionA

Check whether the current OWA session is authenticated.

Makes a lightweight GetFolder call on the inbox. Returns session status, the mailbox display name, and cookie file path.

Returns: JSON object with authenticated (bool), mailbox name, and details.

Inputs: no named parameters. Returns: result

create_folderA

Create a new mail folder.

Args: name: Display name for the new folder. parent_folder_id: Parent folder to create under. Defaults to "msgfolderroot" (top-level). Can be a distinguished folder name or a raw folder ID.

Returns: JSON object with the created folder's id and name.

Inputs: name (required), parent_folder_id. Returns: result

create_meetingA

Create a new calendar meeting.

Args: subject: Meeting subject/topic. date: Meeting date in YYYY-MM-DD format. start_time: Start time in HH:MM format. duration_minutes: Duration in minutes (default 30). required_attendees: List of email addresses for required attendees. optional_attendees: List of email addresses for optional attendees. location: Location or video link. description: Meeting description/body text. is_all_day: Whether this is an all-day event. reminder_minutes: Minutes before start for reminder (default 15). importance: Importance level: Low, Normal, or High. sensitivity: Sensitivity: Normal, Personal, Private, or Confidential.

Returns: JSON object with creation result including item_id on success.

Inputs: subject (required), date (required), start_time (required), duration_minutes, +8 more. Returns: result

delete_emailA

Delete one or more emails.

Args: item_ids: List of Exchange ItemIds to delete. permanent: If True, permanently delete (HardDelete). Otherwise move to Deleted Items.

Inputs: item_ids (required), permanent. Returns: result

delete_folderA

Delete a mail folder.

Args: folder_id: The Exchange folder ID to delete (from get_folders). permanent: If True, permanently delete (HardDelete). Otherwise move to Deleted Items. Default False.

Returns: JSON object with success status.

Inputs: folder_id (required), permanent. Returns: result

download_attachmentsA

Download all file attachments from an email to disk.

Args: item_id: The Exchange ItemId of the email to download attachments from. target_folder: Local directory to save files (default /tmp/attachments).

Inputs: item_id (required), target_folder. Returns: result

download_event_attachmentsA

Download all file attachments from a calendar event to disk.

Args: item_id: The Exchange ItemId of the calendar event. target_folder: Local directory to save files (default /tmp/attachments).

Inputs: item_id (required), target_folder. Returns: result

empty_folderB

Empty all items from a mail folder.

Args: folder_id: The Exchange folder ID to empty (from get_folders). delete_sub_folders: If True, also delete sub-folders. Default False. permanent: If True, permanently delete items (HardDelete). Otherwise move to Deleted Items. Default False.

Returns: JSON object with success status.

Inputs: folder_id (required), delete_sub_folders, permanent. Returns: result

find_free_timeA

Find free time slots in your own calendar.

Analyzes your calendar events and returns available time slots within working hours for each weekday in the range.

Args: start_date: Start date in YYYY-MM-DD format. end_date: End date in YYYY-MM-DD format. Defaults to start_date if not provided (single-day search). duration_minutes: Minimum slot duration in minutes. Default 30. start_hour: Working day start hour (0-23). Default 9. end_hour: Working day end hour (0-23). Default 18.

Returns: JSON object with free_slots keyed by date, each containing an array of {start, end, duration_minutes} objects.

Inputs: start_date (required), end_date, duration_minutes, start_hour, +1 more. Returns: result

find_meeting_timeA

Find meeting times that work for multiple people.

Uses the OWA GetUserAvailability API to check cross-mailbox availability and find common free slots for all attendees. Supports multi-day ranges — searches each weekday in the range.

Args: emails: Comma-separated email addresses or names of attendees. start_date: Start date in YYYY-MM-DD format. end_date: End date in YYYY-MM-DD format. Defaults to start_date if not provided (single-day search). duration_minutes: Minimum slot duration in minutes. Default 30. start_hour: Working day start hour (0-23). Default 9. end_hour: Working day end hour (0-23). Default 18.

Returns: JSON object with attendee info and free_slots keyed by date, each containing an array of {start, end, duration_minutes}.

Inputs: emails (required), start_date (required), end_date, duration_minutes, +2 more. Returns: result

find_personA

Search for people in the corporate directory.

Looks up employees by name, email, department, or keyword using the Exchange ResolveNames API against Active Directory.

Args: query: Name, email address, or keyword to search for.

Returns: JSON array of matching people with contact details (name, email, job_title, department, company, office, phones, address, manager, direct_reports, alias).

Inputs: query (required). Returns: result

forward_emailB

Forward an email to other recipients.

Args: item_id: The Exchange ItemId of the email to forward. to: Comma-separated list of recipient email addresses. body: Optional message to include above the forwarded content.

Inputs: item_id (required), to (required), body. Returns: result

get_calendar_eventsA

Get calendar events within a date range.

Args: start_date: Start date in YYYY-MM-DD format. end_date: End date in YYYY-MM-DD format. include_body: If True, fetch full event details (organizer, attendees, body) via GetItem for each event. Slower but more complete. Ignored when expand_recurring=True. expand_recurring: If True, show every individual occurrence of recurring meetings (via GetUserAvailability). This gives an accurate count of all events but returns fewer fields per event (no item_id, attendees, or body). Default False.

Returns: JSON array of event objects with subject, start, end, location, attendees, etc.

Inputs: start_date (required), end_date (required), include_body, expand_recurring. Returns: result

get_emailB

Get a single email with full body and details.

Args: item_id: The Exchange ItemId of the email to retrieve.

Inputs: item_id (required). Returns: result

get_emailsA

Get emails from a mailbox folder.

Args: folder: Folder name (Inbox, Sent, Drafts, Deleted, Junk, or custom name). limit: Maximum number of emails to return (default 10, max 50). offset: Number of emails to skip for pagination. include_body: If True, fetch full body for each email (slower). unread_only: If True, only return unread emails. ids_only: If True, return only item IDs and dates (compact, for bulk ops). Max limit raised to 500 in this mode.

Inputs: folder, limit, offset, include_body, +2 more. Returns: result

get_foldersA

List mail folders from the Exchange mailbox.

Args: parent_folder_id: Parent folder to list children of. Defaults to "msgfolderroot" (top-level). Can be a distinguished folder name or a raw folder ID. recursive: If True, traverse all subfolders recursively (Deep). If False, only list immediate children (Shallow).

Returns: JSON array of folder objects with: name, id, total_count, unread_count, child_folder_count.

Inputs: parent_folder_id, recursive. Returns: result

get_meeting_contactsA

Build a connection matrix: who you meet with most often.

Analyzes your calendar over a date range, accounting for recurring meetings. For each contact found in your meetings, returns the weighted count of shared meetings.

Args: start_date: Start date in YYYY-MM-DD format. end_date: End date in YYYY-MM-DD format. top_n: Number of top contacts to return. Default 30.

Returns: JSON object with total_meetings, unique_contacts, and a ranked contacts array of {name, email, meetings} objects.

Inputs: start_date (required), end_date (required), top_n. Returns: result

get_meeting_statsA

Get meeting count statistics for one or more people over a date range.

Uses GetUserAvailability to count calendar events (including expanded recurring instances) for each person.

Args: people: Comma-separated names or email addresses to analyze. start_date: Start date in YYYY-MM-DD format. end_date: End date in YYYY-MM-DD format.

Returns: JSON object with per-person stats sorted by meeting count: total_meetings, meetings_per_workday, days_with_meetings.

Inputs: people (required), start_date (required), end_date (required). Returns: result

loginA

Authenticate to Exchange OWA (handles credential setup and 2FA login).

Call this tool when the session has expired or before first use. It performs browser-based SSO login with 2FA mobile push approval. Session cookies are encrypted at rest with the master password.

Two-call 2FA flow: The first call starts the browser login in the background and returns immediately asking you to tell the user to approve 2FA on their phone. Call login again with the same master_password after the user approves — the second call picks up the result.

Args: master_password: Decrypts stored credentials (and cookies), or encrypts new ones if username/password are also provided. username: Email address. Provide together with password for first-time credential setup (replaces login.py --setup). password: Account password. Required together with username for setup.

Returns: JSON result with success status and any error details.

Inputs: master_password (required), username, password. Returns: result

mark_email_readA

Mark one or more emails as read or unread.

Args: item_ids: List of Exchange ItemIds to update. is_read: True to mark as read, False to mark as unread (default True).

Inputs: item_ids (required), is_read. Returns: result

move_emailA

Move one or more emails to a different folder.

Args: item_ids: List of Exchange ItemIds to move. target_folder: Destination folder name (e.g. Inbox, Sent, Deleted, or custom).

Inputs: item_ids (required), target_folder (required). Returns: result

move_folderA

Move a mail folder to a different parent folder.

Args: folder_id: The Exchange folder ID to move (from get_folders). target_parent_folder_id: Destination parent folder. Defaults to "msgfolderroot" (top-level). Can be a distinguished folder name or a raw folder ID.

Returns: JSON object with success status and new folder ID.

Inputs: folder_id (required), target_parent_folder_id. Returns: result

rename_folderA

Rename an existing mail folder.

Args: folder_id: The Exchange folder ID to rename (from get_folders). new_name: New display name for the folder.

Returns: JSON object with success status and new folder id.

Inputs: folder_id (required), new_name (required). Returns: result

reply_emailA

Reply to an email.

Args: item_id: The Exchange ItemId of the email to reply to. body: Reply body text. reply_all: If True, reply to all recipients. Otherwise reply to sender only.

Inputs: item_id (required), body (required), reply_all. Returns: result

respond_to_meetingA

Respond to a meeting invitation (accept, decline, or tentative).

Args: item_id: The ItemId of the meeting to respond to. response: Response type: "Accept", "Decline", or "Tentative". message: Optional message to include with the response.

Returns: JSON object with response result.

Inputs: item_id (required), response (required), message. Returns: result

send_emailA

Send a new email.

Args: to: Comma-separated list of recipient email addresses. subject: Email subject line. body: Email body text. cc: Comma-separated CC recipients (optional). bcc: Comma-separated BCC recipients (optional). importance: Email importance: Low, Normal, or High (default Normal). is_html: If True, body is treated as HTML. Otherwise plain text.

Inputs: to (required), subject (required), body (required), cc, +3 more. Returns: result

update_meetingA

Update an existing calendar meeting.

Internally cancels the old meeting and creates a new one with updated fields, because OWA's JSON API does not support UpdateItem for calendar items reliably. Unchanged fields are preserved from the original meeting.

Args: item_id: The ItemId of the meeting to update (from get_calendar_events). subject: New subject (omit to keep original). date: New date in YYYY-MM-DD format (omit to keep original). start_time: New start time in HH:MM format (omit to keep original). duration_minutes: New duration in minutes (omit to keep original). location: New location (omit to keep original). description: New description/body text (omit to keep original). required_attendees: Email addresses for required attendees. Replaces existing list. Omit to keep original attendees. optional_attendees: Email addresses for optional attendees. Replaces existing list. Omit to keep original attendees. change_key: Ignored (kept for backward compatibility).

Returns: JSON object with update result including new item_id.

Inputs: item_id (required), subject, date, start_time, +6 more. Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.8/5.0

    Scored across 30 tools

    Disambiguation5/5

    Every tool targets a distinct action or resource, with clear differentiation between email, calendar, folder, and people operations. No overlapping functionality is evident.

    Naming Consistency4/5

    Tool names follow a consistent verb_noun pattern in snake_case, with only minor deviations like 'login' (single verb) and 'mark_email_read' (adjective after noun). Overall predictable naming.

    Tool Count4/5

    30 tools cover email, calendar, folder, and contact management comprehensively. While slightly on the higher end, each tool serves a distinct purpose for a full-featured Exchange MCP server.

    Completeness4/5

    The tool surface covers core CRUD and lifecycle operations for emails, folders, and calendar events, plus supporting features like attachment download, link extraction, and scheduling analytics. Minor gaps like email search are absent but not critical.

    Maintenance

    ActivityInactive
    ResponsivenessLimited data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      C
      maintenance
      A production-ready MCP server that provides secure, delegated access to Microsoft 365 services including Email, SharePoint, OneDrive, and Calendar. It enables AI models to search messages, browse files, manage calendar events, and parse document contents using OAuth 2.1 authentication.
      MIT
    • F
      license
      B
      quality
      C
      maintenance
      MCP server providing AI assistants with full access to Microsoft Outlook email and calendar via the Microsoft Graph API, featuring 26 tools for mail, calendar, contacts, and scheduling with delegated authentication.
      29
      -