MCP OAuth mit Azure Entra ID und APIM — ein Realitätscheck
Ich wollte kürzlich eine bestehende REST API als MCP Tools über Azure API Managements neues MCP-Feature bereitstellen. Die Idee war simpel: Claude Code, VS Code Copilot und andere AI-Clients sollen unsere interne Datenplattform über das Model Context Protocol abfragen können — keine neuen Services, kein eigener MCP-Server, nur APIM als Übersetzer zwischen MCP und REST.
Der APIM-Teil hat funktioniert. Der Auth-Teil nicht.
Das hier ist eine Zusammenfassung meiner Erkenntnisse: welche Clients funktionieren, welche nicht, und was man heute (März 2026) dagegen tun kann.
Was ich gebaut habe
Unsere API läuft auf Azure Functions mit Hono. Sie ermöglicht SQL-Abfragen gegen interne Datenprodukte — quasi eine Natural-Language-to-SQL-Pipeline. Wir wollten eine Teilmenge dieser Endpoints (Query ausführen, Tabellen auflisten, gespeicherte Queries verwalten) als MCP Tools exponieren, damit Entwickler sie direkt aus ihren AI-Coding-Assistenten nutzen können.
Azure APIM hat dafür ein Preview-Feature: Man registriert eine API mit apiType: 'mcp', verweist auf die OpenAPI-Spec der bestehenden REST API, und APIM generiert automatisch MCP Tool Definitions. Wenn ein Client tools/call aufruft, übersetzt APIM den JSON-RPC-Request in einen HTTP-Call ans Backend. Das Backend sieht kein MCP — es verarbeitet ganz normale REST-Requests.
Das ist APIMs "Mode 1" (REST als MCP), im Gegensatz zu "Mode 2", wo APIM Traffic an ein Backend weiterleitet, das nativ MCP spricht. Mode 1 ist der interessante — keine Code-Änderungen an der API, nur Infrastruktur-Konfiguration.
Das OAuth-Problem
MCP unterstützt OAuth zur Authentifizierung. Unsere API ist durch Microsoft Entra ID (Azure AD) geschützt. Theoretisch sollte ein MCP-Client:
- Den Authorization Server via RFC 9728 (Protected Resource Metadata) entdecken
- Einen Token mit der richtigen Audience und den richtigen Scopes holen
- Diesen mit MCP-Requests senden
In der Praxis ist diese Kette an mehreren Stellen kaputt.
Claude Code: scope wird ignoriert
Das Kernproblem ist simpel: Claude Code sendet den scope-Parameter nicht, wenn es Tokens von Entra ID anfordert. Man kann "scope": "api://your-app-id/.default" in die .mcp.json-Konfiguration schreiben — Claude Code ignoriert es.
Ohne Scope weiß Entra ID nicht, für welche API der Token sein soll. Es fällt auf Microsoft Graph zurück. Man bekommt also einen Token mit der Audience https://graph.microsoft.com statt der eigenen API. Die APIM-Policy lehnt ihn ab, oder — falls noch keine Token-Validierung eingerichtet ist — das Backend bekommt einen Token, der nichts über die Berechtigungen des Nutzers für die App aussagt.
Ich habe das über die Entra ID Sign-in Logs bestätigt: resourceDisplayName: Microsoft Graph, resourceId: 00000003-0000-0000-c000-000000000000. Der Token zielt nie auf unsere API.
Die relevanten Issues:
| Issue | Beschreibung | Status |
|-------|-------------|--------|
| claude-code#4540 | Fehlender scope in DCR + Authorization Requests | Offen (seit Jul 2025) |
| claude-code#7744 | Ignoriert scopes_supported aus Protected Resource Metadata | Offen (seit Sep 2025) |
| claude-code#12077 | OAuth fehlender Scope für HTTP-Server | Geschlossen (Inaktivität, nicht behoben) |
Dynamic Client Registration: Nicht möglich mit Entra ID
Die MCP-Auth-Spec setzt stark auf Dynamic Client Registration (DCR) — der Client registriert sich on-the-fly beim Authorization Server. Entra ID unterstützt kein DCR. Es gibt keinen Weg, das zum Laufen zu bringen.
Claude Code versucht DCR selbst dann, wenn man eine explizite clientId in der Konfiguration angibt. Das bedeutet: Selbst wenn man eine App in Entra ID vorregistriert und Claude Code die Client-ID gibt, versucht es zuerst DCR und scheitert.
| Issue | Beschreibung | Status |
|-------|-------------|--------|
| claude-code#3273 | Server ohne DCR funktionieren nicht | Offen |
| claude-code#26675 | clientId in Config, erzwingt trotzdem DCR | Offen |
| claude-code#38102 | DCR trotz clientId (Duplikat, bestätigt aktuellen Stand) | Offen |
Das MCP TypeScript SDK hat dasselbe Problem
Es ist nicht nur Claude Code. Das MCP TypeScript SDK selbst übergibt keine Scopes beim Token Exchange:
| Issue | Beschreibung | Status | |-------|-------------|--------| | typescript-sdk#941 | Scope fehlt beim Token Exchange | Offen, P2, ready for work | | typescript-sdk#1669 | Fix-PR: Scope zum Token Exchange hinzufügen | Offen seit 12. März, nicht gemergt |
Dazu kommt eine Inkompatibilität auf Spec-Ebene: Entra ID v2 lehnt den resource-Parameter (RFC 8707) mit AADSTS901002 ab, aber die MCP-Spec verlangt ihn. Das wird in modelcontextprotocol#1614 getrackt.
Welche Clients funktionieren tatsächlich?
Ich habe jeden größeren MCP-Client gegen unseren APIM MCP Endpoint mit Entra ID OAuth getestet:
| Client | Funktioniert? | Anmerkungen |
|--------|--------------|-------------|
| VS Code (Copilot Agent Mode) | Ja | Eingebaute Entra Client-ID, implementiert RFC 9728. Kleiner Bug: vscode#261120 — /authorize-URL wird manchmal aus der Resource-Origin statt dem Authorization Server gebaut |
| ChatGPT | Ja | OAuth-Flow funktioniert korrekt |
| Claude Code CLI | Nein | Scope ignoriert, DCR erzwungen |
| claude.ai Web | Teilweise | Popup-Bugs nach Auth |
| GitHub Copilot CLI | Teilweise | OAuth unterstützt aber fehlerhaft: Auth-Prompt wird nicht getriggert (#1967), DCR-Port-Probleme (#1951), kein Silent Refresh (#1797) |
| Copilot Coding Agent (Cloud) | Nein | Unterstützt nur stdio MCP-Server, kein Remote/OAuth |
Die Ironie: VS Code Copilot und ChatGPT — nicht Claude — sind diejenigen, die sich tatsächlich gegen einen MCP-Server authentifizieren können, der durch Microsofts eigene Identitätsplattform geschützt ist.
APIM MCP Preview: Die rauen Kanten
Auch abgesehen von OAuth hat das APIM MCP-Feature eigene Probleme. Für den Basisfall funktioniert es, aber es gibt einige Fallstricke:
- Nur Tools — keine MCP Resources oder Prompts
- Nicht im Consumption-Tier verfügbar — mindestens Developer nötig
context.Productist null in MCP-Policies, also ist produktbasierte Zugriffskontrolle kaputtcontext.Response.Bodyin Policies hängt den Request — es bricht SSE-Streaming, das MCP nutzt/tools/listvalidiert Subscription Keys standardmäßig nicht- Backend-HTTP-Fehler (500) kommen als HTTP 200 zurück — der Fehler steckt im JSON-Body — der MCP-Client sieht eine "erfolgreiche" Antwort mit einer Fehlermeldung
- ARM-Deployment-Bug mit
operationId: Erneuter Import von OpenAPI-Specs mitoperationId-Feldern via ARM (Bicep) verursachtInternalServerErrorim Developer-Tier. Direkte REST-API-Calls funktionieren. Mein Workaround:operationIdaus den Specs entfernen, die in APIM importiert werden, während sie in der vollständigen Spec für Swagger UI erhalten bleiben.
Quelle für mehrere dieser Punkte: Azure APIM MCP: The Good, The Bad, The Ugly von itsrene, was sich mit meiner Erfahrung deckt.
Was heute tatsächlich funktioniert
Wer MCP mit Azure APIM heute nutzen will, hat folgende Optionen:
Option 1: Subscription Key (kein OAuth)
Der einfachste Weg. Keine User-Level-Identität, aber funktioniert mit jedem Client.
{
"mcpServers": {
"my-api": {
"type": "http",
"url": "https://your-apim.azure-api.net/mcp/your-api/mcp",
"headers": {
"Ocp-Apim-Subscription-Key": "<key>"
}
}
}
}Option 2: Manueller Bearer Token
Man bekommt User-Identität, aber der Token läuft ab (typischerweise 1 Stunde).
TOKEN=$(az account get-access-token \
--resource api://your-app-id \
--query accessToken -o tsv){
"mcpServers": {
"my-api": {
"type": "http",
"url": "https://your-apim.azure-api.net/mcp/your-api/mcp",
"headers": {
"Authorization": "Bearer ${MY_API_TOKEN}"
}
}
}
}Option 3: mcp-remote Proxy
Handhabt OAuth außerhalb des MCP-Clients. Siehe geelen/mcp-remote.
Option 4: DCR Proxy (mcp-gateway)
Fängt DCR-Requests ab und injiziert die richtigen Scopes. Siehe hyprmcp/mcp-gateway.
Wo wir stehen
Das MCP-Ökosystem bewegt sich schnell, aber OAuth mit Enterprise-Identity-Providern hat offensichtlich noch keine Priorität. Der Scope-Bug in Claude Code ist seit Juli 2025 offen. Der SDK-Fix liegt seit dem 12. März als PR vor. Der Spec-Level-Konflikt beim resource-Parameter mit Entra ID ist ungelöst.
Wer auf Azure baut und MCP mit ordentlicher Auth will, ist mit VS Code Copilot derzeit am besten bedient. Für alles andere: Subscription Key nutzen und akzeptieren, dass es keine User-Level-Identität gibt, bis die OAuth-Story ausgereift ist.
Ich lasse unseren APIM MCP Endpoint vorerst ohne Inbound-Token-Validierung deployed. Die vollständige Policy mit validate-azure-ad-token ist fertig und wartet — sie braucht nur Clients, die tatsächlich die richtigen Tokens produzieren können.
Alle Issues und Erkenntnisse sind auf dem Stand von März 2026. Die MCP-Auth-Spec ist noch im Draft, und Client-Implementierungen ändern sich aktiv. Die Dinge könnten sich schnell verbessern — oder auch nicht. Die verlinkten Issues für den aktuellen Stand prüfen.