mcp-demo-aad-viz
mcp-demo-aad-viz
Ein ausgearbeitetes Beispiel für zwei MCP-Fähigkeiten, die üblicherweise isoliert gezeigt werden und zusammen deutlich interessanter sind:
Microsoft Entra ID (Azure AD)-Autorisierung – der Server ist ein OAuth-2.1-Ressourcenserver. Ihre Entra-Gruppenmitgliedschaft entscheidet, welche Datasets für Sie existieren. Nicht „aufgelistet, dann verweigert“ – sondern schlicht nicht vorhanden.
Inline-Apps / Erweiterungen (
io.modelcontextprotocol/ui) – Diagramme treffen als interaktives Widget ein, das innerhalb der Konversation gerendert wird, und ihre Anpassung kostet null Tokens.
Beides zusammen ergibt das Sehenswürdige: einen Altair-Diagramm-Builder, dessen Dropdown genau die Datasets enthält, die Ihre Entra-Gruppen erlauben – serverseitig durchgesetzt bei jeder einzelnen Interaktion mit dem Widget.
Erstellt mit MCP 2026-07-28 und dem Python-SDK mcp 2.0. Läuft auf Azure Container Apps. MIT-lizenziert.
Hinweis: Dies ist eine Demonstration, kein Produkt. Sie bringt zehn öffentliche Beispieldatensätze und ein bewusst simples Stufenmodell mit, damit die Autorisierungs-Logik gut nachvollziehbar ist.

