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.ReadWriteMail.ReadWrite.SharedMail.SendMailboxSettings.ReadWriteCalendars.ReadWriteCalendars.ReadWrite.SharedUser.ReadChat.Read(Teams)Team.ReadBasic.All(Teams)Channel.ReadBasic.All(Teams)ChannelMessage.Read.All(Teams)
- Admin consent: required for
ChannelMessage.Read.All(always), plus the*.Sharedpermissions 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-runuv run msgraph-mcp-loginso 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.bindoes not exist but~/.outlook-mcp/token_cache.bindoes, 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-mcp → msgraph-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, mode0600, parent dir mode0700. - 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.binand re-runmsgraph-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; passsave_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:
- Add the four delegated permissions to the app registration.
- Grant tenant admin consent for
ChannelMessage.Read.All. - 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
contentUrlpoints into SharePoint, which is a different Graph surface (needsFiles.Read.All/Sites.Read.Alland the driveItem APIs).download_hosted_contentcovers inline hosted content (images), not shared files. This is deferred.
Future work
Not implemented; reasonable additions:
- Graph
$batchrequests. 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8f76ec4725c0b6a6d66a46795a9897cd864b73d484f7f20097f806075cb73026
|
|
| MD5 |
5cf6c55ee7043fc257bbe84250ae961d
|
|
| BLAKE2b-256 |
51a2cc7801f6a21beda4a66521029ec3d39e6e4c9159516cc268878501d03386
|
Provenance
The following attestation bundles were made for msgraph_mcp_server-0.3.0.tar.gz:
Publisher:
release.yml on timfurlong/msgraph-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
msgraph_mcp_server-0.3.0.tar.gz -
Subject digest:
8f76ec4725c0b6a6d66a46795a9897cd864b73d484f7f20097f806075cb73026 - Sigstore transparency entry: 2553831131
- Sigstore integration time:
-
Permalink:
timfurlong/msgraph-mcp@ed908916e260abf85ae499d02465f1b2d98d6e7e -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/timfurlong
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ed908916e260abf85ae499d02465f1b2d98d6e7e -
Trigger Event:
push
-
Statement type:
File details
Details for the file msgraph_mcp_server-0.3.0-py3-none-any.whl.
File metadata
- Download URL: msgraph_mcp_server-0.3.0-py3-none-any.whl
- Upload date:
- Size: 52.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98b47cf4f96d07f009907da23304653dca5fe6acfbfd28b4c3c126250e654ba3
|
|
| MD5 |
cc06c0e3b75228e29b128defd7794842
|
|
| BLAKE2b-256 |
c0ac29c6529885849e0acfba9122daf9fb48a65f82724cc05ac2c26a662adf02
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
msgraph_mcp_server-0.3.0-py3-none-any.whl -
Subject digest:
98b47cf4f96d07f009907da23304653dca5fe6acfbfd28b4c3c126250e654ba3 - Sigstore transparency entry: 2553831190
- Sigstore integration time:
-
Permalink:
timfurlong/msgraph-mcp@ed908916e260abf85ae499d02465f1b2d98d6e7e -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/timfurlong
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ed908916e260abf85ae499d02465f1b2d98d6e7e -
Trigger Event:
push
-
Statement type: