Server Microsoft365
FreeNot checkedEnables identity provisioning and management for Microsoft 365/Entra ID via Microsoft Graph, including user creation, license assignment, group membership manag
About
Enables identity provisioning and management for Microsoft 365/Entra ID via Microsoft Graph, including user creation, license assignment, group membership management, and more, with a focus on least-privilege and idempotency.
README
TypeScript MCP server for Microsoft 365 / Entra ID via Microsoft Graph. It runs app-only (client-credentials) and is intentionally boring good: typed, documented, read-first, least-privilege, credential-sane, and idempotent where Graph allows it. Its focus is identity provisioning — create a user, assign a license, manage group membership — plus the supporting reads you need to do that safely.
Disclaimer: This is an independent, unofficial project by Borgels. Borgels is not affiliated with, endorsed by, or supported by Microsoft. "Microsoft 365", "Microsoft Graph", and "Entra ID" are referenced only to describe what this server talks to. You need your own Entra ID app registration, and use of Microsoft Graph is subject to Microsoft's own terms.
Scope
Read tools (enabled by default):
get_user,list_userslist_subscribed_skus(license lookup and seat availability)list_groups,get_group,list_group_membersget_user_license_details
Write tools (disabled by default):
create_userupdate_user(PATCH attributes on an existing user, incl.accountEnabledto enable/disable — e.g. activate a pre-created hire or disable a leaver)set_managerassign_license,remove_licenseadd_group_member,remove_group_memberset_usage_locationcreate_temporary_access_pass(passwordless first sign-in / MFA setup; regenerated until alphanumeric),delete_temporary_access_passactivate_pim_role(just-in-time PIM self-activation of an eligible directory role, e.g. Authentication Administrator for TAP — delegated mode only)
Authentication modes
The server authenticates to Graph in one of three ways, chosen by which options are set (never as tool arguments):
- App-only (client credentials) —
MS_TENANT_ID+MS_CLIENT_ID+MS_CLIENT_SECRET. Acts as the app; requires the application permissions below. - Delegated (on-behalf-of a user) — a
refreshTokenoption /MS_REFRESH_TOKEN(plus the app'sMS_CLIENT_ID/MS_CLIENT_SECRET/MS_TENANT_ID). The server mints short-lived delegated access tokens via therefresh_tokengrant, so calls act as the consenting user and are bounded by that user's own roles. Entra rotates the refresh token on use; the latest is exposed on the client (latestRefreshToken) so the host can persist it.activate_pim_roleonly works in this mode. - Static token — a pre-fetched
MS_ACCESS_TOKEN/accessToken(short-circuits the grants).
Least-privilege application permissions
Grant the app registration only what it needs, then admin-consent:
| Task | Application permission |
|---|---|
| Read users and groups | User.Read.All |
| Create users | User.Create (or User.ReadWrite.All) |
| Update users / set manager / enable-disable | User.ReadWrite.All |
| Read license/SKU state | LicenseAssignment.Read.All |
| Assign / remove licenses | LicenseAssignment.ReadWrite.All |
| Manage group membership | GroupMember.ReadWrite.All |
| Create / delete Temporary Access Pass | UserAuthenticationMethod.ReadWrite.All |
JIT PIM role activation (activate_pim_role, delegated) |
delegated RoleManagement.ReadWrite.Directory (bounded by the user's eligible roles) |
User.Create is narrower than User.ReadWrite.All; use it when you only need to
provision new users. update_user and set_manager require User.ReadWrite.All.
Reading SKUs (/subscribedSkus) is covered by LicenseAssignment.Read.All.
Temporary Access Pass additionally requires the app to hold the Authentication
Administrator (or Privileged Authentication Administrator) directory role —
UserAuthenticationMethod.ReadWrite.All alone is not sufficient to manage another
user's authentication methods. Assign the role to the app's service principal in
Entra ID → Roles and administrators, and ensure the tenant's TAP policy is
enabled (it also caps lifetimeInMinutes).
App registration and admin consent
- In the Entra admin center, register an application (single tenant).
- Under Certificates & secrets, create a client secret and copy its value.
- Under API permissions, add the Microsoft Graph Application permissions above, then click Grant admin consent.
- Copy the Directory (tenant) ID and Application (client) ID.
Setup
Install dependencies and build the CLI:
npm install
npm run build
Provide credentials through the environment. The server never accepts them as tool arguments.
export MS_TENANT_ID="contoso.onmicrosoft.com" # or the tenant GUID
export MS_CLIENT_ID="your-application-client-id"
export MS_CLIENT_SECRET="your-client-secret"
Optional settings:
export MS_GRAPH_BASE_URL="https://graph.microsoft.com/v1.0"
export MS_TIMEOUT_MS=30000
# export MS_AUTHORITY_HOST="https://login.microsoftonline.com"
If another process manages tokens, provide a bearer token directly:
export MS_ACCESS_TOKEN="your-access-token"
Claude or Cursor config
Use the stdio server for local MCP clients:
{
"mcpServers": {
"microsoft365": {
"command": "node",
"args": ["/absolute/path/to/mcp-server-microsoft365/dist/transports/stdio.js"],
"env": {
"MS_TENANT_ID": "contoso.onmicrosoft.com",
"MS_CLIENT_ID": "your-application-client-id",
"MS_CLIENT_SECRET": "your-client-secret"
}
}
}
}
During development:
{
"mcpServers": {
"microsoft365": {
"command": "npm",
"args": ["run", "dev", "--prefix", "/absolute/path/to/mcp-server-microsoft365"],
"env": {
"MS_TENANT_ID": "contoso.onmicrosoft.com",
"MS_CLIENT_ID": "your-application-client-id",
"MS_CLIENT_SECRET": "your-client-secret"
}
}
}
}
Streamable HTTP transport
npm run dev:http
The HTTP transport binds to 127.0.0.1 by default and serves POST /mcp. Set
MCP_HTTP_TOKEN to require bearer auth, and MCP_ALLOWED_ORIGINS for
browser-based local clients. MCP_MAX_BODY_BYTES defaults to 10485760
(10 MiB).
Provisioning workflow
list_subscribed_skusto see which licenses exist and how many seats are free.create_userwith adisplayNameanduserPrincipalName. A strong temporary password is generated when you do not supply one, and returned in the result withforceChangePasswordNextSignInset. Store it securely and share it out-of-band.create_userfails clearly if the UPN already exists.assign_licensewith a friendly name (E3), askuPartNumber(ENTERPRISEPACK), or a SKU GUID. PassusageLocationon the same call for a brand-new user.add_group_memberto place the user in the right groups. It is idempotent: an already-a-member response is treated as success.
The usageLocation gotcha
Microsoft Graph rejects a license assignment for a user with no usageLocation
(the classic "License assignment failed because of an invalid usage location"
error). This server sets usageLocation first:
- If you pass
usageLocationtoassign_license, it isPATCHed onto the user before the license is assigned. - If you do not, the server reads the user's current
usageLocationand returns a clear, actionable error when it is missing — rather than letting Graph fail cryptically.
usageLocation is an ISO 3166-1 alpha-2 country code, e.g. DK.
License resolution
assign_license / remove_license accept a friendly name, a skuPartNumber,
or a SKU GUID. The server fetches /subscribedSkus (no $filter; it matches
client-side) and:
- matches a GUID directly against
skuId; - otherwise matches
skuPartNumbercase-insensitively; - otherwise maps a friendly name (e.g.
E3→ENTERPRISEPACK/SPE_E3,E5→ENTERPRISEPREMIUM/SPE_E5) to the first candidate the tenant owns; - checks availability (
prepaidUnits.enabled - consumedUnits) and refuses to assign when no seats are free.
Rate limiting
On HTTP 429, the raised GraphHttpError carries the Retry-After value and the
Graph request-id. The Borgels connector layer uses these to retry.
Borgels gateway
The ./gateway subpath exports the Borgels gateway contract:
import { createMicrosoft365Gateway, microsoft365GatewayTools } from 'mcp-server-microsoft365/gateway';
const gateway = createMicrosoft365Gateway({ tenantId, clientId, clientSecret });
const result = await gateway.callTool('list_subscribed_skus');
microsoft365GatewayTools lists the same tool surface as the MCP server, with
riskLevel and enabledByDefault so the gateway can gate writes. Read tools are
enabledByDefault: true; every write tool is enabledByDefault: false.
Live smoke test
The normal test suite uses mocked fetch. With real credentials in the
environment you can run a read-only live smoke test that counts subscribed SKUs:
npm run smoke:live
Security and audit
- Credentials are read from environment variables only.
MS_GRAPH_BASE_URLmust behttps://; loopbackhttp://is allowed for local mocks.- Formatted errors redact Authorization headers, client secrets, access tokens, and token-like material.
- Write tools are disabled by default and surfaced with write/destructive annotations.
- The Streamable HTTP transport binds to
127.0.0.1by default.
Security reports: [email protected].
Install Server Microsoft365 in Claude Desktop, Claude Code & Cursor
unyly install mcp-server-microsoft365Installs into Claude Desktop, Claude Code, Cursor & VS Code — handles npx, uvx and build-from-source repos for you.
First time? Get the CLI: curl -fsSL https://unyly.org/install | sh
Or configure manually
Run in your terminal:
claude mcp add mcp-server-microsoft365 -- npx -y github:borgels/mcp-server-microsoft365FAQ
Is Server Microsoft365 MCP free?
Yes, Server Microsoft365 MCP is free — one-click install via Unyly at no cost.
Does Server Microsoft365 need an API key?
No, Server Microsoft365 runs without API keys or environment variables.
Is Server Microsoft365 hosted or self-hosted?
A hosted option is available: Unyly runs the server in the cloud, no local setup required.
How do I install Server Microsoft365 in Claude Desktop, Claude Code or Cursor?
Open Server Microsoft365 on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
GitHub
PRs, issues, code search, CI status
by GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
by mcpdotdirectCompare Server Microsoft365 with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All development MCPs
