Skip to main content
Glama
ItayElizur

mcp-outlook

by ItayElizur

mcp-outlook

A self-hostable MCP server for on-prem Microsoft Exchange over EWS (Exchange Web Services). Built for air-gapped / no-cloud environments — it talks directly to your internal Exchange server and never touches Microsoft Graph, Azure AD, or a desktop Outlook client.

  • Backend: exchangelib (EWS client)

  • Framework: FastMCP

  • Transport: streamable-http (a standalone HTTP server other internal hosts connect to)

  • Auth to Exchange: basic or NTLM (selectable via config)

Two modes

Mode

Who it serves

How it authenticates

static (default)

one mailbox

connects as that account (your own creds) — great for local beta testing

jwt

many users

validates each caller's user JWT, then acts on that user's mailbox via a service account + EWS Impersonation

How jwt mode bridges identity. Exchange can't consume your company JWTs (Outlook is PKINIT/Kerberos-based). So the MCP does two separate authentications that never mix:

User --(JWT)--> MCP validates the token, reads the user's email
                MCP --(service account, NTLM)--> Exchange
                MCP --(impersonation header = user's email)--> acts on the user's mailbox

The user's JWT is never sent to Exchange, and no user password/smart-card ever touches the MCP — only the single service credential does. See TODO.md for what admins must set up for jwt mode.

Related MCP server: OWA Exchange MCP Server

Tools

Tool

Purpose

list_emails(folder="inbox", limit=20)

Recent messages, newest first

search_emails(query, folder, start_date, end_date, sender, recipient, limit)

Text + date-range + sender/recipient search

get_email(message_id, folder="inbox")

Full message: body, recipients, attachment names

draft_email(to, subject, body)

Open the interactive compose widget (MCP Apps); user edits and sends

reply_email(message_id, folder, reply_all, body)

Open the compose widget pre-filled as a reply

forward_email(message_id, folder, to, body)

Open the compose widget pre-filled as a forward

send_email(to, subject, body, cc, bcc, html, attachments)

Send — callable only from the widget (app-only visibility)

search_contacts(query, limit)

Search contacts — callable only from the widget (app-only)

mark_email_read(message_id, folder) / mark_email_unread(...)

Toggle read status

delete_email(message_id, folder, permanent=False)

Move to Deleted Items, or permanently delete

flag_email_important(message_id, folder, important=True)

Set Outlook's High/Normal importance

move_email(message_id, destination, folder)

Move a message to another folder

list_folders()

Available mail folder names, including subfolders

list_events(start_date, end_date, limit)

Calendar events in a date range

get_event(event_id)

Full event: body, attendees, location

find_meeting_slots(attendees, duration_minutes, ...)

Scheduling assistant — ranks slots by attendee availability

draft_event(subject, start, end, location, body, required_attendees, optional_attendees)

Open the interactive event-draft widget

create_event(subject, start, end, required_attendees, ...)

Create the event — callable only from the widget (app-only)

accept_meeting(event_id) / decline_meeting(event_id)

Respond to a meeting invite

list_emails and search_emails also take unread_only=true to return only unread messages.

Setup

Requires Python 3.11+ and uv.

uv sync                 # create venv + install deps
cp .env.example .env    # then edit .env with your Exchange details
uv run python -m mcp_outlook

The server binds to MCP_HOST:MCP_PORT (default 127.0.0.1:8000) and serves the streamable-http MCP endpoint at /mcp.

Local beta testing (no admin setup needed)

Run against your own mailbox with your own username/password — no JWT, no impersonation, no service account, no smart card:

# in .env:
OUTLOOK_AUTH_MODE=static          # the default
OUTLOOK_EWS_ENDPOINT=https://mail.corp.local/EWS/Exchange.asmx
OUTLOOK_USERNAME=CORP\you
OUTLOOK_PASSWORD=...
uv run python -m mcp_outlook

Configuration

All settings come from environment variables (or a .env file). See .env.example for the full list. The essentials:

Var

Notes

OUTLOOK_AUTH_MODE

static (default, one mailbox) or jwt (multi-user tunnel)

OUTLOOK_EWS_ENDPOINT

Full asmx URL, e.g. https://mail.corp.local/EWS/Exchange.asmx. Preferred.

OUTLOOK_SERVER

Host-only alternative (endpoint assumed at /EWS/Exchange.asmx)

OUTLOOK_USERNAME

The connecting account — your own (static) or the service account (jwt). DOMAIN\user for NTLM, or email for basic. Not used with sspi

OUTLOOK_EMAIL

Mailbox to open (static mode). Optional — defaults to OUTLOOK_USERNAME when it's an email; ignored in jwt mode; required with sspi (no username to default from)

OUTLOOK_PASSWORD

account password. Not used with sspi

OUTLOOK_AUTH_TYPE

ntlm (default), basic, or sspi (Windows Integrated Auth — authenticates as this process's own AD identity, no username/password; Windows-only, needs uv sync --extra sspi)

OUTLOOK_JWT_ISSUER / _AUDIENCE

Required in jwt mode — token issuer and audience to require

OUTLOOK_JWT_JWKS_URI / _PUBLIC_KEY

jwt mode — signing keys (JWKS URI, or a static PEM for air-gapped)

OUTLOOK_JWT_EMAIL_CLAIM

jwt mode — claim holding the user's SMTP address (default email)

OUTLOOK_CA_BUNDLE

Path to internal CA .pem (for self-signed / internal CA)

OUTLOOK_VERIFY_SSL

true (default); false disables TLS verify (dev only)

MCP_HOST / MCP_PORT

HTTP bind (default 127.0.0.1:8000)

Finding your EWS endpoint

The EWS URL is not the OWA (webmail) URL. On the Exchange server:

Get-WebServicesVirtualDirectory | fl Name,InternalUrl,ExternalUrl

In an air-gapped setup you almost always want the InternalUrl.

Testing

Unit tests need no Exchange server (config parsing + serialization only):

uv run pytest

Live smoke test (with a real .env): start the server, connect an MCP client or the MCP Inspector, then call list_folderslist_emailssend_email (to yourself) and confirm receipt. Flip OUTLOOK_AUTH_TYPE between ntlm and basic to confirm whichever your Exchange admin has enabled.

Compose UI (MCP Apps)

draft_email opens an interactive MCP Apps widget — a React composer built into a single self-contained HTML file (src/mcp_outlook/widgets/compose.html). Any MCP Apps-capable host renders it inline in the chat thread.

Widget capabilities:

  • To field with inline contact search — type after the last comma to search contacts; pick a result to replace the query with a chip; valid addresses render as labelled chips.

  • Send / Discard — Send fires send_email directly from the widget (app-only — the model cannot call it); Discard collapses the card.

  • Supersession — opening a new draft greys out any older open draft widget.

  • Signature — every draft is pre-seeded with "Written with Airchat" (editable).

Air-gap guarantee: the built HTML (React + bridge JS inlined) ships with the Python package. No external asset requests are made at runtime; Node.js is only needed to rebuild the widget.

Rebuild the widget (dev only)

cd frontend
npm ci
npm run build     # tsc + vite build + artifact copy → src/mcp_outlook/widgets/compose.html

The build script validates no external URLs ended up in the HTML before copying.

Try it visually (standalone dev preview)

cd frontend && npm run dev
# Opens http://localhost:5173 with a mock host — no Exchange needed.
# Type in To, see chips form, contact results appear, Send/Discard collapse the card.

Gotchas

  • Basic auth is often disabled on modern Exchange — NTLM is the safer default.

  • Internal/self-signed certs require OUTLOOK_CA_BUNDLE, or the connection fails on TLS verification.

  • Multi-user (jwt) mode needs one Exchange grant — the service account must hold the ApplicationImpersonation RBAC role. See TODO.md. static mode needs no such grant.

  • .env holds a plaintext password. It is git-ignored; also lock down file permissions (chmod 600 .env) on the host.

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    MCP server for any Microsoft Exchange / OWA deployment. Gives LLM agents access to email, calendar, directory search, folders, availability, and meeting analytics via 30 tools.
    30
    7
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables reading, sending, and managing Microsoft 365/Outlook emails through MCP tools with OAuth 2.1 authentication.
    114
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ItayElizur/mcp-outlook'

If you have feedback or need assistance with the MCP directory API, please join our Discord server