Skip to main content
Glama

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

Mail

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.


Related MCP server: Outlook Assistant

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, so update_device / delete_device cannot run through this delegated public-client flow (device read works fine; Intune wipe/retire use 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:

  1. Register an Azure app and grant it Graph permissions (this section).

  2. Point the server at the app via .env.

  3. Generate a token with the add_account tool (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

ApplicationsApp 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 permissionMicrosoft GraphDelegated 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_scopes tool to print the exact base + work-only list the add_account flow will request, or

  • Read the authoritative source: DEFAULT_SCOPES and WORK_ONLY_SCOPES in src/auth/multi-account-auth.ts.

Add every scope your tier needs, then click Add permissions.

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:

  1. The tool reads the matching client/tenant IDs from .env and builds the consent URL (base scopes for personal, base + work-only for work).

  2. Your default browser opens. Sign in and approve. The call blocks for up to 2 minutes waiting for you to finish.

  3. A local listener on http://localhost:3333/auth/callback catches the redirect and exchanges the code for tokens.

  4. 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.

  5. The access + refresh tokens are written to a per-alias cache under .token-cache/, and the account metadata to accounts.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> then add_account again.

  • Widen scopes: add the broader scope in the Azure portal, then re-run add_account with the same alias and a wider scopes array (MSAL merges the consent).

  • Fully revoke: remove_account deletes 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. homeAccountId is 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.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Corvalon/mcp-m365'

If you have feedback or need assistance with the MCP directory API, please join our Discord server