Ohne Azure ausprobieren
Kein Mandant, keine Authentifizierung, kein Deployment – genug, um das Widget in Bewegung zu sehen:
uv sync && uv run python scripts/fetch_datasets.pyMCP_DATAVIZ_AUTH_ENABLED=false MCP_DATAVIZ_PORT=3001 uv run python -m mcp_datavizJeder Aufrufer wird dann so behandelt, als hätte er Zugriff auf alle drei Dataset-Stufen. Richten Sie einen beliebigen MCP-Apps-Host auf http://localhost:3001/mcp aus – für einen browserbasierten Host, der das komplette ui/-Protokoll live anzeigt, siehe Lokale Entwicklung.
Mit Azure ausprobieren
# 1. Directory objects (app registration, scopes, app roles, 3 groups)
./scripts/entra-setup.sh# 2. Put yourself in a group to pick a persona
source entra.env
az ad group member add --group "$MCP_DATAVIZ_GROUP_ANALYSTS_ID" \
--member-id "$(az ad signed-in-user show --query id -o tsv)"# 3. Deploy (builds the image in Azure; no local Docker needed)
./scripts/deploy.sh --tag v1Das Skript gibt Ihren MCP-Endpunkt aus. Fügen Sie ihn exakt so in Ihren Client ein – der /mcp-Pfad ist Teil der OAuth-Ressourcenangabe → docs/CONNECT.md.
Verwenden Sie bei jedem Deployment einen eindeutigen
--tag. Bei einem wiederholten Tag ist das Bicep-Template byte-identisch mit dem bereits laufenden, es wird keine neue Revision erstellt, und das Deployment meldet Erfolg, während nichts ausgeliefert wird.
Was es zeigt
MCP-Funktion | Wo | Was Sie sehen |
Autorisierung (OAuth-2.1-Ressourceserver) | Gruppenmitgliedschaft ändert die Größe des Katalogs | |
MCP-Apps ( | Dropdowns rendern das Diagramm direkt im Diagramm neu | |
Nur-App-Tools ( |
| Das Neu-Rendern des Widgets kostet null Tokens |
|
| Hosts ohne Widgets erhalten stattdessen ein Formular |
Scope-Hochstufung ( |
| Der erste Export verant eine erneute Zustimmung |
Ressourcen + Vorlagen |
| Nach Berechtigungen gefiltert |
Vervollständingungen | Dataset-Argumente | Die Autovervollhält nennt nie ein Dataset, das Sie nicht öffnen dürfen |
Prompts |
| Geführte erste Erkundung |
Zwei Dinge, die die Spezifikation in dieser Revision abgelehnt hat und die dieser Server deshalb vermeidet: sampling und die logging-Fähigkeit (SEP-2577). suggest_chart wählt einen Marker anhand der Spaltentypen statt nach einem Zeichnen.
Das Autorisierungsmodell
Zwei unabhängige Achsen. Das übliche Mincheingang.
WHO YOU ARE WHAT YOU'RE DOING
Entra group ──► app role ──► dataset tier OAuth scope ──► operation
(roles claim) (scp claim)
analysts → Open ( 4) Datasets.Read → everything
engineers → Open + Operations ( 7) Datasets.Export → export_chart
scientists → Open + Confidential ( 7) ↑ withheld at first, so the
...a *different* 7 first export triggers a step-up
(no group) → nothing ( 0)Stufe | Rolle | Datasets |
open |
| iris, penguins, cars, barley |
operations |
| seattle-weather, us-employment, gapminder |
confidential |
| diamonds, movies, titanic |
Ingenieure und Wissenschaftler halten jeweils die gleiche Anzahl an Datasets, aber nicht dieselben; zwei Kollegen, die dieselbe Frage stellen, erhalten also unterschiedliche Antworten.
./scripts/assign-persona.sh engineer colleague@example.com --now--now weist die Rollen zusätzlich direkt dem Benutzer zu: Eine Gruppenänderung kann bei Entra einige Minuten brauchung, bis sie ein neues Token beeinflusst, eine direkte Zuweisung hingegen etwa zwanzig Sekunden.
Rollen sind eine harte Verweigung. Sie können sich nicht in eine Gruppe hineinfragen. Datasets außerhalb Ihrer Stufe fehlen in den Ergebnissen von tools/list, resources, Vervollständigen und im Dropdown des Widgets – nicht aufgelistet und dann verweigert.
Scopes sind eine weiche Verweigerung. Fehlbare Datasets.Export ergibt 403 mit einer WWW-Authenticate: Bearer error="nefik"insufficient_scope"-Herausforderung, und der Client autorisiert sich erneut, um den Scope abzufragen.
Zwei Entra-spezifische Stolpersteine werden hier umgegangen, die beide verwirrende Fehler erzeugen, wenn die Konfiguration von Hand: Entra hat keine dynamische Client-Registrierung und keinen Metadaten-Endpunkt nach RFC 8414, und die MCP-URLs müssen als Application-ID-URI (Application-ID-URI) registriert sein, sonst schlägt die RFC-8707 resource=-Angabe mit AADSTS9010010 fehl.
Ausführliche Details: docs/AUTHZ.md · docs/CONNECT.md.
Warum das Widget interessant ist
Eine Vegar-Lite-Spezifikation mit Inline-Daten ist 30–300 KB groß. Wenn Sie sie naiv aus einem Tool zurück geben, landet sie bei jedem Diagramm im Kontext des Modells.
Stattdessen:
plot_datasetgibt einen ~900-Byte-Handle zurück – Codierung, Zeilenanzahlen, Warnungen. Kein Spezifikation.Der Host rendert die
ui://-App und übergibt ihm diesen Handle.Das Widget ruft
render_chart(ein Nur-App-Tool) für das eigentliche Spezifikation ab.
Weil Schritt 3 der Client-Typstered aus der App stammt und nicht vom Model, gelangt die Spezifikation nie in die Konversation. Das Ändern eines Dropdowns ist eine kleiner Zserver-Roundtrip und null Voltes.
Auch die Autorisierungsstory bleibt erhalten: render_chart und app_catalogue leiten die Stufen des Aufrufers bei jedem Aufruf neu ab, sodass das Widget kein Dataset erreichen kann, das das Token nicht erlaubt – selbst wenn sich das Modell nicht mehr in feedback-Schleife befindet.
Design-Notizen: docs/DESIGN.md.
Lokale Entwicklung
Zwei Testumgebungen für zwei unterschiedliche Fragen.
„Ist das HTML/JS meines Widget korrekt?“ – ein Miniatur-Host, der das reale ui/-postMessage-Protokoll spricht und jede Nachricht protokolliert, und ein MCP-client vorhanden:
uv run python scripts/preview_widget.py # http://127.0.0.1:8765Er setzt dieselbe strikte CSP ein, die auch ein echter Host anwendet, sodass sich CSP-Fehler hier reproduzieren lassen und nicht nur in Produktion auftreten. --strip-structured-content simuliert den einen Hostefekt, die in docs/HOST-COMPATIBILITY.md beschrieben wird.
„Ist meine MCP-Oberfläche korrekt?“ – der Referenz-Host im Modell-Apps-Repository, der den echten Server über HTTP ansteuert:
git clone https://github.com/modelcontextprotocol/ext-apps.git
cd ext-apps && npm install && cd examples/basic-host
SERVERS='["http://localhost:3001/mcp"]' npm start # http://localhost:8080Das ist die Testungen die mit zuerst wählt, wenn ein Widget leer bleibt: Sie meldet Protokollverletzungen, die echte Hosts still. Wenn Sie den Server sein, erlauben sie das Origin-Check des SDKs und ergänzt die CORS-Header, die ein browserbasierter Host braucht, und die ausgeschaltet sind, sobald die Autorisierung aktiv ist.
Beachten Sie: Die HTML des Widgets wird **einmal „beim Serverstart“ gelesen – wird Programmieren Sie das Widget, ist ein Neustart des Servers erforderlich.
Host-Kompatibilität
Die Support zwischen verschiedenen Hosts variiert und erzeugt zuverlässig identisch aussehende Symptome – in der Regel ein leeres oder eingeklapptes Widget ohne Hinweis auf einem Fehler. docs/HOST-COMPATIBILITY.md dokumentiert die – CTX, wie die einzelnen Ursachen isoliert wurden und welche serverseitig behebbar sind (eine von drei) und welche nicht.
Datasets
Vier öffentliche, drei operations, drei vertrauliche – alles öffentliche Beispieldatensätze aus der Vega-Datasets-Sammlung. Sie werden zur Build-Zeit in das Image eingebunden, sodass der laufende Container keinen Netzwerkzugriff auf Datensenger braucht. Die Stufennamen sind nur illustrativ und so gewählt, dass das Zugriffsmodell greifbar ist.
| minimal | vertraulich (illustrativer Grund) |
|
|
|
|
|
|
|
|
|
|
Layout
src/mcp_dataviz/
server.py tools, resources, prompts, completions
auth.py Entra token verification, roles→tiers, scope challenge
catalog.py the 10 datasets and the tier gate
charts.py Altair → Vega-Lite, with aggregation pushed into pandas
config.py environment settings (nothing hardcoded)
widgets/ the MCP App
infra/ Bicep: ACR, Container Apps, Log Analytics
scripts/ entra-setup.sh, deploy.sh, assign-persona.sh, preview_widget.py
tests/ 168 tests, incl. HTTP-level auth and step-up
docs/ AUTHZ, CONNECT, DESIGN, HOST-COMPATIBILITYDas Python-Paket behält den Namen mcp_dataviz (und das Präfix DevExperimente Umgebungsvariable) bei, auch wenn das Repository allow heißt- Demo` heißt; eine Umbenennung würde jeden Azure-Ressourcennamen und jede Umgebungsvariable anlaufen, ohne einen Vorteil zu bringen.
Tests
uv run pytest # 168 tests, no Azure neededuv run ruff check src tests scriptstests/test_http.py startet einen echten uvicorn Server und prüft den 401-Challenge, den PRM-Dokument, das 403 unsufficient_scope-Hochstufung und den input_required-Rundtrip.
Kosten
Container Apps skaliert auf null (minReplicas: 0), sodass eine ruhende Demo praktisch nichts kostet; ACR Basic und Log Analytics sind die einzigen stehenden Gebühren (einige Euro pro Monat).
az group delete --name rg-mcp-dataviz --yes && ./scripts/entra-teardown.shLizenz
MIT.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
MCP Hub: AI service discovery, per-user OAuth, and multi-service workflow orchestration
Official Microsoft MCP Server to query Microsoft Entra data using natural language
Manage SRG+ hubs, channels, content, assets, users, and workspaces from any MCP-aware AI agent.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/sinanpl/mcp-demo-aad-viz'
If you have feedback or need assistance with the MCP directory API, please join our Discord server