mcp-demo-aad-viz
mcp-demo-aad-viz
Рабочий пример двух возможностей MCP, которые обычно демонстрируют по отдельности, а вместе они становятся гораздо интереснее:
Авторизация Microsoft Entra ID (Azure AD) — сервер является ресурсным сервером OAuth 2.1. Членство в ваших группах Entra определяет, какие наборы данных для вас существуют. Не «перечислены, а потом закрыты» — их просто нет.
Встроенные приложения / расширения (
io.modelcontextprotocol/ui) — графики приходят в виде интерактивного виджета, отображаемого прямо в беседе, а его изменение стоит ноль токенов.
Вместе они образуют то, на что стоит посмотреть: конструктор графиков Altair, в выпадающем списке наборов данных которого находятся ровно те наборы, которые разрешают ваши группы Entra, — проверка выполняется на стороне сервера при каждом взаимодействии с виджетом.
Сделано против спецификации MCP 2026-07-28 с Python SDK mcp 2.0. Разворачивается в Azure Container Apps. Лицензия MIT.
Внимание: это демонстрация, а не продукт. В репозитории десять публичных демонстрационных наборов данных и намеренно простая модель уровней, чтобы сценарий авторизации был понятен.

Попробуйте без Azure
Без тенанта, без авторизации, без деплоя — достаточно, чтобы увидеть работу виджета:
uv sync && uv run python scripts/fetch_datasets.pyMCP_DATAVIZ_AUTH_ENABLED=false MCP_DATAVIZ_PORT=3001 uv run python -m mcp_datavizВ этом режиме каждый вызывающий считается обладателем всех трёх уровней наборов данных. Подключите любой хост MCP Apps к адресу http://localhost:3001/mcp — см. Локальная разработка для браузерного хоста, который показывает весь протокол ui/ в реальном времени.
Related MCP server: Vela MCP Server
Попробуйте с Azure
# 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 v1Скрипт выводит ваш MCP-endpoint. Добавьте его в клиент точно в том виде, как он выведен — путь /mcp является частью идентификатора ресурса OAuth → docs/CONNECT.md.
Используйте уникальный
--tagпри каждом деплое. При повторном теге шаблон Bicep становится байт-в-байт идентичен текущему, новая ревизия не создаётся, и деплой сообщает об успехе, хотя ничего не выкатывает.
Что здесь демонстрируется
Возможность MCP | Где это видно | Что вы видите |
Авторизация (OAuth 2.1 RS) | Членство в группе меняет размер каталога | |
MCP Apps ( | Выпадающие списки перерисовывают график на месте | |
Инструменты только для приложений ( |
| Перерисовка виджета стоит ноль токенов |
|
| Хосты без виджетов получают форму вместо виджета |
Повышение объёма прав ( |
| Первый экспорт запускает повторное подтверждение прав |
Ресурсы и шаблоны |
| Отфильтровано по правам доступа |
Автодополнение | аргументы набора данных | Автодополнение никогда не называет недоступный вам набор данных |
Промпты |
| Управляемый первый вход |
Две возможности, которые спецификация в этой ревизии объявила устаревшими и которых этот сервер поэтому избегает: sampling и capability logging (SEP-2577). Вместо запросов к модели suggest_chart выбирает маркер графика на основе типов столбцов.
Модель авторизации
Две независимые оси. Их путаница — обычная ошибка.
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)Уровень | Роль | Наборы данных |
open |
| iris, penguins, cars, barley |
operations |
| seattle-weather, us-employment, gapminder |
confidential |
| diмаmonds, movies, titanic |
Инженеры и учёные владеют одинаковым количеством наборов данных, но не одними и теми же наборами, поэтому два коллеги с одним и тем же вопросом получают два разных ответа.
GXP1 += "GXP7"
--now также назначит роли непосредственно пользователю: изменение группы в Entra может занимать несколько минут, прежде чем попадёет в новый токен, а прямое назначение — около двадцати секунд.
Роли — это жёсткий отказ. Вы не можете запросить себе путь в группу. Наборы данных за пределами вашего уровня отсутствуют в результатах tools/list, опепуляциях завершения и выпадающем списке виджета — это происходит не после «показано, потом отказано», а они просто не доступны.
Scope — мягкий отказ. Если не достаёт Datasets.Export, сервер отвечает 403 со WWW-Authenticate: Bearer error="insufficient_scope", и клиент авторизовается заново, запросавая этот scope.
У Entra есть две ловушки, которые этот репозиторий обходит: у него нет динамической регистрации клита и нет endpoint метаданных RFC 8414, а URL должен быть обязательно зарегистрирован как URI идентификатора приложения иначе resource= по RFC 8707 упадёт с AADSTS9010010.
Подробнее: docs/AUTHZ.md · docs/CONNECT.md.
Почему виджет интересен
Спецификация Vega-Lite с встроенными данными занимает 30–300 КБ. Если просто отдавить её из инструмента, она окажется в контексте модели на каждом графике.
Вместо этого:
plot_datasetвозвращает дескриптор ~900 байт — кодировка, число строк, предупреждения. Без спецификации.Хост отображает приложение
ui://и передаёт ему дескриптор.Виджет вызывает
render_chart(инструмент только для приложений) для получения уже конкретной спецификации.
Поскольку шаг 3 выполняется приложением, а не моделью, спецификация никогда не попадает в диалог. Изменение выпадающего списка — это лишь один небольшой цикл запроса к серверу и ноль токенов.
История с авторизацией сохраняется и здесь: render_chart и app_catalogue заново определяют уровень доступа на каждый вызов, поэтому виджет не может обратиться к набору, который не разрешён токеном, — даже когда модель уже не участвует в цикле.
Заметки по дизайну: docs/DESIGN.md.
Локальная разработка
Два стенда, для двух разных задач.
«Корректны ли HTML/JS моего виджета?» — миниатюрный хост, который говорит на реальном протоколе ui/ postMessage и протоколирует каждое событие, без участия MCP-клиента:
uv run python scripts/preview_widget.py # http://127.0.0.1:8765Он внедряет ту же строгую CSP, что и настоящий хост, поэтому сбои CSP воспроизводятся здесь, а не только в проде. --strip-structured-content эмулирует дефект хоста, описанный в docs/HOST-COMPATIBILITY.md.
«Корректна ли моя MCP-поверхность?» — эталонный хост из репозитория MCP Apps, который управляет сервером напрямую по HTTP:
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:8080Это тот каркас, который стоит брать рамонем, когда виджет выглядит пустым: он сообщает о нарушениях протокола, которые боевые хосты молча проглатывают. Запуск сервера с переменной MCP_DATAVIZ_AUTH_ENABLED=false также ослабид, повышает ограничение Origin в SDK и добавляет CORS-заголовки, необходимыми браузерному хосту; эти механизмы отключены всегда, когда включена авторизация.
Обратите внимание: HTML-файл виджета читается один раз при создании сервера, поэтому его правка требует перезапуска сервера.
Совместимость с хостами
Поддержка MCP Apps отличается от хоста к хосту и приводит к одинаковым по внешнему виду симптомам — обычно это пустой или свёрнутый виджет без каких-либо ошибок. docs/HOST-COMPATIBILITY.md документирует, что наблюдалось в реальности, как была выявлена каждая причина, и какие из них можно исправить на стороне сервера (одна из трёх), а какие нельзя.
Наборы данных
Четыре открытых, три операции, три конфиденциальных — все это публичные демонстрационные наборы из коллекции Vega. Они зашиты в образ при сборке, поэтому работающий контейнер не требует сетевого доступа к источнику данных. Идентификаторы уровней являются наглядными и выбраны для того, чтобы модель прав доступа была конкретной.
open | operations | confident (пояснительная причина) |
|
|
|
|
|
|
|
|
|
|
Структура репозитория
```bash
uv sync && uv run python scripts/fetch_datasets.py
Wait, GXP10. Correction: "GXP10".
Пакет Python сохраняет имя `mcp-datatool` даже если репозиторий называется `mcp-demo-aad-viz`; переименование привело бы к массовой перетасовке всех имён ресурсов Azure и переменных окружения без какой-либо пользы.
## Секции
```bash
uv run pytest # 168 tests, no Azure neededuv run ruff check src tests scriptstests/test_http.py запускает настоящий uvicorn-сервер и проверяет 401 challenge, PRM-документ, 403 insufficient_scope step-up и цикл input_required.
Стоимость
Azure Container Apps масштабируются до нуля (minReplicas: 0), поэтому неиспользуемое демо практически ничего не не стоит; единственные постоянные статьи расходов — ACR Basic и Log Analytics (несколько € / месяц).
az group delete --name rg-mcp-dataviz --yes && ./scripts/entra-teardown.shЛицензия
MIT.
This server cannot be deployed
Maintenance
Related MCP Connectors
- BasedashOAuthcom.basedash
Governed BI MCP. Ask questions of live company data and list workspace sources via OAuth.
Build multi-tenant apps over MCP. Schemas, CRUD, deploys — access control enforced server-side.
Query your warehouse or a CSV with Claude/ChatGPT over MCP, governed by table-level ACL + audit.
Governed data discovery, exact queries, decisions, simulations, and runtime utilities over MCP.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to interact with Databricks Genie Spaces through MCP tools. Provides secure OAuth-based access to query and interact with Databricks data catalogs and schemas via custom Genie interfaces.-
- AlicenseNot gradedqualityDmaintenanceEnables governed, agent-agnostic data exploration by allowing users to ask natural language questions through MCP-compatible agents, executing safe, permission-scoped queries against data sources and returning interactive charts.18 npmApache 2.0
- AlicenseAqualityBmaintenanceInteractive visualization of Microsoft Entra ID identity relationships, enabling exploration of org charts, groups, attributes, and access assignments through a D3 force-directed graph within MCP clients.860 npmMIT
- FlicenseNot gradedqualityBmaintenanceProvides MCP tools to query Tableau Server/Cloud datasources via REST API and VizQL Data Service, with support for Gemini or OpenAI as the LLM backend. Enables a natural language chat interface that can be embedded in Tableau dashboards, automatically including dashboard filter context.-