May 12, 2026 9 min read
MCP for Microsoft 365: reading Entra ID and audit logs through a protocol that does not exist yet
Microsoft does not ship an MCP server for M365 or Entra ID. We built a bridge that turns Microsoft Graph API calls into MCP tools, so Claude can reason about user licenses, sign-in logs, and conditional access policies in real time. Why this is harder than it looks, and what is coming next.
Microsoft ships APIs for almost every Microsoft 365 and Entra ID surface. You can read user licenses, audit sign-ins, list conditional access policies, and query service health through Microsoft Graph. What Microsoft does not ship is an MCP server that exposes those APIs as tools that Claude, Cursor, or any other MCP client can discover and call.
We built the bridge. Cloud Horizon's MCP server translates Microsoft Graph API calls into MCP tools, so Claude can reason about your Microsoft 365 tenant in real time. The architecture is different from AWS and GCP because Microsoft authentication is different. Here is how it works.
Why Microsoft is harder
AWS and GCP use long-lived access keys. Microsoft uses OAuth 2.0 with short-lived access tokens. A service account key for AWS lasts until you rotate it. A Microsoft Graph token expires in 1 hour. The MCP server must handle token refresh transparently or the tools stop working mid-conversation.
The second problem is permission models. AWS IAM and GCP IAM
are straightforward: you create a role, attach a policy, done.
Microsoft Graph permissions require admin consent in Entra ID.
If the admin has not consented to AuditLog.Read.All,
the sign-in log tool returns a 403 even if the user has the right
role assignments.
The third problem is data volume. A 10,000-user tenant generates millions of sign-in events per month. The Graph API paginates at 1,000 events per request. Claude cannot reason about millions of rows. The MCP server filters and aggregates before returning anything.
Architecture: the Microsoft bridge
The MCP server stores three secrets in Cloudflare:
MS_TENANT_ID— the Entra ID tenant IDMS_CLIENT_ID— the app registration client IDMS_CLIENT_SECRET— the client secret for token acquisition
On each tool call, the Function requests an access token from
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
using client credentials grant. The token is cached in a Durable
Object for 45 minutes. The Graph API call uses the token, and
the result is returned to the MCP client.
The permission model
We recommend an app registration with exactly these Microsoft Graph application permissions:
User.Read.All— list users and their license assignmentsDirectory.Read.All— read organization details and groupsAuditLog.Read.All— read sign-in and audit logsPolicy.Read.All— read conditional access policiesServiceHealth.Read.All— read service health incidents
No Directory.ReadWrite.All. No
User.ReadWrite.All. The app cannot create users,
change policies, or delete groups. Everything is read-only.
After creating the app registration, an Entra ID admin must grant admin consent in the Azure Portal. Without consent, every Graph call returns 403 regardless of the client credentials.
The tool surface
| Tool | What it returns |
|---|---|
ms_entra_list_users | Users, roles, license assignments, last sign-in times |
ms_entra_get_signins | Sign-in events filtered by user, app, or risk level |
ms_entra_list_policies | Conditional access policies, rules, and assignments |
ms_service_health | Active incidents, advisories, and planned maintenance |
ms_license_summary | Assigned vs available licenses by SKU |
What Claude can ask
- "Which users have not signed in for 30 days but still have E5 licenses?"
- "Show me failed sign-ins from outside the EU in the last 24 hours"
- "Which conditional access policies do not require MFA?"
- "How many M365 E3 licenses are unassigned?"
- "Is there any active service incident affecting Exchange Online?"
Claude translates each question into one or more Graph API calls, aggregates the results, and presents a structured answer. The human can then ask follow-up questions without writing KQL or PowerShell.
Demo mode: no tenant required
Without credentials, the MCP server returns realistic demo data: 500 users, 3 conditional access policies, 10,000 sign-in events, and 200 E5 licenses. The structure matches the real Graph API responses. When you connect your tenant, the same queries return live data.
Performance: handling scale
The sign-in log tool defaults to the last 24 hours with a maximum of 1,000 events. For larger queries, the tool aggregates by application, location, and status code before returning. The user gets a summary, not a raw event dump.
Token refresh is automatic. The cached token is refreshed 10 minutes before expiry. The MCP client never sees a 401.
What is next
Exchange Online mailbox audit. SharePoint site inventory. Teams usage analytics. Intune device compliance. Secure Score recommendations. Defender alerts. Every Microsoft 365 surface that exposes a read API through Graph belongs in the MCP server eventually.
The server is open source at cloudevolvers/cloudsight. The MCP config lives at /.well-known/mcp.json.