exchange-online-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@exchange-online-mcpConvert alice@contoso.com to a shared mailbox and hide her from the GAL."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
exchange-online-mcp
Exchange Online MCP server — exposes the mailbox, distribution-group and mobile-device operations that Microsoft Graph cannot express, as MCP tools over Streamable HTTP.
What this is for
Microsoft 365 user offboarding needs a handful of actions that only Exchange owns:
Action | Cmdlet behind it | Why Graph cannot do it |
Read a mailbox's type, size, archive and hold state |
| Graph has no mailbox management object; usage is only available as a tenant-level report with up to 48h latency, unusable as a pre-action gate |
Convert a user mailbox to a shared mailbox |
| The Entra user object has no concept of mailbox type |
Hide a mailbox from the global address list |
| Graph's |
Remove a member from a distribution / mail-enabled security group |
| Graph rejects membership writes on these Exchange-owned objects |
List / remove a mailbox's mobile devices |
| Graph exposes Intune |
Use this server alongside a Microsoft Graph MCP server: user accounts, sign-in blocking, licences, Microsoft 365 groups and security groups stay on the Graph side.
No PowerShell runtime is involved. Every cmdlet runs through the same REST entry point the Exchange Online PowerShell module itself uses:
POST https://outlook.office365.com/adminapi/beta/{tenant}/InvokeCommand
{"CmdletInput": {"CmdletName": "Get-Mailbox", "Parameters": {"Identity": "alice@contoso.com"}}}Related MCP server: ms-teams-mcp
Tools
Tool | Semantics |
| read-only. Returns the mailbox, its statistics, and a |
| write, destructive. |
| write, idempotent, reversible |
| write, destructive. One named member per call — no bulk removal |
| read-only. |
| write, destructive. Removes the mailbox partnership only — it does not wipe data on the handset |
Cmdlet output is projected onto a field whitelist and the @odata.type companion keys
are stripped, so a typical response is under 1.5 KB instead of the ~40 KB a raw
Get-Mailbox returns.
Credentials (HTTP headers)
Exchange Online app-only access accepts certificates only — not client secrets — so the certificate itself is the credential and this server mints the access token per request. Credentials are read from headers only; there is no environment-variable fallback, and nothing is cached between requests.
Header | Required | Meaning | Where it comes from |
| Yes | Tenant GUID or default domain ( | Entra admin center → Overview |
| Yes | Application (client) ID of the registered app | Entra admin center → App registrations → your app → Overview |
| Yes | Base64 of the certificate and private key: either a PEM bundle or a PKCS#12 ( | Generated when you create the app credential (see below) |
| No | Password for the | — |
| No | Overrides the | — |
Missing any required header on /mcp returns 401 with the missing names listed.
Tenant setup
Entra admin center → App registrations → New registration (single tenant).
API permissions → Add a permission → APIs my organization uses → Office 365 Exchange Online → Application permissions →
Exchange.ManageAsApp→ Grant admin consent.Create the certificate and upload the public half to the app (Certificates & secrets → Certificates → Upload certificate):
openssl req -x509 -newkey rsa:2048 -sha256 -days 730 -nodes \ -keyout exo.key -out exo.crt -subj "/CN=mspbots-exo-mcp" openssl pkcs12 -export -out exo.pfx -inkey exo.key -in exo.crt # upload exo.crt base64 -w0 exo.pfx # X-Exo-CertificateA PEM bundle works just as well:
base64 -w0 <(cat exo.key exo.crt).Assign an Exchange role to the service principal — Entra admin center → Roles and administrators, or Exchange admin center → Roles → Admin roles.
Recipient Managementcovers every tool here;View-Only Organization Managementis enough for the two read-only tools. Without a role assignment every cmdlet returnsunauthorized.
Certificates expire: rotate them before notAfter and re-upload. One certificate per
tenant — this server never shares credentials across tenants.
Endpoints
Endpoint | Behaviour |
| MCP Streamable HTTP. Requires the credential headers |
|
|
Environment variables
Variable | Default | Purpose |
|
| Listening host |
|
| Listening port |
|
| Admin endpoint host; override for sovereign clouds (GCC High, DoD, 21Vianet) |
|
| Entra login host; same reason |
Unknown environment variables are ignored, never fatal. No credential ever comes from the environment.
Errors
Tools never raise; they return a JSON envelope as their string result:
{"error": {"code": "unauthorized", "message": "...", "retryable": false}}code is one of not_configured, unauthorized, not_found, invalid_argument,
rate_limited, upstream_error. Exchange reports a missing recipient or device as a
400 carrying a cmdlet exception; those are normalised to not_found. An empty list is
a successful empty result, never not_found.
Run it
docker compose up --build # or: docker build --platform linux/amd64 -t exchange-online-mcp:dev .
curl http://localhost:8080/healthuv sync
uv run python -m exo_mcpTests
uv run pytest # 50 unit tests, no network
docker build --platform linux/amd64 -t exchange-online-mcp:dev .
uv run python tests/mock_e2e/run_e2e.py # real container, mocked Entra + Exchangetests/mock_e2e/ runs the built image against a local stand-in for Entra ID and the
admin endpoint: it generates a throwaway certificate, verifies the client assertion the
container signs (RS256, x5t, audience), and drives
401 → initialize → tools/list → tools/call for all six tools. No customer tenant
required.
Delivery checklist (SOP §14 self-assessment)
Integration contract
POST /mcpStreamable HTTP;GET /healthreturns 200{"status":"ok"}, local-only probeDefaults to
0.0.0.0:8080Unknown environment variables ignored (
SettingsConfigDict(extra="ignore"))No session stickiness, no local persistent state
Credentials read from headers only — no env fallback, no credential fields in config
Header names follow
X-<Vendor>-<Credential>and match this README character for characterMissing headers → 401 listing them
Request-scoped isolation via
contextvars, reset infinally; no global credential stateNo cross-request caching of tenant data or derived tokens (one token exchange per tool call)
DNS-rebinding protection disabled
MCP app's lifespan mounted on the outer Starlette app
tools/callverified over real HTTP with headers (tests/mock_e2e/)
Errors and network
Structured error envelope, fixed code vocabulary, no exceptions raised
Empty results are empty, not
not_found; messages carry no credentialsOutbound timeouts (connect 5s / read 30s; token exchange read 15s)
Limited retry with backoff on 429/5xx honouring
Retry-After; worst case well under 120sOne shared connection pool, reused within a request
Container and deliverables
docker build --platform linux/amd64builds and runsMulti-stage build, production stage runs as non-root
appcurlinstalled in the production stageEXPOSE 8080, defaultMCP_HTTP_PORT/MCP_HTTP_HOST,HEALTHCHECKconfigureduv.lockcommitted, no private dependency sourcesCredential header table present (above)
Agent-facing design
6 tools, modelled on the offboarding flow rather than on endpoints
Every description ≤ 500 chars (longest 404), first line ≤ 100 chars
Service-level
instructionsprovided (1027 chars)Tools self-describing; required/optional parameters explicit
All tools prefixed
exo_exo_list_mobile_deviceshaslimit(default 50, hard cap 200) withhas_moreReturns capped at 20,000 chars, compact separators,
ensure_ascii=FalsereadOnlyHint/destructiveHint/idempotentHintset on every toolWrite tools justified by the offboarding SOP; single named resource only, no bulk deletes
Business flow run by an agent against a real tenant — pending a customer sandbox
Logging and sensitive information
No credentials, tokens or certificate material logged or echoed in error messages
No real credentials anywhere in the repo; certificates are generated per test run
.env/.venv/*.pem/*.pfx/*.keyexcluded in.gitignoreand.dockerignore
Known follow-ups for the first real tenant
Whether
X-AnchorMailboxis required for every cmdlet under app-only auth, and whether the tenant-GUID form (which yields no anchor) is sufficient.Whether
BypassSecurityGroupManagerCheckis needed for mail-enabled security groups in practice, or should become opt-in.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Manage Microsoft 365 email, calendar, contacts and inbox rules via the Graph API with OAuth 2.0.
Query, browse, and automate OmegaAI workspaces from any MCP client. Streamable HTTP with OAuth 2.0.
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
Hosted MCP server for inbox health, reporting, and warmup operations.
Related MCP Servers
- AlicenseAqualityBmaintenanceMCP server for Microsoft Outlook via Graph API. 20 consolidated tools for email, calendar, contacts, folders, rules, categories, and settings with safety controls (dry-run preview, rate limiting, recipient allowlists) and MCP annotations on every tool.221,03736MIT
- AlicenseBqualityCmaintenanceMCP server for Microsoft Teams that exposes 73 tools to manage teams, channels, chats, messages, meetings, planner, calendar, apps, tabs, scheduling, search, and authentication via the Graph API.7318MIT
- AlicenseNot gradedqualityBmaintenanceExposes Azure Entra ID user and license management as MCP tools over HTTP-SSE, enabling operations such as user creation, group assignment, and license management via Microsoft Graph API.Apache 2.0
- AlicenseAqualityCmaintenanceProduction-grade MCP server for Microsoft 365, providing tools to manage Email, Calendar, Contacts, OneDrive, Teams, Tasks, and Users via delegated OAuth.44601MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/MSPbotsAI/exchange_online_mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server