m365-mcp
A Model Context Protocol server that gives Claude Code (or any MCP client) controlled access to Microsoft 365 through the Microsoft Graph API: mail, calendar, contacts, files, notes, tasks, Teams, SharePoint, and the full tenant-admin surface.
README
m365-mcp
A Model Context Protocol server that gives Claude Code (or any MCP client) controlled access to Microsoft 365 through the Microsoft Graph API: mail, calendar, contacts, files, notes, tasks, Teams, SharePoint, and the full tenant-admin surface.
It runs locally as a stdio subprocess. There is no hosted component, no client secret, and no inbound network exposure. Authentication is a local public-client browser flow, and every token stays on your machine.
Status: 441 tools, multi-account (personal and work identities mixed in one server), public-client auth with per-account token caches. License: MIT.
┌──────────────────────────────────────────────────────────────────┐
│ 441 tools total │
│ │
│ 173 available to PERSONAL Microsoft accounts (167 + 6 mgmt) │
│ 441 available to WORK / school accounts (everything) │
│ │
│ Same tool names for both. Work-only tools are blocked, with a │
│ clear error, when called against a personal account. │
└──────────────────────────────────────────────────────────────────┘
The capability story has three tiers, and the rest of this document follows that order: what a personal account gets, what a business (work/school) account adds, and what a tenant administrator unlocks on top.
Tier 1: Personal Microsoft accounts
If you sign in with a consumer Microsoft account (@outlook.com, @hotmail.com,
@live.com, @msn.com), you get the complete personal productivity surface:
173 tools (167 Graph tools plus 6 account-management tools). Everything in
this tier works against a personal mailbox with no tenant, no admin, and no
license beyond a normal Microsoft account.
| Family | Tools | What it covers |
|---|---|---|
| 50 | Read, search (full-text + paginated), draft, reply, reply-all, forward, send, move, copy, flag, categorize, delete, folders, rules, inbox-tip checks, MIME export, Focused-Inbox overrides, attachments (incl. >3 MB upload sessions), auto-reply | |
| Calendar | 25 | List/create/update/delete events, respond to invites, calendars + groups, recurring-event expansion, find_meeting_times, free/busy, reminders, event attachments |
| Contacts | 15 | Search, CRUD, contact folders, per-contact photos, delta sync |
| Microsoft To Do | 14 | Task lists, tasks, checklist steps, linked resources, delta sync |
| OneDrive | 24 | Browse, upload, download, copy/move, share links, invitations, permissions, versions, search, recent, thumbnails, Office-Online previews, delta sync |
| OneNote | 14 | Notebooks, sections, section groups, pages, HTML content read/write, previews |
| Profile + Mailbox settings | 13 | Profile, photo, working hours, time zone, language, supported-locale lists |
| Subscriptions / webhooks | 5 | Change-notification subscriptions (needs a public HTTPS callback) |
| Refinements | 7 | Master categories, unified search, reference + item attachments |
| Account management | 6 | add_account, list_accounts, get_account_info, set_default_account, remove_account, list_known_scopes |
Primary use case
Screen an inbox, draft replies for your approval, triage junk, route newsletters with rules, manage your calendar, and organize OneDrive, all by asking Claude in natural language.
Known personal-account limits
A handful of Graph families are work-only at the platform level and simply do
not exist for consumer accounts (Teams chat, SharePoint sites, org directory,
Planner, admin reports, and so on). The server tags those tools so they fail
fast with a readable message instead of a raw Graph 403. The durable list lives
in PERSONAL_ACCOUNT_LIMITS.md. Notable nuances:
- OneNote works on personal accounts but can return transient errors for a minute or two right after first consent. Retry rather than concluding it is unsupported.
- Subscriptions require a publicly reachable HTTPS
notificationUrl, so they need an ngrok-style tunnel to be useful from a purely local server. Personal accounts support subscriptions on mail, calendar, drive root, and To Do tasks, but not on contacts. - Workbook / Excel API is de-scoped on personal accounts in favor of the
separate
excel-mcp-server.
Tier 2: Business (work / school) accounts
Sign in with a Microsoft Entra (Azure AD) work or school account and you keep all 173 personal tools, then add 157 collaboration tools that only exist inside an organization. These target Teams, SharePoint, Microsoft 365 Groups, Planner, the org directory, shared mailboxes, Bookings, and the Excel Workbook API.
| Family | Tools | What it covers |
|---|---|---|
| Teams chat | 17 | List/create chats, send/edit/delete/restore messages, reactions, members, pins, tenant-wide message search |
| Teams channels | 16 | Channels (standard/private/shared), messages + threaded replies, reactions, members, files folder |
| Teams (teams themselves) | 10 | List/get/update teams, archive/unarchive, clone, members, promote owners |
| Online meetings | 9 | Create/update/delete Teams meetings, attendance reports, recordings, transcripts (WebVTT) |
| SharePoint sites | 7 | Root site, site search, subsites, modern pages, permission entries |
| SharePoint document libraries | 14 | Browse/upload/download/copy/move, share links, invitations, search across libraries |
| SharePoint lists + items | 11 | Lists, items, fields, columns (the row-oriented data primitive: trackers, registers, lookups) |
| Microsoft 365 Groups | 15 | Groups, members, owners, group mailbox conversations + threads, the backing team |
| Planner | 15 | Plans, buckets, tasks, task details, assignments (etag-aware) |
| Directory + people | 14 | User search, manager/reports hierarchy, member-of, free/busy, People API, org contacts |
| Shared mailboxes | 10 | Read/search/draft/send on a shared mailbox you have delegate access to |
| Bookings | 9 | Booking businesses, appointments, services, staff, publish |
| Workbook / Excel | 10 | Worksheets, ranges, tables, named ranges, persistent sessions |
What this enables
A work account turns the server into a full collaboration assistant: post to a Teams channel, search every chat you are in, manage a SharePoint document library or list, run a Planner board, look up a colleague's manager or free/busy, or draft from a shared support mailbox.
Delegated, not impersonating. Every action runs as you, with your permissions. Shared-mailbox tools require that you already hold Exchange Online FullAccess or folder rights on that mailbox; the Graph scopes alone do not grant the underlying data access.
Tier 3: Tenant administrators
If your work account holds admin roles, the server exposes 111 additional governance, security, and device-management tools. These read and write tenant-wide configuration, so several are flagged as sensitive and should only be invoked with explicit intent.
| Family | Tools | What it covers |
|---|---|---|
| Admin queries | 10 | Service health + incidents, Message Center posts, directory audit log, sign-in logs, provisioning logs |
| Reports | 15 | Usage reports across Office 365, email, Teams, OneDrive, SharePoint, Outlook, apps, Yammer (rolling D7/D30/D90/D180 windows) |
| Identity governance | 12 | Access reviews + decisions, entitlement-management access packages, Terms of Use, lifecycle workflows |
| Conditional Access + roles | 17 | CA policies (create/update/delete ⚠), named locations, auth-strength, authorization policy, directory role assignments, PIM activate |
| Directory objects | 20 | Devices, applications, service principals, administrative units, deleted-item recycle bin (restore ⚠ / purge ⚠) |
| eDiscovery + sensitivity labels | 13 | eDiscovery cases, custodians (legal hold ⚠), KQL searches, review sets, sensitivity labels |
| Security | 9 | Alerts, incidents, Secure Score history + controls, threat indicators |
| Intune | 10 | Managed devices (wipe ⚠ / retire ⚠ / sync), configuration + compliance policies, app policies, mobile apps |
| Universal Print | 5 | Printers, printer jobs, your jobs, print shares |
⚠ sensitive operations. Tools that change tenant-wide security policy, place data on legal hold, or wipe a device are marked with a warning in
CLAUDE.md. Confirm intent before invoking. Note that Graph exposes device writes only to app-only auth, soupdate_device/delete_devicecannot run through this delegated public-client flow (device read works fine; Intunewipe/retireuse a different, supported path).
The full, per-tool inventory for every tier lives in
CLAUDE.md; architecture and design notes are in
As_Built_Specification.md.
Setup overview
Setup is three moving parts, done once per identity:
- Register an Azure app and grant it Graph permissions (this section).
- Point the server at the app via
.env. - Generate a token with the
add_accounttool (the Authentication section).
You register one app per Microsoft identity:
| Path | You have | Apps to register |
|---|---|---|
| A | A personal account only | 1 app in the consumer (Microsoft-account) directory |
| B | A work account only | 1 single-tenant app in that work tenant |
| C | Both, or several work tenants | 1 app per identity; they coexist in one server |
Microsoft only allows one personal account per person, so the personal app uses
the un-suffixed .env keys. Each work tenant adds its own suffixed pair.
Prerequisites
- Node.js 18+ (uses native
fetch). - A Microsoft account (personal, or work/school in any Entra tenant).
- For work apps: tenant-admin consent, either yours or your admin's.
- macOS, Windows, or Linux. The first sign-in opens your default browser briefly.
Account setup and permission changes (Azure portal)
Do this in the Microsoft Entra admin center, entra.microsoft.com. Sign in as the identity you want to connect. For a work account, confirm the tenant shown in the top-right is the correct one before you start.
Step 1: Register the app
Applications → App registrations → + New registration.
| Field | Personal-account app | Work-account app |
|---|---|---|
| Name | m365-mcp-personal |
m365-mcp-<tenant> |
| Supported account types | "Personal Microsoft accounts only" | "Accounts in this organizational directory only (single tenant)" |
| Redirect URI | Platform = Public client/native (mobile & desktop), URI = http://localhost:3333/auth/callback |
same |
Click Register.
Use single-tenant for work apps. Register a separate app per work tenant rather than one multitenant app, unless you specifically need cross-tenant sign-in.
Step 2: Copy the identifiers
On the app's Overview page, copy:
| Field | Goes into .env as |
Notes |
|---|---|---|
| Application (client) ID | AZURE_CLIENT_ID (or AZURE_CLIENT_ID_<TENANT>) |
A GUID |
| Directory (tenant) ID | AZURE_TENANT_ID (or AZURE_TENANT_ID_<TENANT>) |
The tenant GUID for work apps. For personal apps, use the literal string consumers |
Step 3: Add API permissions
API permissions → + Add a permission → Microsoft Graph →
Delegated permissions. These are the 14 base (Tier 1) scopes every
account needs:
| Scope | Enables |
|---|---|
User.Read, User.ReadWrite |
Profile, photo, sign-in discovery |
Mail.Read, Mail.ReadWrite, Mail.Send |
All mail tools |
MailboxSettings.Read, MailboxSettings.ReadWrite |
Auto-reply, working hours, time zone, inbox rules |
Calendars.Read, Calendars.ReadWrite |
All calendar tools |
Contacts.Read, Contacts.ReadWrite |
All contact tools |
Tasks.ReadWrite |
Microsoft To Do |
Notes.ReadWrite |
OneNote |
Files.ReadWrite |
OneDrive |
Work and admin accounts add the work-only scope set on top (roughly 90 scopes spanning Teams, SharePoint, Groups, Planner, directory, reports, identity governance, Conditional Access, security, Intune, Print, and Bookings). Rather than transcribe all of them here:
- Run the
list_known_scopestool to print the exact base + work-only list theadd_accountflow will request, or - Read the authoritative source:
DEFAULT_SCOPESandWORK_ONLY_SCOPESinsrc/auth/multi-account-auth.ts.
Add every scope your tier needs, then click Add permissions.
Step 4: Grant admin consent (work accounts only)
On the API permissions page, click
"Grant admin consent for <tenant>". The admin-only .All scopes
require this. If you are not an admin, send your tenant admin this URL
(substitute your client and tenant IDs):
https://login.microsoftonline.com/<tenant-id>/adminconsent?client_id=<client-id>&redirect_uri=http://localhost:3333/auth/callback
After a one-time approval, users in the tenant sign in without per-user consent
prompts on the .All scopes. Personal accounts skip this step; you consent
in-browser during add_account.
Step 5: Confirm authentication settings
Authentication:
- Platform configurations = "Mobile and desktop applications" with redirect
URI
http://localhost:3333/auth/callback. - Advanced settings → Allow public client flows = Yes. If it is No, switch it and Save.
There is no client secret. This is a public-client / loopback-redirect app by design; adding a secret is unnecessary and unsupported by the flow.
Install and configure
git clone https://github.com/Corvalon/mcp-m365.git
cd mcp-m365
npm install
cp .env.example .env
npm run build
Edit .env. The server auto-loads it from the repo root on startup. Always
single-quote every value (Node's env parser treats an unquoted # as a
mid-line comment and will silently truncate a value).
# --- Personal account (Path A) ---
AZURE_CLIENT_ID='<personal-app-client-id>'
AZURE_TENANT_ID='consumers'
# --- Work account(s) (Path B/C), one suffixed pair per tenant ---
# The suffix is a short name you choose; it must match the `tenant`
# argument you pass to add_account.
AZURE_CLIENT_ID_CONTOSO='<contoso-app-client-id>'
AZURE_TENANT_ID_CONTOSO='<contoso-tenant-guid>'
A personal pair and any number of work pairs can all live in the same .env.
Wire into Claude Code
Add the server to your MCP config (~/.claude.json, or via the Claude Code IDE
settings):
{
"mcpServers": {
"m365": {
"command": "node",
"args": ["/absolute/path/to/mcp-m365/dist/index.js"]
}
}
}
Restart Claude Code. The server logs a startup line confirming
441 tools registered, and the tools become available as mcp__m365__*. The
.env is read from the repo root automatically, so no extra env wiring is
needed.
Authentication / token generation
Tokens are generated at runtime through the MCP tools, not by a separate
script. The first time you connect an identity you run add_account, which
opens a browser for OAuth consent and caches the result locally.
Generate your first token
Ask Claude to run add_account, or call it directly:
Personal account:
// mcp__m365__add_account
{ "alias": "personal", "accountType": "personal" }
Work account (the tenant value must match your .env suffix, lowercase):
// mcp__m365__add_account
{ "alias": "contoso", "accountType": "work", "tenant": "contoso" }
What happens:
- The tool reads the matching client/tenant IDs from
.envand builds the consent URL (base scopes for personal, base + work-only for work). - Your default browser opens. Sign in and approve. The call blocks for up to 2 minutes waiting for you to finish.
- A local listener on
http://localhost:3333/auth/callbackcatches the redirect and exchanges the code for tokens. - An audience guard verifies you signed in with the expected identity. If
you accidentally pick the wrong account in the browser, the account is
rejected and not persisted; just re-run
add_account. - The access + refresh tokens are written to a per-alias cache under
.token-cache/, and the account metadata toaccounts.json. The first account you add becomes the default.
After this, MSAL refreshes tokens silently. You only return to the browser when
a refresh fails or you widen scopes. On failure the server raises a structured
REAUTH_REQUIRED <alias> error naming exactly which account to re-add.
Routing calls to an account
Every Graph tool accepts an optional account alias. Omit it to use the
default.
{ "account": "contoso", "top": 10 } // read_inbox against the work account
{ "top": 10 } // read_inbox against the default account
Manage accounts
| Tool | Purpose |
|---|---|
list_accounts |
Show every registered account and which is default |
get_account_info |
Full record for one alias (client/tenant/authority, scopes, timestamps) |
set_default_account |
Choose the alias used when a call omits account |
remove_account |
Remove an account and delete its token cache |
list_known_scopes |
Preview the scopes add_account will request |
Re-authenticate or widen scopes
- Re-auth one account:
remove_account <alias>thenadd_accountagain. - Widen scopes: add the broader scope in the Azure portal, then re-run
add_accountwith the same alias and a widerscopesarray (MSAL merges the consent). - Fully revoke:
remove_accountdeletes the local cache but does not revoke the Microsoft-side grant. To revoke server-side, remove the app at account.microsoft.com/consent/manage (personal) or via the tenant admin (work).
Security model
- No client secret. Public-client, loopback-redirect, delegated auth only.
- Local-only tokens.
.env,accounts.json, and.token-cache/are all gitignored and never leave your machine. - Delegated permissions. Every action runs as the signed-in user with that user's rights. The server cannot exceed what the account can already do.
- Tier enforcement. Work-only tools carry a
compatibility="work"flag and are rejected before the handler runs when targeted at a personal account (and vice versa), for both explicit-alias and default-account calls. - Redaction.
homeAccountIdis truncated to its first 8 characters in surfaces an operator might log (list_accounts,get_account_info).
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
AADSTS50194 / multitenant error signing in with a work account |
App audience wrong, or .env has AZURE_TENANT_ID=consumers for a work app |
Use the tenant GUID for work; consumers only for personal |
AADSTS65001: not consented |
Missing admin consent for a .All scope |
Grant admin consent on the API permissions page, or send the admin-consent URL |
Audience mismatch from add_account |
You picked the wrong identity in the browser popup | Re-run add_account and choose the correct account |
| Browser opens but the callback hangs | Something else is on port 3333 | Free the port (lsof -i :3333); the redirect port is fixed at 3333 |
REAUTH_REQUIRED <alias> |
Refresh token expired or scopes changed | Run add_account again for that alias |
403 on Teams/SharePoint/Groups/admin tools |
Called against a personal account | Those are work-only; see PERSONAL_ACCOUNT_LIMITS.md |
OneNote returns Transient error ... {0} right after consent |
Known OneNote warm-up flakiness | Retry; it is not a real failure |
Development
npm run build # tsc -> dist/
npm run dev # tsx src/index.ts (no build step)
npm test # Vitest unit suite
npm run test:coverage # with coverage
Tools are grouped into per-family src/tools/*.register.ts modules and wired up
in src/index.ts. Auth lives in src/auth/ (multi-account-auth.ts holds the
scope catalog and MSAL layer; graph-client.ts loads .env and exposes the
default client). All Graph calls funnel through one account-aware choke point,
so tools stay account-agnostic.
Reference docs
| File | Purpose |
|---|---|
CLAUDE.md |
Full per-tool inventory + project conventions |
As_Built_Specification.md |
Architecture, modules, auth model, scope catalog |
RUNBOOK.md |
Operational procedures (re-auth, build, scope changes) |
PERSONAL_ACCOUNT_LIMITS.md |
Graph families that do not work on personal accounts |
License
MIT. See LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。
mcp-server-qdrant
这个仓库展示了如何为向量搜索引擎 Qdrant 创建一个 MCP (Managed Control Plane) 服务器的示例。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。