On this page
Authenticate an MCP server with Microsoft Entra ID (Azure AD) managed identity
When your agent runs in Azure, a managed identity gets it a Microsoft Entra ID (formerly Azure AD) access token for your MCP server, with no secret in configuration and nothing to rotate.
- Method
- Microsoft Entra ID
- Platform
- Azure
- MCP specification
- 2026-07-28
How it works
The agent asks Azure for an access token whose audience is the MCP server's Application ID URI, then sends it as Authorization: Bearer ACCESS_TOKEN. The Azure Identity libraries take that audience as a scope: the Application ID URI with /.default appended, one resource per request. The server verifies the signature against Entra's JWKS and checks aud, iss, and tid before reading the calling workload from oid, or from appid on a v1.0 token and azp on a v2.0 one. Which token version you get is decided by the API's own app registration, so settle that before hard-coding an aud comparison.
- Best for
- Agents and MCP servers that both run in Azure, in one Entra tenant
- Specification
- Outside the specification's OAuth flow, which the specification makes OPTIONAL
- Works with
- Your own workloads, plus Microsoft Foundry Agent Service; no chat or desktop client can mint one
- Effort
- A day, mostly the app registration and role assignments
The exchange
- Your agent VM, App Service, Functions
- Entra ID via the local endpoint
- MCP server validates the JWT
-
Whatever you pass as resource is also what appears in the token's aud claim. You rarely write this request yourself: the Azure Identity libraries take the same audience as scope=api://…/.default and use whichever local token endpoint the host provides. On a VM that is IMDS, which requires the Metadata: true header.
Your agent to Entra ID
GET …/identity/oauth2/token resource=api://mcp.example.com -
Entra ID to Your agent
access_token (carries the credential) aud = api://mcp.example.com -
Your agent to MCP server
POST /mcp (carries the credential) Authorization: Bearer ACCESS_TOKEN -
Validation is offline against cached JWKS, so the server never calls Entra. The tid check is what rejects a token from another tenant, and the aud check is what stops a token minted for a different Azure resource being replayed here.
Inside MCP server
Verify the signature against Entra's JWKS -
Inside MCP server
Check aud, iss, and tid, then read the caller appid (v1.0) or azp (v2.0) = 00001111-aaaa-2222-bbbb-3333cccc4444 -
MCP server to Your agent
200 OK tool result
Connect your agent
Pick your language and SDK.
When to use something else
Use this when
- The agent and the MCP server both run in Azure under one Entra tenant, so the platform issues the identity and your server only has to verify it.
- You want no stored secret: on Azure a managed identity is the client-credentials grant with the credential taken out.
- Tool calls must name a specific workload: the
oidclaim, or the calling app asappidon a v1.0 token andazpon a v2.0 one, rather than a key five services share.
Use something else when
- An MCP client you don't control has to connect. The configuration surfaces in Claude, ChatGPT, Cursor, and VS Code take static headers or run the specification's OAuth flow; none of them can acquire an Azure token.
- You need a client to onboard itself. Microsoft's own guidance is that many providers don't support Dynamic Client Registration, 'including Microsoft Entra ID', so you pre-register or preauthorize every client by hand.
- The agent calls on behalf of a named person: a managed identity token is app-only, so there is no user in it to authorize or to audit.
The specification makes authorization OPTIONAL and says HTTP transports SHOULD, rather than MUST, conform to its OAuth flow, so an Entra ID token presented as a bearer credential sits outside the specification rather than in conflict with it. That paragraph is word for word the same on the 2025-11-25 and 2026-07-28 authorization pages.
A managed identity answers one question, and only for workloads in your own tenant: is this workload allowed in. What comes next is about what it can do once it is in, and about callers Entra cannot vouch for at all.
- Step 1.
Add the second door
Managed identity stays exactly as it is for workloads inside your tenant. The gateway is a second route, for the callers with no managed identity — a partner, a chat client, someone else's agent.
"url": "https://
api. example. com/ mcp" - Step 2.
Add the policies
A list on the route, and the options that go with it. No SDK, no middleware, nothing imported into your server.
"inbound": [ "mcp-entra-oauth-inbound", "require-user-claims-inbound", "mcp-capability-filter-inbound", "rate-limit-inbound" ]
- Step 3.
Deploy
Your in-tenant workloads keep their managed identity and their
audcheck. Nothing on that path changes.zuplo deploy
-
Legal wants these tools in Claude with our Entra sign-in. Is that a rewrite?
-
mcp-entra-oauth-inboundNo — sign-in is a policy on the route, and Zuplo ships one for Entra ID. Your server code doesn't change and the Entra token never reaches the MCP client. One MCP OAuth policy per project.
-
The token is valid but this app role is not. Where does that check go?
-
require-user-claims-inboundOn the route. Allow or deny on any claim the token carries —
roles,tid, a group, the calling app — and the caller gets a403before your server runs a line of code. -
Can the reporting agent see fewer tools than the admin one?
-
mcp-capability-filter-inboundYes, on one route: tag each tool with the roles or groups allowed to use it, matched against the claims Entra put in the token. A tool the caller can't see is refused with
MethodNotFoundbefore your server hears about it. -
One agent looped overnight and burned the quota on the API behind it.
-
rate-limit-inboundCap it at the route, per identity: every tool call is counted against the workload that made it, not the egress IP, so one loop exhausts its own budget rather than the API's.
All of these attach to one MCP route's policies.inbound — the same policy engine on the way in and on the way out. Your MCP server keeps
the code and the authentication it has today.
Common questions
Managed identity or service principal — which one for MCP?
The same token either way; managed identity is the version with no credential to store. Use a service principal with a certificate or a federated credential only where a managed identity is unavailable, which in practice means outside Azure.
Why scope=api://…/.default rather than the resource parameter?
Two endpoints, two spellings of one value. The v2.0 token endpoint takes the resource identifier — the Application ID URI — in the scope parameter with .default appended, and rejects the older parameter: AADSTS901002 reports that the 'resource' request parameter isn't supported. The local managed-identity endpoint is the exception and still takes resource=. Either way, that value is what appears in the token's aud claim, and every scope in one request must belong to a single resource.
Should the audience be the Application ID URI or the server's URL?
You ask for the Application ID URI you registered, character for character. What the server then validates depends on the token version, which the API's own app registration decides through requestedAccessTokenVersion. Leave it null or 1 and aud is that same Application ID URI, so compare the string. Set it to 2 and Entra puts the API's client ID — a GUID — there instead, and a server still comparing against api://… refuses every token it receives. Ask for an audience Entra doesn't know and the request fails with AADSTS500011: the resource principal named {name} wasn't found in the tenant named {tenant}. A malformed one fails earlier with AADSTS70011, the provided value for the input parameter 'scope' isn't valid.
Does Entra ID support the MCP authorization flow?
Only in part, and Microsoft says so plainly: some providers support Dynamic Client Registration, 'but many don't, including Microsoft Entra ID', so you preconfigure each client with a client ID and, preferably, preauthorize it. Discovery is thinner than it looks. Entra publishes OpenID Connect Discovery at /{tenant}/v2.0/.well-known/openid-configuration, the third endpoint a conformant client probes, because both higher-priority well-known forms return 404. That document omits code_challenge_methods_supported, and the specification says its absence must make a client refuse to proceed.
Can Claude, ChatGPT, or Cursor connect to a server protected this way?
No. Their configuration surfaces accept static headers or the specification's OAuth flow, and none of them can acquire an Azure token. The one hosted exception is Microsoft Foundry Agent Service, which authenticates to an MCP server with either the agent identity or the project's managed identity.
Can the tool forward the caller's token to Microsoft Graph?
No. The specification is explicit — the MCP server MUST NOT pass through the token it received from the MCP client — and Microsoft says the same about its own platform: pass-through scenarios 'create security vulnerabilities', and its guidance is to obtain a new token through the on-behalf-of flow. Foundry enforces it for managed OAuth: send a Microsoft-audience token to a custom MCP endpoint and Agent Service returns 'Cannot pass Microsoft token to untrusted MCP endpoint.'
Is the Azure Functions MCP extension using Entra ID?
No — it gates the endpoint with a shared secret. Hosted in Azure, the extension requires the mcp_extension system key on /runtime/webhooks/mcp: without it in the x-functions-key header or the code query string, the client gets a 401. To drop that requirement, set system.webhookAuthorizationLevel to Anonymous in host.json. Either way, you can layer App Service's built-in MCP server authorization on top as an identity-based access control layer.
Did the 2026-07-28 specification release change anything for this method?
No. Authorization is still OPTIONAL and HTTP transports still only SHOULD conform — that paragraph is unchanged between the two revisions.
One policy engine for APIs, AI, and MCP
Put your MCP servers behind a gateway that speaks every identity provider, filters tools per role, and logs every call.