Connecting Codex to the Microsoft MCP Server for Enterprise
Microsoft recently shipped the Microsoft MCP Server for Enterprise, currently in preview: a hosted MCP server at https://mcp.svc.cloud.microsoft/enterprise that lets AI agents query your Entra tenant in natural language. Ask “how many guest users do we have?” and behind the scenes it translates that into Microsoft Graph calls, respecting your existing permissions the whole way.
The official docs walk you through connecting it to VS Code, Visual Studio, ChatGPT, and Claude. Conspicuously absent: OpenAI Codex. I ran through it myself in a dev tenant and got it working; you just have to do some Entra plumbing that the first-party clients get for free, and push through two scary-looking (but harmless) auth errors along the way.
Why Codex needs extra work
The MCP Server for Enterprise uses OAuth with delegated permissions only; every query runs as you, with your privileges. Clients like VS Code are pre-registered in Entra and get their permissions granted in one shot by Microsoft’s provisioning script.
Codex has no such registration. Worse, Codex’s default codex mcp login flow assumes the server supports Dynamic Client Registration (DCR), and Microsoft Entra doesn’t do DCR. The fix: register your own Entra application to act as Codex’s identity, then tell Codex to use that pre-registered client ID instead of trying to register one dynamically.
The plan:
- Register the service principal(s) in your tenant
- Create an app registration for Codex and grant it MCP scopes
- Add the server to Codex (and fix the redirect URI it complains about)
- Log in with explicit scopes and test
Prerequisites
- A Microsoft Entra tenant where you can register applications and grant admin consent (Application Administrator or Cloud Application Administrator at minimum)
- PowerShell with the Microsoft.Graph module
- A recent Codex CLI; the
--oauth-client-idflag is a newer addition, so checkcodex --version. If you normally live in the GUI, don’t worry: you only need the CLI for this one-time setup, and the config carries over. - No extra license or cost; you just need licenses for whatever data you query (e.g., Entra ID P2 for PIM data)
Step 1: Register the service principal(s)
The MCP Server is a Microsoft-owned multitenant app, so its service principal needs to exist in your tenant before anything can authenticate to it. The official docs do this with the Microsoft.Entra.Beta module’s Grant-EntraBetaMCPServerPermission, which also provisions and grants permissions to VS Code whether you want that or not. I went leaner and created the service principal directly:
# Install the required module if you don't have it
Install-Module Microsoft.Graph -Scope CurrentUser
# Connect with the necessary directory management scopes
Connect-MgGraph -Scopes "Application.ReadWrite.All", "Directory.AccessAsUser.All"
# Microsoft MCP Server for Enterprise: e8c77dc2-69b3-43f4-bc51-3213c9d915b4
if (-not (Get-MgServicePrincipal -Filter "AppId eq 'e8c77dc2-69b3-43f4-bc51-3213c9d915b4'")) {
New-MgServicePrincipal -AppId 'e8c77dc2-69b3-43f4-bc51-3213c9d915b4' -AccountEnabled
}
# Microsoft Graph (should already exist in most tenants): 00000003-0000-0000-c000-000000000000
if (-not (Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'")) {
New-MgServicePrincipal -AppId '00000003-0000-0000-c000-000000000000'
}
That app ID, e8c77dc2-69b3-43f4-bc51-3213c9d915b4, is the Microsoft MCP Server for Enterprise itself. It’s the same in every tenant, and we’ll need it again for the OAuth scope later.
Step 2: Create the app registration
This is the part the docs only cover generically under “custom MCP clients.” In the Entra admin center:
- Go to Entra > App registrations and click Add.
- Name:
Microsoft MCP Server for Enterprise - Codex(or whatever you want). - Supported account types: Single tenant only (only do multi-tenant if you know what you’re doing).
- Redirect URI: select Public client/native and enter
http://localhost; this is a placeholder we’ll replace shortly. - Click Register, then copy the Application (client) ID from the overview page. Mine was
21a9cc56-9c39-426b-983f-ca7b1a8772bc; yours will differ.
Now grant it permissions on the MCP Server:
- Go to API permissions > Add a permission, switch to APIs my organization uses, and scroll down to Microsoft MCP Server for Enterprise.
- Expand MCP and select the permission(s) you want. I used
MCP.User.Read.Allfor this example. - Click Grant admin consent.
The scopes follow the pattern MCP.{graph-scope-name}: User.Read.All becomes MCP.User.Read.All. There are ~40 of them covering users, groups, devices, audit logs, conditional access policies, and more; the full list is in the docs. Grant only what you’ll actually query. Everything is read-only by design; there are no write scopes.
Finally, still in the app registration, go to Authentication, enable Allow public client flows, and Save. You shouldn’t do this reflexively on app registrations; it lets the app authenticate without a client secret, which also unlocks flows like device code that you may not want on most apps. Here it’s appropriate because Codex is a CLI running on your machine: a native public client with nowhere safe to store a secret, so it authenticates via the authorization code flow with PKCE instead.
Step 3: Add the server to Codex
If you don’t have it already, install the Codex CLI. Add the server with your app registration’s client ID:
codex mcp add microsoft-enterprise \
--url https://mcp.svc.cloud.microsoft/enterprise \
--oauth-client-id 21a9cc56-9c39-426b-983f-ca7b1a8772bc
This kicks off an OAuth attempt, and here’s expected error #1, a redirect URI mismatch:
AADSTS50011: The redirect URI 'http://127.0.0.1:38087/callback/WgHZsR6lRfzy' specified in the
request does not match the redirect URIs configured for the application
'21a9cc56-9c39-426b-983f-ca7b1a8772bc'. Make sure the redirect URI sent in the request matches
one added to your application in the Azure portal.
This error is actually useful: it tells you the exact callback URI Codex generated for this server. Copy it, go back to the app registration’s Authentication section, and swap out the http://localhost placeholder for that exact URI.
Run the add command again. This time the browser shows “Authentication complete. You may close this window.” The terminal, however, hits expected error #2:
Error: failed to handle OAuth callback
Caused by:
OAuth token exchange failed: Server returned error response: invalid_target: AADSTS9010010:
The resource parameter provided in the request doesn't match with the requested scopes.
This is OK. Codex’s default login flow doesn’t request the right scopes for this server; we’ll pass them explicitly in the next step. The server config itself was saved. Confirm with:
codex mcp list
Name Url Bearer Token Env Var Status Auth
microsoft-enterprise https://mcp.svc.cloud.microsoft/enterprise - enabled Not logged in
Step 4: Log in with explicit scopes
Now we “fix” the login by specifying the scopes ourselves. The api://{mcp-server-appId}/.default scope requests everything your app registration was consented for:
codex mcp login --scopes openid,profile,email,offline_access,api://e8c77dc2-69b3-43f4-bc51-3213c9d915b4/.default microsoft-enterprise
You’ll see the same “Authentication complete” page in the browser, and this time the terminal agrees:
Successfully logged in to MCP server 'microsoft-enterprise'.
Remember, everything is delegated: sign in with an account whose directory privileges match what you plan to query. The MCP server can never see more than the signed-in user can.
Step 5: Test it
Fire up codex and ask something tenant-specific:
List the users in my Microsoft tenant.
Codex calls the server’s microsoft_graph_suggest_queries tool to find candidate Graph queries, picks one, executes it via microsoft_graph_get, and hands you a plain-English answer. You can watch the tool calls happen, which is half the fun; it also makes this a great way to discover Graph API calls you can lift into scripts.
Gotchas and notes
Two expected auth errors. The redirect URI mismatch (AADSTS50011) during add is how you learn the real callback URI, and the invalid_target (AADSTS9010010) after fixing it just means the default login didn’t request the right scopes. Neither means you broke something.
Preview product. The server, the endpoint, and the setup flow can all change. Codex’s --oauth-client-id support is also relatively new; update your CLI if the flag isn’t recognized.
Read-only, delegated-only. No app-only auth, no writes. This is a query surface for Entra identity data, not an automation backdoor.
Rate limits. 100 calls per minute per user, plus normal Graph throttling.
Auditing. Every request flows through Microsoft Graph, so if you enable Microsoft Graph activity logs, you get full visibility. Filter by the MCP server’s app ID:
MicrosoftGraphActivityLogs
| where TimeGenerated >= ago(30d)
| where AppId == "e8c77dc2-69b3-43f4-bc51-3213c9d915b4"
| project RequestId, TimeGenerated, UserId, RequestMethod, RequestUri, ResponseStatusCode
Kill switch. You can’t delete the Microsoft-owned service principal, but you can disable it:
PATCH https://graph.microsoft.com/v1.0/servicePrincipals(appId='e8c77dc2-69b3-43f4-bc51-3213c9d915b4')
{
"accountEnabled": false
}
Wrapping up
There are three tricks to know: pre-register an app because Entra won’t do dynamic client registration, steal the redirect URI from the first error message, and pass the scopes explicitly at login. Once you know them, hooking Codex up to the Enterprise MCP Server is quick. It’s a surprisingly pleasant way to poke at tenant data: no memorizing Graph endpoints, no Graph Explorer tab juggling, and the delegated permission model means it can’t do anything you couldn’t already do yourself.
Hopefully Microsoft adds first-class Codex instructions to the docs. Until then, this works.