Skip to main content

MSGraph MCP

A Model Context Protocol (MCP) server for Microsoft Graph. It exposes Microsoft Outlook mail and calendar, plus read-only Microsoft Teams message history, to AI agents via the Microsoft Graph SDK. Acts as the signed-in user (delegated permissions, MSAL device code flow).

Formerly published as outlook-mcp. Renamed because the scope grew beyond Outlook (Teams today, potentially other Graph surfaces later). Outlook mail and calendar remain first-class capabilities. See Migrating from outlook-mcp.

What it does

Workflow-oriented tools covering common mail, calendar, and read-only Teams operations:

Group Tools
Util whoami
Mail — read list_messages, search_messages, get_message, list_attachments, download_attachment
Mail — write send_message, create_draft, reply_message, reply_all_message, forward_message, update_message, delete_message
Mail — folders list_folders, create_folder, update_folder, delete_folder, move_message
Mail — actions archive_message, mark_read, mark_unread, flag_message, unflag_message
Mail — rules list_rules, get_rule, create_rule, update_rule, delete_rule
Calendar list_calendars, list_events, get_event, create_event, update_event, delete_event, cancel_event, respond_to_event, find_meeting_times
Teams (read) list_chats, list_chat_messages, list_joined_teams, list_channels, list_channel_messages, list_message_replies, download_hosted_content

Every tool that touches a mailbox or calendar accepts an optional mailbox argument (email or user ID) to target shared mailboxes/calendars. Omit it to use the signed-in user's own mailbox.

Every tool that returns objects accepts include_raw=true to also include the full Graph payload.

List/search tools support pagination via limit (1-100, default 25) and page_token.

Prerequisites

  • Python ≥ 3.11
  • uv: https://docs.astral.sh/uv/
  • An Entra (Azure AD) app registration with the right permissions (see "Entra setup" below)

Install

From PyPI (package msgraph-mcp-server; the commands it installs are msgraph-mcp and msgraph-mcp-login):

uv tool install msgraph-mcp-server   # or: pip install msgraph-mcp-server

Then use msgraph-mcp-login / msgraph-mcp directly wherever the quickstart below says uv run ..., and wire the host with claude mcp add msgraph -- msgraph-mcp.

Quickstart (from source)

# 1. Install dependencies
uv sync

# 2. Configure environment
cp .env.example .env
# Fill in MSGRAPH_MCP_CLIENT_ID and MSGRAPH_MCP_TENANT_ID

# 3. One-time sign-in (device code flow)
uv run msgraph-mcp-login
# Follow the prompt: visit the URL, enter the code, complete sign-in.
# A token cache is written to ~/.msgraph-mcp/token_cache.bin (mode 0600).

# 4. Wire the MCP into your host
# - Claude Code:
claude mcp add msgraph -- uv --directory "$(pwd)" run msgraph-mcp

# - Anything else: configure the host to launch `uv run msgraph-mcp` (stdio).

Entra setup

The app registration (e.g. "MSGraph MCP") requires:

  • Account type: single tenant
  • Redirect URI (public client): https://login.microsoftonline.com/common/oauth2/nativeclient
  • Delegated permissions (Microsoft Graph):
    • Mail.ReadWrite
    • Mail.ReadWrite.Shared
    • Mail.Send
    • MailboxSettings.ReadWrite
    • Calendars.ReadWrite
    • Calendars.ReadWrite.Shared
    • User.Read
    • Chat.Read (Teams)
    • Team.ReadBasic.All (Teams)
    • Channel.ReadBasic.All (Teams)
    • ChannelMessage.Read.All (Teams)
  • Admin consent: required for ChannelMessage.Read.All (always), plus the *.Shared permissions if your tenant requires it.

If you signed in before any of these scopes were added to the app (for example MailboxSettings.ReadWrite, or the Teams scopes), re-run uv run msgraph-mcp-login so the cached token picks up the new scopes. Without them, calls needing the missing scope fail with a consent error.

The CLI uses public-client device code flow — no client secret is needed or stored.

Environment variables

Var Required Default Purpose
MSGRAPH_MCP_CLIENT_ID yes Entra (Azure AD) app client ID
MSGRAPH_MCP_TENANT_ID yes Tenant ID (single-tenant authority)
MSGRAPH_MCP_TOKEN_CACHE_PATH no ~/.msgraph-mcp/token_cache.bin Override token cache file location

Process env wins; .env at the repo root is loaded as a dev fallback.

Legacy OUTLOOK_MCP_* names are honored as a fallback for each variable (the MSGRAPH_MCP_* name wins when both are set).

Migrating from outlook-mcp

This project was named outlook-mcp through v0.2.0. What changed in the rename:

Old New
package outlook-mcp package msgraph-mcp
module outlook_mcp module msgraph_mcp
uv run outlook-mcp uv run msgraph-mcp
uv run outlook-mcp-login uv run msgraph-mcp-login
OUTLOOK_MCP_* env vars MSGRAPH_MCP_* env vars
~/.outlook-mcp/token_cache.bin ~/.msgraph-mcp/token_cache.bin

Backward compatibility, so an existing setup keeps working without re-authenticating:

  • OUTLOOK_MCP_* env vars are still read as a fallback.
  • If ~/.msgraph-mcp/token_cache.bin does not exist but ~/.outlook-mcp/token_cache.bin does, the legacy cache is used. To move to the new location: mv ~/.outlook-mcp ~/.msgraph-mcp.

You do need to update anything that launches the server by script name (MCP host configs): outlook-mcpmsgraph-mcp.

