Content
# M365 MCP
This lets Claude Desktop work with your Microsoft 365 mailbox, calendar, contacts, folders, rules, categories, and small readable attachments through Microsoft Graph.
It is a local MCP server. That means Claude starts it on your computer when Claude Desktop opens. You normally do not need to run the server manually.
## What Claude Can Do
Claude can:
- Check and search mail, including shared mailboxes you can access.
- Read message bodies, threads, categories, flags, Focused/Other classification, read/unread state, subfolders, and small text/PDF attachments.
- Draft or send mail, reply in threads, move messages, and mark messages read/unread.
- Create, rename, delete, and navigate mail folders and subfolders.
- List, create, update, and delete Outlook Inbox rules.
- Search, create, update, and delete contacts.
- List, create, update, and delete calendar events.
- Browse SharePoint sites, document libraries, and folders, and find files anywhere you have access.
- Edit Excel workbooks stored in SharePoint/OneDrive in place: read and write cell ranges and append table rows.
Claude can also read [M365_MCP_CAPABILITIES.md](M365_MCP_CAPABILITIES.md) through the `m365_capabilities` tool or the `m365://capabilities` MCP resource.
## Setup Checklist
You need four things:
- `uv` installed on the computer running Claude Desktop.
- A Microsoft Entra app registration.
- A local `.env` file with your Microsoft app values.
- A Claude Desktop config entry for this MCP server.
## 1. Install uv
`uv` is the Python runner Claude will use to start this MCP server.
macOS or Linux:
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
Windows PowerShell:
```powershell
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
```
Close and reopen your terminal, then check that it installed:
```bash
uv --version
```
If Claude Desktop later cannot find `uv`, use the full path to `uv` in `claude_desktop_config.json`. On Windows, run:
```powershell
where uv
```
## 2. Create The Microsoft App
In the Azure Portal, create a Microsoft Entra app registration for local use.
Use these settings:
- Platform: `Web`
- Redirect URI: `http://localhost:8787/auth/microsoft/callback`
- Supported account type: `Accounts in this organizational directory only`
- Advanced setting: `Allow public client flows = No`
Create a client secret under `Certificates & secrets`. Copy the secret `Value`, not the `Secret ID`.
Add these delegated Microsoft Graph permissions:
- `Mail.ReadWrite`
- `Mail.ReadWrite.Shared`
- `Mail.Send`
- `Mail.Send.Shared`
- `Calendars.ReadWrite.Shared`
- `Contacts.ReadWrite.Shared`
- `MailboxSettings.ReadWrite`
- `Sites.Read.All`
- `Files.ReadWrite.All`
- `openid`
- `profile`
- `email`
- `offline_access`
If your organization requires admin approval, click `Grant admin consent`.
Focused/Other mail triage uses Microsoft Graph's `inferenceClassification` message property. There is no separate delegated Graph permission named `inferenceClassification`; the existing mail read/write scopes above cover reading that field. Microsoft documents this in [Manage Focused Inbox](https://learn.microsoft.com/en-us/graph/api/resources/manage-focused-inbox?view=graph-rest-1.0) and the [message resource](https://learn.microsoft.com/en-us/graph/api/resources/message?view=graph-rest-1.0).
## 3. Create Your .env File
Copy `.env.example` to `.env`, then fill in the values.
Important fields:
- `MICROSOFT_TENANT_ID`: the Azure tenant/directory ID.
- `MICROSOFT_CLIENT_ID`: the Azure app/client ID.
- `MICROSOFT_CLIENT_SECRET`: the client secret `Value`.
- `TOKEN_ENCRYPTION_KEY`: a base64 32-byte key used to encrypt the local token cache.
- `KNOWN_MAILBOXES`: optional comma-separated shared mailboxes, such as `shared@company.com`.
- `M365_AUDIT_LOG_ENABLED`: optional. Defaults to `true`.
- `M365_AUDIT_LOG_FILE`: optional. Defaults to `.audit/m365-mcp-audit.jsonl`.
Generate `TOKEN_ENCRYPTION_KEY` with:
```bash
python3 -c "import os, base64; print(base64.b64encode(os.urandom(32)).decode())"
```
On Windows, if `python3` is not available, try:
```powershell
python -c "import os, base64; print(base64.b64encode(os.urandom(32)).decode())"
```
After filling in `.env`, restrict its permissions so only your user account can read it:
```bash
chmod 600 .env
```
On Windows, right-click `.env` → Properties → Security → Advanced and remove access for any account other than your own. `.env` contains plaintext secrets; anyone who can read the file can authenticate as your Microsoft app.
## 4. Add It To Claude Desktop
Use [claude_desktop_config.json](claude_desktop_config.json) as the starting point. It keeps Claude's default `preferences` block and adds the `m365` MCP server.
If you are adding this server inside Codex instead of Claude Desktop, use [CODEX_MCP_SETUP.md](CODEX_MCP_SETUP.md). Codex has separate fields for command and arguments, so the setup is a little different.
Replace this example path:
```text
C:\Users\YOUR_WINDOWS_USER\Documents\m365mcp
```
with the real full path to this repo on your computer.
The config should look like this:
```json
{
"preferences": {
"coworkScheduledTasksEnabled": false,
"coworkWebSearchEnabled": true,
"ccdScheduledTasksEnabled": false
},
"mcpServers": {
"m365": {
"command": "uv",
"args": [
"--directory",
"C:\\Users\\YOUR_WINDOWS_USER\\Documents\\m365mcp",
"run",
"--env-file",
".env",
"mcp",
"run",
"src/m365_mcp/server.py"
]
}
}
}
```
Keep Microsoft secrets in `.env`. Do not paste tenant IDs, client secrets, or token keys directly into Claude's config.
After saving the config, fully quit and reopen Claude Desktop.
## Alternative To Steps 3 And 4: Install As A `.mcpb` Extension
This repo also ships an [MCP Bundle](https://github.com/anthropics/mcpb) manifest ([manifest.json](manifest.json)), so you can install the server into Claude Desktop as a single `.mcpb` file instead of writing a `.env` file and editing `claude_desktop_config.json` by hand.
You still need the Microsoft Entra app from step 2. You do not need step 3 or step 4.
Download `m365-mcp-<version>.mcpb` from the [Releases page](https://github.com/ianrelecker/m365mcp/releases). If you would rather build it yourself from a checkout of this repo:
```bash
npx @anthropic-ai/mcpb pack
```
Open the `.mcpb` file with Claude Desktop, or drag it onto the Extensions pane in Claude Desktop settings, and Claude will show an install dialog asking for:
- **Microsoft Tenant ID**
- **Microsoft Client ID**
- **Microsoft Client Secret**
- **Token Encryption Key** — generate it with the command in step 3
- **Known Shared Mailboxes** — optional, comma-separated
- **Local Sign-in Port** — defaults to `8787`
- **Local Audit Log** — defaults to on
Claude stores those values for you, so no `.env` file is involved. Secrets are marked sensitive in the manifest, so Claude keeps them in the OS keychain rather than in plain config.
Notes:
- The bundle uses the `uv` server type: it ships this repo's source plus `pyproject.toml` and `uv.lock`, and Claude installs the Python dependencies with `uv` at install time. Recent Claude Desktop versions manage `uv` and Python for you.
- If you change **Local Sign-in Port**, update the redirect URI on your Entra app registration to match: `http://localhost:PORT/auth/microsoft/callback`.
- After installing, continue with step 5 to connect Microsoft.
- `.env`, `.tokens/`, `.audit/`, and `claude_desktop_config.json` are excluded from the bundle by [.mcpbignore](.mcpbignore), so a bundle you build never carries your local secrets. Still, treat a built `.mcpb` as source code you are sharing, not as a configured install.
## 5. Connect Microsoft
Do not run the MCP server manually for normal use. Let Claude Desktop start it.
After Claude Desktop reopens, the local auth site should be available here:
```text
http://localhost:8787/
```
If that page is not available yet, open a Claude chat and ask:
```text
Check my Microsoft auth status with the m365 MCP server.
```
Claude should start the MCP server and call `auth_status`. The result includes the Microsoft connect URL.
To sign in, open the local helper page and click **Connect Microsoft 365**:
```text
http://localhost:8787/
```
Sign in with the Microsoft 365 account Claude should use. After sign-in, tokens are stored locally at `.tokens/microsoft-graph-token.json`, encrypted with `TOKEN_ENCRYPTION_KEY`.
If Claude says it is not authenticated, or `auth_status` shows missing scopes, return to the local helper page and click **Connect Microsoft 365** again to reconnect:
```text
http://localhost:8787/
```
If `offline_access` is the only missing scope, reconnecting through the local auth link is still the right fix. It allows Microsoft to issue a refresh token so the local server can keep working after the current access token expires.
If `http://localhost:8787/` does not load, Claude probably did not start the MCP server. Open Claude Desktop settings, find the `m365` MCP server, click `View Logs`, and check the troubleshooting section below.
## Everyday Use
Once authenticated, ask Claude things like:
- `Check my Microsoft inbox.`
- `Search my M365 mail for invoices from Microsoft.`
- `Read this thread and draft a reply.`
- `Create a subfolder under Inbox called Clients.`
- `Move this message to Inbox/Clients/Acme.`
- `Create a rule that moves Acme invoices to that folder.`
- `Read the PDF attachment on this email.`
- `Create a calendar event for tomorrow at 2 PM.`
For shared mailboxes, mention the mailbox address in your request, for example:
```text
Check the shared@company.com inbox.
```
## Troubleshooting
If Claude shows `Server disconnected`, click `View Logs`.
Common fixes:
- If the logs say `TOKEN_ENCRYPTION_KEY must be a base64-encoded 32-byte key`, regenerate `TOKEN_ENCRYPTION_KEY` and update `.env`.
- If the MCP details still show placeholder values like `MICROSOFT_TENANT_ID=your-tenant-id`, remove any old environment-variable block from Claude's config and use `--env-file .env`.
- If the logs include `WinError 10048` or say the port is already in use, something else is using port `8787`. Stop the other process, then restart Claude Desktop.
- If the local auth page does not open, make sure Claude Desktop is running and the `m365` MCP server is enabled.
- If Claude cannot find `uv`, replace `"command": "uv"` with the full path from `where uv` on Windows or `which uv` on macOS/Linux.
- If Microsoft sign-in fails, confirm the Azure redirect URI exactly matches `http://localhost:8787/auth/microsoft/callback`.
- If `auth_status` reports `missingScopes`, add the missing permissions in Azure, grant consent if needed, then reconnect Microsoft.
## For Developers
You do not need these commands for normal Claude Desktop use.
Install dependencies:
```bash
uv sync
```
Run tests:
```bash
uv run pytest
```
Optional manual smoke test:
```bash
uv run mcp run src/m365_mcp/server.py
```
Stop the manual smoke test before opening Claude Desktop. Two copies cannot both use the same localhost helper port.
Build and validate the `.mcpb` bundle:
```bash
npx @anthropic-ai/mcpb validate manifest.json
npx @anthropic-ai/mcpb pack
```
`manifest.json` advertises every tool this server exposes. After adding, renaming, or removing a tool, regenerate that list:
```bash
uv run python scripts/sync_mcpb_tools.py
```
`tests/test_manifest.py` fails if the manifest is out of date, so `uv run pytest` catches a missed sync.
To publish a bundle, bump `version` in both `pyproject.toml` and `manifest.json`, then push a matching tag:
```bash
git tag v0.1.0
git push origin v0.1.0
```
The [Release MCPB bundle](.github/workflows/release-mcpb.yml) workflow verifies the tag matches `manifest.json`, checks the tool list is in sync, runs the tests, packs the bundle, and attaches it to the GitHub Release for that tag. Running the workflow manually from the Actions tab builds the bundle and uploads it as a workflow artifact without creating a release.
## Tool Reference
Mail and folders:
- `auth_status`
- `m365_capabilities`
- `mail_check_inbox`
- `mail_list`
- `mail_search`
- `mail_get`
- `mail_list_drafts`
- `mail_create_draft`
- `mail_send`
- `mail_send_draft`
- `mail_move`
- `mail_list_folders`
- `mail_folder_tree`
- `mail_resolve_folder`
- `mail_create_folder`
- `mail_rename_folder`
- `mail_delete_folder`
Attachments, threads, categories, and rules:
- `mail_list_attachments`
- `mail_get_attachment_content`
- `mail_get_thread`
- `mail_create_reply_draft`
- `mail_send_reply`
- `mail_list_categories`
- `mail_set_categories`
- `mail_add_categories`
- `mail_remove_categories`
- `mail_clear_categories`
- `mail_create_category`
- `mail_update_category`
- `mail_delete_category`
- `mail_mark_read`
- `mail_set_flag`
- `mail_list_rules`
- `mail_create_rule`
- `mail_update_rule`
- `mail_delete_rule`
Contacts and calendar:
- `contacts_list`
- `contacts_search`
- `contacts_get`
- `contacts_create`
- `contacts_update`
- `contacts_delete`
- `contacts_set_categories`
- `contacts_add_categories`
- `contacts_remove_categories`
- `contacts_clear_categories`
- `contacts_list_folders`
- `calendar_list_events`
- `calendar_create_event`
- `calendar_update_event`
- `calendar_delete_event`
SharePoint and OneDrive files:
- `sharepoint_search_items`
- `sharepoint_search_sites`
- `sharepoint_get_site`
- `sharepoint_list_drives`
- `sharepoint_list_children`
- `sharepoint_search_in_drive`
- `sharepoint_get_item_by_url`
Excel workbooks:
- `workbook_resolve`
- `workbook_list_worksheets`
- `workbook_list_tables`
- `workbook_get_range`
- `workbook_get_used_range`
- `workbook_get_ranges`
- `workbook_update_range`
- `workbook_update_ranges`
- `workbook_add_table_row`
- `workbook_clear_range`
- `workbook_copy_range`
- `workbook_insert_range`
- `workbook_delete_range`
- `workbook_calculate`
- `workbook_list_names`
- `workbook_get_name_range`
- `workbook_create_session`
- `workbook_close_session`
## Contacts
- Use `contacts_search` to resolve recipients before drafting mail.
- Use `contacts_list_folders` when contacts are organized in folders.
- Contact tools can create, update, and delete Outlook contacts in mailboxes the signed-in user can access.
- Contacts include category names, parent folder IDs, Outlook Website values, personal notes, and structured business/home/other addresses.
- Use `contacts_set_categories`, `contacts_add_categories`, `contacts_remove_categories`, and `contacts_clear_categories` to manage contact categories. These use the same Outlook master categories as mail.
- Use `contacts_update` with `personalHomePage`, `personalNotes`, `businessAddress`, `homeAddress`, or `otherAddress` for website, notes, street, city, state, country or region, and postal code changes.
- `personalHomePage` maps to Outlook Contacts `Other -> Website` through the MAPI `PR_PERSONAL_HOME_PAGE` extended property (`String 0x3A50`). This does not require an additional Graph permission beyond the existing contact permission.
## Mail Notes
- Prefer `mail_check_inbox` or `mail_list` filters for fast inbox triage. `mail_search` uses Microsoft Graph `$search`, which can be slower on large mailboxes.
- `mail_get_thread` sorts returned thread messages locally by received time when available instead of asking Graph to sort a filtered conversation query.
- `mail_update_category` can update an Outlook master category color. Microsoft Graph does not support renaming an existing master category; create a new category and delete the old one only after confirming that is safe.
## SharePoint And Excel Notes
- The `sharepoint_*` tools are read-only browsing. Start with `sharepoint_search_items` to find a file or folder anywhere you have access, or walk `sharepoint_search_sites` -> `sharepoint_list_drives` -> `sharepoint_list_children`. Each tool returns a `driveId` and `itemId` you can pass on.
- The `workbook_*` tools edit `.xlsx` files **in place** through the Microsoft Graph Workbook API. Resolve the file once with `workbook_resolve`, then reuse its `driveId` + `itemId`. Edits are applied by Excel server-side, so formulas, formatting, and validation are preserved, and SharePoint versions every change.
- `workbook_update_range` and `workbook_add_table_row` write directly to the stored file, so confirm the workbook, worksheet, and range before writing.
- These features need the `Sites.Read.All` and `Files.ReadWrite.All` delegated permissions. `Sites.Read.All` is read-only and only covers browsing SharePoint sites and libraries the signed-in user can already access; the in-place workbook edits are authorized by `Files.ReadWrite.All` (workbook calls go through `/drives`, not `/sites`), so no SharePoint write permission is requested. If `auth_status` lists either under `missingScopes`, add them in Azure, grant consent if required, then reconnect Microsoft.
- If you previously connected with the broader `Sites.ReadWrite.All`, `auth_status` will report `Sites.Read.All` as missing until you reconnect, because the granted token still carries the old scope. Add `Sites.Read.All` to the Azure app registration and reconnect to refresh it.
## Security Notes
- `.env`, `.env.local`, and `.tokens/` are local-only files and are ignored by git.
- `.audit/` is local-only and ignored by git. It stores JSONL tool-call audit records for incident review.
- `TOKEN_ENCRYPTION_KEY` encrypts the saved Microsoft token cache at rest. If you rotate or lose it, delete `.tokens/microsoft-graph-token.json` and reconnect Microsoft.
- On macOS and Linux, the token file (`.tokens/microsoft-graph-token.json`) and audit log (`.audit/m365-mcp-audit.jsonl`) are written with `0o600` permissions (owner read/write only) and their parent directories with `0o700`. This is enforced automatically by the server; no manual step is needed.
- On Windows, token and audit file permissions rely on your user-profile ACLs. Restrict `.env` manually as described in step 3.
- This server uses a confidential-client `Web` app registration, so `MICROSOFT_CLIENT_SECRET` is required.
- Microsoft sign-in uses authorization-code flow with PKCE. PKCE hardens the login code exchange, but it does not reduce Microsoft Graph permissions or replace token protection.
- The Connect and Disconnect actions on the local helper page (`http://localhost:8787/`) are form POST submissions, not plain links, so they cannot be triggered by a cross-origin web page loaded in your browser while the server is running.
- Audit records include timestamp, tool name, outcome, mailbox, operation category, and key IDs such as message/event/folder/rule/drive/item IDs when present.
- Audit records do not include access tokens, refresh tokens, client secrets, encryption keys, email bodies, attachment content, draft body text, calendar body text, or raw Microsoft Graph payloads.
- Treat `MICROSOFT_CLIENT_SECRET` like any other local credential and do not place it in shared configs or screenshots.
- This is for local MCP clients, not `claude.ai` remote connectors.
- No public HTTPS endpoint, IIS, WSL, Linux server, or public web server is required.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
Python tool for converting files and office documents to Markdown.
awesome-claude-skills
A curated list of awesome Claude Skills, resources, and tools for...
antigravity-awesome-skills
The Ultimate Collection of 130+ Agentic Skills for Claude...
claude-context-mode
claude-context-mode plugin reduces MCP context bloat, saving up to 99% of tokens.
context-mode
MCP is the protocol for tool access. We're the virtualization layer for context.