Skip to main content
Glama
sinanpl

mcp-demo-aad-viz

by sinanpl

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.

Внимание: это демонстрация, а не продукт. В репозитории десять публичных демонстрационных наборов данных и намеренно простая модель уровней, чтобы сценарий авторизации был понятен.

Интерактивный виджет конструктора графиков Altair: элементы управления набором данных, осями и типом маркера рядом с точечной диаграммой набора данных Palmer penguins


Попробуйте без Azure

Без тенанта, без авторизации, без деплоя — достаточно, чтобы увидеть работу виджета:

uv sync && uv run python scripts/fetch_datasets.py
MCP_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)

auth.py

Членство в группе меняет размер каталога

MCP Apps (io.modelcontextprotocol/ui)

chart_builder.html

Выпадающие списки перерисовывают график на месте

Инструменты только для приложений (visibility: ["app"])

render_chart

Перерисовка виджета стоит ноль токенов

input_required

plot_dataset

Хосты без виджетов получают форму вместо виджета

Повышение объёма прав (403 insufficient_scope)

export_chart

Первый экспорт запускает повторное подтверждение прав

Ресурсы и шаблоны

data://catalog

Отфильтровано по правам доступа

Автодополнение

аргументы набора данных

Автодополнение никогда не называет недоступный вам набор данных

Промпты

explore_dataset

Управляемый первый вход

Две возможности, которые спецификация в этой ревизии объявила устаревшими и которых этот сервер поэтому избегает: 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

Datasets.Open

iris, penguins, cars, barley

operations

Datasets.Operations

seattle-weather, us-employment, gapminder

confidential

Datasets.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 КБ. Если просто отдавить её из инструмента, она окажется в контексте модели на каждом графике.

Вместо этого:

  1. plot_dataset возвращает дескриптор ~900 байт — кодировка, число строк, предупреждения. Без спецификации.

  2. Хост отображает приложение ui:// и передаёт ему дескриптор.

  3. Виджет вызывает 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 (пояснительная причина)

iris

sea-weather

diamonds — цены, используемые в разных регионах

penguins

us-employment

movies — доходы от продаж

cars

gapminder

titanic — персональные записи

barley


Структура репозитория

```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 needed
uv run ruff check src tests scripts

tests/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.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables 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.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables 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 npm
    Apache 2.0
  • A
    license
    A
    quality
    B
    maintenance
    Interactive 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.
    8
    60 npm
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Provides 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.
    -