Security

  • The token cache contains your refresh token, which can mint access tokens for your mail, calendar, and Teams data. Treat it like a credential.
  • Default location: ~/.msgraph-mcp/token_cache.bin, mode 0600, parent dir mode 0700.
  • To revoke access: sign in to https://account.microsoft.com or your org's identity portal, revoke the app, then rm ~/.msgraph-mcp/token_cache.bin.
  • To switch accounts: rm ~/.msgraph-mcp/token_cache.bin and re-run msgraph-mcp-login.

Recipes

Route a sender into a new folder

# 1. Make a folder for the notifications.
create_folder(display_name="Notifications")
# -> {"id": "AAMkFolderId", "display_name": "Notifications", ...}

# 2. Create an inbox rule that moves matching senders into it.
create_rule(
    display_name="Notifications",
    sender_contains=["example.com"],
    move_to_folder="AAMkFolderId",
    stop_processing_rules=True,
)

Conditions inside one rule are AND-ed by Outlook. Pass a list to a single condition (e.g. sender_contains=["example.com", "monitor.io"]) for OR within that condition. Rules only run against the inbox — Graph's messageRules endpoint is hardcoded there and does not support per-folder rules.

create_rule requires at least one condition and one action. update_rule patches a rule in place but replaces the conditions or actions block whenever you pass any condition/action arg — call get_rule first if you need to preserve existing values.

Microsoft Teams (read-only)

Read Teams message history as the signed-in user:

  • list_chats, list_chat_messages: your 1:1 and group chats.
  • list_joined_teams, list_channels, list_channel_messages, list_message_replies: team channels and their threads.
  • download_hosted_content: download an inline image referenced by a message (hosted_content_refs). Images come back as a native MCP image block the agent can view directly; pass save_path (file or existing directory) to write the bytes to disk and get back a path instead.

Permissions and consent

These delegated scopes are required (already listed in SCOPES):

  • Chat.Read, Team.ReadBasic.All, Channel.ReadBasic.All: user-consentable.
  • ChannelMessage.Read.All: requires tenant administrator consent.

Setup:

  1. Add the four delegated permissions to the app registration.
  2. Grant tenant admin consent for ChannelMessage.Read.All.
  3. Because the scope set changed, re-run the device-code login so the cached token carries the new scopes.

Notes and limits

  • Reading is delegated-only: you can read your own chats, not other users' chats.
  • Channel message and reply pages are capped at 50 by Graph.
  • SharePoint/OneDrive-backed file attachments are not downloadable here. In Teams, shared files are attachments whose contentUrl points into SharePoint, which is a different Graph surface (needs Files.Read.All / Sites.Read.All and the driveItem APIs). download_hosted_content covers inline hosted content (images), not shared files. This is deferred.

Future work

Not implemented; reasonable additions:

  • Graph $batch requests. Performance optimization that bundles multiple Graph calls into one HTTP round-trip; would speed up multi-step workflows but adds complexity. Defer until profiling proves the win.

Other known gaps (intentionally out of scope): chunked attachment upload (>3 MB), category master-list management, mail signatures, contacts/To-Do/OneNote, multi-account switching, change-notification subscriptions, force-delete of non-empty folders.

Development

# Run unit tests
uv run pytest

# Run unit + live integration smoke (requires a valid token cache)
MSGRAPH_MCP_INTEGRATION=1 uv run pytest

# Type check
uv run pyright src tests

# Lint
uv run ruff check .

Troubleshooting

Symptom Fix
NotAuthenticatedError: Not authenticated. Run \msgraph-mcp-login`...` Run uv run msgraph-mcp-login.
ConfigError: Missing required env var: MSGRAPH_MCP_CLIENT_ID Set the var in .env or in your MCP host's env config.
Graph API 403: ErrorAccessDenied — ... Permission mismatch on the Entra app. Verify the delegated permissions list above and re-consent.
Graph API 400: BadRequest — Syntax error: character ... is not valid at position N from search_messages The query is passed to Graph's $search as-is. Wrap literal/multi-character tokens in double quotes (e.g. "weekly report"), or use KQL fielded forms (e.g. from:alice subject:"report"). Bare alphanumeric strings with embedded digits are invalid KQL.
Server boots but tools 404 in the host Confirm the host is launching uv run msgraph-mcp with the right working directory.

Spec: docs/specs/2026-05-19-outlook-mcp-design.md (gitignored — local working doc).

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

msgraph_mcp_server-0.3.0.tar.gz (170.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

msgraph_mcp_server-0.3.0-py3-none-any.whl (52.6 kB view details)

Uploaded Python 3

File details

Details for the file msgraph_mcp_server-0.3.0.tar.gz.

File metadata

  • Download URL: msgraph_mcp_server-0.3.0.tar.gz
  • Upload date:
  • Size: 170.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for msgraph_mcp_server-0.3.0.tar.gz
Algorithm Hash digest
SHA256 8f76ec4725c0b6a6d66a46795a9897cd864b73d484f7f20097f806075cb73026
MD5 5cf6c55ee7043fc257bbe84250ae961d
BLAKE2b-256 51a2cc7801f6a21beda4a66521029ec3d39e6e4c9159516cc268878501d03386

See more details on using hashes here.

Provenance

The following attestation bundles were made for msgraph_mcp_server-0.3.0.tar.gz:

Publisher: release.yml on timfurlong/msgraph-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file msgraph_mcp_server-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for msgraph_mcp_server-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 98b47cf4f96d07f009907da23304653dca5fe6acfbfd28b4c3c126250e654ba3
MD5 cc06c0e3b75228e29b128defd7794842
BLAKE2b-256 c0ac29c6529885849e0acfba9122daf9fb48a65f82724cc05ac2c26a662adf02

See more details on using hashes here.

Provenance

The following attestation bundles were made for msgraph_mcp_server-0.3.0-py3-none-any.whl:

Publisher: release.yml on timfurlong/msgraph-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.3.2

2 files

0.3.1

2 files

This release

0.3.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page