Microsoft Graph MCP Server
# Microsoft Graph MCP Server
[](https://github.com/oliverhruby/msgraph-mcp/releases)
[](https://github.com/oliverhruby/msgraph-mcp/actions/workflows/quality-gates.yml)
[](https://github.com/oliverhruby/msgraph-mcp/actions/workflows/security.yml)
[](https://github.com/oliverhruby/msgraph-mcp/actions/workflows/container-security.yml)
[](https://github.com/oliverhruby/msgraph-mcp/actions/workflows/upstream-coverage.yml)
## Project
A **Model Context Protocol (MCP) server** that exposes the full
**Microsoft Graph API** to AI agents such as opencode, Claude, Cursor and any
other MCP client — **219 tools** across 25 workloads: mail, calendar, files,
Teams, SharePoint, Planner, To Do, OneNote, Excel, presence, search, identity
governance, security, reports, audit logs and more.
Microsoft Graph is the single REST entry point into Microsoft 365. This server
lets an agent authenticate to your tenant **once** and then operate your real
mail, files, calendar, Teams and SharePoint data directly — reading inboxes,
drafting and sending mail, creating calendar events and meetings, uploading or
sharing files, answering Teams channels, maintaining Planner/To Do tasks, and
administering users, groups, security and audit logs.
> **Self-hosted & by-design full-coverage.** Unlike the hosted Microsoft-hosted
> "MCP Server for Enterprise", this server runs on **your** machine or
> container, uses **your own** credentials (interactive, device-code,
> client-secret, certificate, on-behalf-of, managed identity), and can **write**
> — with a hard read-only guard when you want it safe.
---
## Table of Contents
- [Why another Microsoft Graph MCP server?](#why-another-microsoft-graph-mcp-server)
- [What it provides](#what-it-provides)
- [Getting started](#getting-started)
- [Prerequisites](#prerequisites)
- [1. Install](#1-install)
- [2. Register an app in Microsoft Entra ID](#2-register-an-app-in-microsoft-entra-id)
- [3. Authenticate](#3-authenticate)
- [4. Register with your MCP client](#4-register-with-your-mcp-client)
- [Prompt examples](#prompt-examples)
- [Configuration](#configuration)
- [Tool reference](#tool-reference)
- [Data & safety notes](#data--safety-notes)
- [Contributing](#contributing)
- [Limitations](#limitations)
- [Support](#support)
- [License](#license)
---
## Why another Microsoft Graph MCP server?
Several Microsoft Graph / Microsoft 365 MCP servers already exist. I have **no
affiliation** with any of them — they are referenced honestly for comparison,
exactly as with this project's siblings.
### The main players
- **[Official — Microsoft MCP Server for Enterprise]** — hosted by Microsoft,
*read-only Entra directory* only. It does not expose one tool per Graph operation;
instead it uses RAG to *generate Queries* on the fly (`microsoft_graph_suggest_queries`,
`microsoft_graph_get`, `microsoft_graph_list_properties`). Global cloud only,
write support still in preview, no self-hosting.
- **[Softeria/ms-365-mcp-server]** — Node.js, the closest "full coverage"
alternative: 300+ tools, 1:1 endpoint mapping, MSAL, read-only mode, tool
presets + dynamic discovery. Worth evaluating.
- **[DustHoff/msgraphmcp]** — Node.js/TypeScript, 140+ tools, four auth modes,
notably deep **Intune** coverage (~64 Intune tools).
- **[nitin27may/ms-graph-mcp]** — Python, 85 tools, no SDK / no `azure-identity`
(raw `httpx`), namespaced tools, read/write separation behind a write scope.
- **[CodyBuilder-dev/ms-graph-mcp]** — Node.js, device-code auth, mail/calendar/
Teams/To Do/contacts/files/OneDrive/SharePoint, but **no directory** surface.
- **[timfurlong/msgraph-mcp]** — Python, on PyPI, mail + calendar + read-only
Teams, MSAL device code only, uses the Graph SDK.
[Official — Microsoft MCP Server for Enterprise]: https://learn.microsoft.com/graph/mcp-server/overview
[Softeria/ms-365-mcp-server]: https://github.com/softeria/ms-365-mcp-server
[DustHoff/msgraphmcp]: https://github.com/DustHoff/msgraphmcp
[nitin27may/ms-graph-mcp]: https://github.com/nitin27may/ms-graph-mcp
[CodyBuilder-dev/ms-graph-mcp]: https://github.com/CodyBuilder-dev/ms-graph-mcp
[timfurlong/msgraph-mcp]: https://github.com/timfurlong/msgraph-mcp
### What sets this project apart
| Capability | Microsoft official | Softeria | DustHoff | nitin27may | CodyBuilder | timfurlong | **this project** |
|---|---|---|---|---|---|---|---|
| Language / runtime | hosted | Node.js | Node.js | Python | Node.js | Python | **Python** |
| Tools | 3 | 300+ | 140+ | 85 | ~50 | ~45 | **219** |
| Read-only Entra directory | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ |
| Mail (send/reply/rules/archive) | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Calendar + meetings + rooms | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Files / OneDrive (upload, sharing, revoke) | ❌ | ✅ | ✅ | ✅ | ✅ | partial | ✅ |
| SharePoint sites/lists/columns/items | ❌ | ✅ | ✅ | partial | ✅ | ❌ | ✅ |
| Teams channels + messages | ❌ | ✅ | ✅ | ✅ | ✅ | read-only | ✅ |
| Planner + To Do | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ |
| OneNote notebooks/pages | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ |
| Excel workbooks/ranges/tables | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Intune / device management | ❌ | partial | **deep** | ❌ | ❌ | ❌ | ✅ |
| Identity governance (reviews/PIM) | partial | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Security alerts/incidents + audit logs | ❌ | partial | ❌ | ❌ | ❌ | ❌ | ✅ |
| Reports (M365 usage) | ❌ | partial | ❌ | ❌ | ❌ | ❌ | ✅ |
| Change notifications / delta | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Graph `$batch` | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| **No SDK dependency** (`msgraph-sdk`/`azure-identity`) | n/a | n/a | ✅ | ✅ | n/a | ❌ | ✅ |
| **Sovereign clouds** (US Gov, China, Germany) | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
| **Hard read-only guard** (`MSGRAPH_READ_ONLY`) | n/a | ✅ | ❌ | ✅ | partial | ❌ | ✅ |
| **Coverage drift CI** vs official Graph spec | n/a | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
> Tool counts and capabilities are approximate, from the respective READMEs at
> the time of writing — always verify against the source. The point is not that
> "more is better", but that this project is the **deliberate Python,
> self-hostable, write-capable, zero-SDK** option covering the practical day-to-day
> Microsoft 365 surface an agent actually uses, with CI that keeps it honest
> against the ever-growing Graph API.
### Key differentiators
- **Full coverage, one auth seam.** Authenticate once (six modes) and reach
mail, files, calendar, Teams, SharePoint, Planner, To Do, OneNote, Excel,
presence, security, audit and admin — instead of stitching together several
single-workload servers.
- **No `msgraph-sdk`, no `azure-identity`.** A thin raw-HTTP client (`httpx`) +
`msal`. A radically smaller dependency tree and an inspectable wire path.
- **Writes are possible but guarded.** Write tools call `require_write()`; set
`MSGRAPH_READ_ONLY=true` to forbid every one of them. Nothing mutates your
tenant by accident.
- **Coverage drift CI.** A scheduled job compares the tool surface against the
official Microsoft Graph OpenAPI spec and files an issue when Microsoft ships
endpoints we don't cover yet — so the gap is visible instead of silent.
---
## What it provides
A single stdio/HTTP MCP server exposing **219 tools** across 25 workloads:
- **Mail** — list/search/get messages, send, reply, forward, drafts, folders,
move, rules, attachments, mailbox settings
- **Files** — OneDrive + SharePoint drives: items, upload sessions, versions,
permissions, sharing links, revoke
- **Calendar** — events, calendars, free/busy, meeting times, rooms, schedules
- **Teams** — teams, channels, messages, replies, members, tabs
- **Users & groups** — profiles, photos, licenses, memberships, members/owners
- **Planner & To Do** — plans, buckets, tasks, assignments, checklist items
- **OneNote** — notebooks, sections, section groups, pages
- **Excel** — workbooks, worksheets, ranges, tables, sessions
- **SharePoint** — sites, lists, columns, content types, list items
- **Admin & security** — directory, device management, security
alerts/incidents, audit logs, reports, identity governance
- **Meetings & presence** — online meetings, transcripts, attendance,
recordings, presence
- **Search & integration** — unified Microsoft Search, `$batch`, change
notifications / delta
—
## Getting started
You need an MCP-capable client (opencode, Claude Desktop, Cursor, etc.).
### 1. Install
**Option A — from PyPI (recommended)**
```bash
uvx msgraph-mcp-full
# or
pip install msgraph-mcp-full
```
**Option B — from GitHub (latest source)**
```bash
uvx --from "git+https://github.com/oliverhruby/msgraph-mcp.git" msgraph-mcp-full
# or
pip install "git+https://github.com/oliverhruby/msgraph-mcp.git"
```
**Option C — development from source**
```bash
git clone https://github.com/oliverhruby/msgraph-mcp.git
cd msgraph-mcp
uv sync # or: python -m venv .venv && .venv/bin/python -m pip install -e ".[dev]"
uv run msgraph-mcp-full
```
Run the checks before merging changes:
```bash
uv run ruff check src scripts tests
uv run pytest -q # offline unit suite (auth, client, 219-tool sweep)
uv run pytest -m integration -q # live read-only tests; skips without creds
```
**Option D — Docker**
```bash
docker pull ghcr.io/oliverhruby/msgraph-mcp:latest
docker run --rm -i \
-e MSGRAPH_AUTH_MODE=client_secret \
-e MSGRAPH_CLIENT_ID=... -e MSGRAPH_CLIENT_SECRET=... \
-e MSGRAPH_TENANT_ID=... \
ghcr.io/oliverhruby/msgraph-mcp:latest
```
> `pyproject.toml` pins `mcp<2` (the stable FastMCP v1 API). `mcp 2.x` renamed
> `FastMCP` to `MCPServer` and changed the API surface; this server targets the
> FastMCP v1 API for simplicity and stability.
### 2. Register an app in Microsoft Entra ID
1. [App registrations](https://aka.ms/AppRegistrations) → New registration.
2. Under **Authentication**, add a public client / redirect for the auth mode
you plan to use (device-code and interactive need no redirect URI).
3. Grant the API permissions your tools need (delegated and/or application).
`DEFAULT_DELEGATED_SCOPES` in `auth.py` lists sensible defaults.
4. Note the **Tenant ID**, **Client ID** (and **Client Secret** / **cert** if
using those modes).
**Automated (Azure CLI)** — the same registration can be created from your
`az` identity with one command (Bicep/ARM cannot register Entra apps; they live
in Microsoft Graph, not Azure Resource Manager):
```bash
az ad app create --display-name msgraph-mcp-local \
--sign-in-audience AzureADMyOrg \
--public-client-redirect-uris "http://localhost" \
--is-fallback-public-client true
```
`scripts/setup_entra_app.ps1` wraps this for the common cases — it reuses an
app if it already exists, prints the **Client ID / Tenant ID**, optionally
persists `MSGRAPH_CLIENT_ID` / `MSGRAPH_TENANT_ID` (`setx`), dumps the IDs to a
JSON file for other tooling, and can tear the registration back down:
```powershell
.\scripts\setup_entra_app.ps1 # create + print IDs
.\scripts\setup_entra_app.ps1 -SetEnv # + persist env vars
.\scripts\setup_entra_app.ps1 -OutFile C:\temp\g.json # + write IDs for pipeline
.\scripts\setup_entra_app.ps1 -Delete -AppId <clientId> # remove registration
```
The equivalent automation paths are the Microsoft Graph API (e.g. via
`az rest --method POST --uri https://graph.microsoft.com/v1.0/applications`)
or the Entra (`azuread`) Terraform provider, if you need full IaC.
### 3. Authenticate
Configuration is entirely via environment variables.
#### Interactive (default)
```bash
export MSGRAPH_AUTH_MODE=interactive
export MSGRAPH_CLIENT_ID=00000000-0000-0000-0000-000000000000
export MSGRAPH_TENANT_ID=11111111-1111-1111-1111-111111111111
msgraph-mcp-full
```
A browser opens, you sign in, and a token cache is saved at
`~/.msgraph-mcp/token_cache.json` for later runs.
#### Device code
```bash
export MSGRAPH_AUTH_MODE=device_code
export MSGRAPH_CLIENT_ID=...
msgraph-mcp-full
```
#### Client secret (app-only / daemon)
```bash
export MSGRAPH_AUTH_MODE=client_secret
export MSGRAPH_CLIENT_ID=...
export MSGRAPH_CLIENT_SECRET=...
export MSGRAPH_TENANT_ID=...
msgraph-mcp-full
```
#### Certificate (app-only)
```bash
export MSGRAPH_AUTH_MODE=certificate
export MSGRAPH_CLIENT_ID=...
export MSGRAPH_CERT_PATH=/path/to/cert.pem
export MSGRAPH_TENANT_ID=...
msgraph-mcp-full
```
#### On-behalf-of
```bash
export MSGRAPH_AUTH_MODE=obo
export MSGRAPH_CLIENT_ID=... MSGRAPH_CLIENT_SECRET=... MSGRAPH_TENANT_ID=...
msgraph-mcp-full
```
#### Managed identity (Azure)
```bash
export MSGRAPH_AUTH_MODE=managed_identity # or `managed_identity_user`
export MSGRAPH_CLIENT_ID=... # the managed identity's client id
msgraph-mcp-full
```
#### Pre-existing token
```bash
export MSGRAPH_AUTH_MODE=pre_token
export MSGRAPH_ACCESS_TOKEN=eyJ...
msgraph-mcp-full
```
### 4. Register with your MCP client
**opencode** — add to `~/.config/opencode/opencode.json` (or `opencode.jsonc`):
```jsonc
{
"mcp": {
"msgraph": {
"type": "local",
"enabled": true,
"command": ["uvx", "msgraph-mcp-full"],
"env": {
"MSGRAPH_AUTH_MODE": "client_secret",
"MSGRAPH_CLIENT_ID": "{env:MSGRAPH_CLIENT_ID}",
"MSGRAPH_CLIENT_SECRET": "{env:MSGRAPH_CLIENT_SECRET}",
"MSGRAPH_TENANT_ID": "{env:MSGRAPH_TENANT_ID}"
}
}
}
}
```
> Put credentials in your shell/environment (or a `.env`) and reference them
> with `{env:VAR}`, or hardcode them under `env:` directly.
**Claude Desktop / Cursor** — use `claude_desktop_config.json` / `.mcp.json`
with a standard `mcpServers` entry pointing `command`/`args` at the venv python
and `-m msgraph_mcp`, plus an `env` block.
Over HTTP/SSE (`MCP_TRANSPORT=streamable-http`):
```jsonc
{
"mcpServers": {
"msgraph": {
"url": "http://127.0.0.1:3000/mcp",
"headers": { "Authorization": "Bearer <MCP_API_KEY>" }
}
}
}
```
After editing client config, **restart the client** so the MCP server is loaded.
---
## Prompt examples
| User prompt | Likely tool call(s) | Expected response |
|---|---|---|
| "Show my last 10 emails" | `list_messages top=10` | Recent messages (sender, subject, preview). |
| "Draft a reply to Oliver accepting the meeting" | `get_message` → `reply_message` | Draft/sent confirmation (or draft body to review). |
| "What's on my calendar this week?" | `list_events_in_range` | A short event list with times. |
| "Create a 1-hour meeting tomorrow at 10:00" | `create_event` | Created event with id + link. |
| "Upload `report.xlsx` to my OneDrive root" | `upload_file` | File metadata + sharing-ready link. |
| "List questions asked in the #general channel today" | `list_channel_messages` | Channel messages with authors. |
| "What tasks are due in my To Do this week?" | `list_tasks` | Tasks with due dates and states. |
| "Any high-severity security alerts?" | `list_security_alerts` | Alerts with severity + status. |
| "Who signed in from outside the US recently?" | `list_sign_in_logs` | Matching sign-in log entries. |
---
## Configuration
| Variable | Default | Description |
|---|---|---|
| `MSGRAPH_AUTH_MODE` | `interactive` | `interactive`, `device_code`, `client_secret`, `certificate`, `obo`, `managed_identity`, `managed_identity_user`, `pre_token` |
| `MSGRAPH_CLIENT_ID` | *(empty)* | Entra app (client) ID |
| `MSGRAPH_TENANT_ID` | `common` | Entra tenant ID |
| `MSGRAPH_CLIENT_SECRET` | *(empty)* | App secret (client_secret / obo) |
| `MSGRAPH_CERT_PATH` | *(empty)* | Path to PEM cert (certificate mode) |
| `MSGRAPH_CERT_PASSWORD` | *(empty)* | Optional cert password |
| `MSGRAPH_SCOPES` | *(built-in defaults)* | Comma-separated extra scopes |
| `MSGRAPH_CLOUD` | `global` | `global`, `us_gov_l4`, `us_gov_l5`, `china`, `germany` |
| `MSGRAPH_BASE_URL` | *(from cloud)* | Override Graph base URL |
| `MSGRAPH_API_VERSION` | `v1.0` | `v1.0` or `beta` |
| `MSGRAPH_ACCESS_TOKEN` | *(empty)* | Pre-acquired token (pre_token) |
| `MSGRAPH_HTTP_TIMEOUT` | `60` | Seconds a tool call waits for the Graph response; raise for interactive/`device_code` sign-in, which waits for you in the browser |
| `MSGRAPH_TOKEN_CACHE_PATH` | `~/.msgraph-mcp/token_cache.json` | Token cache location |
| `MSGRAPH_READ_ONLY` | `false` | `true` disables every write tool |
| `MSGRAPH_MAX_PAGES` | `50` | Max pages auto-paginated per collection |
| `MSGRAPH_LOG_LEVEL` | `WARNING` | Python log level |
| `MSGRAPH_TOOLSETS` | `core` | Reserved for future per-workload gating |
| `MCP_TRANSPORT` | `stdio` | `stdio` or `streamable-http` |
| `MCP_HOST` | `127.0.0.1` | HTTP bind host |
| `MCP_PORT` | `3000` | HTTP bind port |
| `MCP_API_KEY` | *(empty)* | Bearer token required for HTTP transport |
---
## Tool reference
Tools are grouped by workload (each module under `src/msgraph_mcp/tools/`).
| Workload | # Tools | Examples |
|---|---|---|
| `mail` | 19 | list/send/reply/forward messages, folders, drafts, rules, attachments, mailbox settings |
| `files` | 19 | OneDrive items, upload sessions, versions, permissions, sharing links, revoke |
| `sharepoint` | 17 | sites, lists, columns, content types, list items |
| `calendar` | 14 | events, calendars, free/busy, findMeetingTimes, rooms, schedule |
| `teams` | 13 | teams, channels, messages, replies, members, tabs |
| `users` | 12 | profiles, photo, direct reports, licenses, transitive memberships |
| `onenote` | 11 | notebooks, sections, section groups, pages (CRUD + copy) |
| `planner` | 11 | plans, buckets, tasks, task details, assignments |
| `excel` | 10 | workbooks, worksheets, ranges, tables, sessions |
| `directory` | 9 | directory objects, members, roles |
| `groups` | 9 | CRUD, members, owners, transitive membership |
| `todo` | 9 | task lists, tasks, checklist items |
| `online_meetings` | 8 | create meetings, transcripts, attendance, recordings |
| `chat` | 7 | chats, messages, create chat |
| `contacts` | 7 | contacts, contact folders |
| `device_management` | 7 | managed devices, compliance |
| `security` | 6 | alerts, incidents, secure scores |
| `change_notifications` | 5 | subscriptions (webhooks), delta |
| `identity_governance` | 5 | access reviews, entitlement mgmt, PIM |
| `people` | 5 | relevance search, insights, profile |
| `reports` | 5 | Microsoft 365 usage reports |
| `audit_logs` | 4 | sign-ins, directory audits, provisioning |
| `presence` | 4 | user presence, batch presence |
| `search` | 2 | unified Microsoft Search (KQL) |
| `batch` | 1 | Graph `$batch` |
| **Total** | **219** | |
Each tool is decorated with `@graph_tool(...)` carrying its HTTP method, Graph
endpoint, and the delegated + application scopes required. Read any tool's
docstring for exact behaviour; write tools call `require_write()` and fail fast
in read-only mode.
---
## Data & safety notes
- Most tools are **read-only**. The write tools (send/reply, create event,
upload/share/revoke file, create task, admin mutations, …) mutate real
Microsoft 365 state. Use them with care, or set `MSGRAPH_READ_ONLY=true` to
disable the entire write surface.
- The server acts **as your credentials** — it can reach only what the
authenticated principal can reach. Delegated modes honour your user's
permissions; app-only modes use the application's granted permissions.
- Sovereign-cloud endpoints differ; set `MSGRAPH_CLOUD` accordingly.
- `MSGRAPH_API_VERSION=beta` exposes beta endpoints that may change without
notice — prefer `v1.0` unless you know you need a beta API.
---
## Contributing
Contributor and maintainer guidance is in [CONTRIBUTING.md](CONTRIBUTING.md).
- Contribution workflow and local setup
- Architecture and implementation details
- Release process (PyPI, GitHub Releases, GHCR)
- CI quality gates and upstream coverage drift checks
---
## Limitations
- **Token lifetime = server lifetime.** The auth session lives for the lifetime
of the MCP server process; restarting the client means signing in again (the
on-disk cache in `~/.msgraph-mcp/` helps for interactive/device flows).
- **Beta surface is opt-in.** Only `v1.0` is covered by the coverage-drift
baseline; beta endpoints are registered but not independently monitored.
- **Coverage is broad, not exhaustive.** The Graph API has ~18,000 v1.0
operations; this server wraps the practical day-to-day surface (219 tools) and
tracks the gap via the coverage-drift canary rather than claiming 100%.
- **Intune & admin depth.** Device management and Intune are covered at a useful
level but not as deep as a dedicated Intune server (e.g. DustHoff's).
- **No Graph Explorer-grade generality.** For an arbitrary, not-yet-covered
endpoint, prefer the official server, GraphMind-style indexing, or a raw
`$batch` call.
---
## Support
If you like this project and want to support or request a feature, send me a
beer, it keeps my mind relaxed and ideas will come :-)
[](https://www.paypal.me/oliverhruby/)
---
## License
[MIT](LICENSE) © Oliver Hrubý
This project is **not affiliated with or endorsed by** Microsoft or any of the
other MCP servers referenced above. Microsoft Graph and Microsoft 365 are
trademarks of Microsoft Corporation.
TDQS
Scored across 219 tools
While most tools are named for a distinct resource/action, there are multiple near-duplicate or ambiguous tools: list_list_items vs list_list_items_with_fields, list_transcripts vs list_all_transcripts, list_sections vs list_notebook_sections, and delta_messages vs delta_query. With 219 tools, an agent is likely to select the wrong listing or search tool.
The overwhelming majority use a consistent lowercase verb_noun pattern such as list_, get_, create_, update_, and delete_. A few tools like batch_request, delta_query, search_query, and people_search break the verb-first convention, so the pattern is not perfectly uniform.
219 tools is an extreme surface area for any agent to evaluate and falls far beyond the well-scoped 3-15 tool range. Even though Microsoft Graph is vast, exposing all these operations as flat MCP tools makes selection and maintenance unwieldy.
The server gives solid lifecycle coverage to popular resources like mail, calendar events, files, and SharePoint list items, but many areas are read-only or partial: Teams has no create_team, planner plans/buckets cannot be updated or deleted, security alerts cannot be acted on, and directory/device tools mostly list or get only. batch_request provides a generic escape hatch, but it still requires the agent to know raw Graph API details, so significant gaps remain.