Skip to main content
Glama
sinanpl

mcp-demo-aad-viz

by sinanpl

mcp-demo-aad-viz

通常は単独で紹介されることが多い2つのMCP機能を組み合わせた実用的な例です。組み合わせるとはるかに興味深いものになります。

  • Microsoft Entra ID (Azure AD) 認可 — サーバーはOAuth 2.1リソースサーバーです。Entraグループでのメンバーシップが、あなたに存在するデータセットを決定します。「一覧には出るが拒否される」のではなく、そもそも存在しないのです。

  • インラインアプリ / エクステンション (io.modelcontextprotocol/ui) — チャートは会話内でレンダリングされるインタラクティブなウィジェットとして届き、それを調整のコストはゼロトークンでsu.

この2つを組みわせた、見る価値のあるもができあがります。Entraグループが許したデータセットだけがドロップダウンに表示されされるAltairチャートビルダーを、ウィジェットとのやりとりのすべての操作でサーバ側が強制します。

MCP 20026-07-28 を対象に、Pyton SDK mcp 2.0 で構築。Azure Container Apps にデプロイします。MITラรู้センスです。

注記: これは実演です。製品ではありません。認可のストーリーが伝わりやすいように、公開サンプルデータセット10件と、意図的にシンプルなティアモデルを同梱しています。

インタラクティブなAltairチャートビルダーウィジェット。パーマーペンギンデータセットの散布図の横に、データセット・軸・マークのコントロールを表示している


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

すべての呼び出しが3つのデータセットテイアLevelを保有するものとして扱われます。任意の 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エンドポイントを出力します。表示されたとおりにそのままクライアントに追加してください — /mcp パスはOAuthリソース識別子の一部です → docs/CONNECT.md。

デプロイのたびに固有の --tag を渡してください。同じタグを繰り返すと Bicep テンプレートが現在配備中のものとバイト単位で同一になり、新しいレビジョンが作られず、何も導入されないのにデプロイが成功と表示されます。


デモししていること

MCP 機能

場所

見えるもの

Authorization (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

ガイド付きの最初のパス

このリビジョンで仕様が非推奨にした2つのこと、つまり sampling と logging ケーパビリティ (SEP-2577) は、このサーバーでは避けています。suggest_chart はモデルに聞くのではなく、カラムの型からマークを選びます。


ルールのモデル

独立した2つの軸です。この2つを混同するのがよくあるミスです。

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

diamonds, movies, titanic

エンジニアとサイエンティストは等しい*数のデータセットを持ってしていますが、そのデータは同じではありません。だから、二人の同僚が同じ質問しても、別の答えになる.

./scripts/assign-persona.sh engineer colleague@example.com --now

--now はロールをユーザーに直接割り当てます。グループの変更が新しいトークンに反映されるまでEntraでは数分かかる場合がありますが、直接割り当ては約20秒ですでいます。

ルールはハード拒否です。自分をグループに入れるように要求することはできません。自分のティア外のデータセットは tools/list の結果、resources/list、補完候補、ウィジェットのドロップダウンから存在しません — ありますが拒否されるのではありません。

スコープはソフト拒否です。Datasets.Export が欠けている場合、403 に WWW-Authenticate: Bearer error="insufficient_scope" のチャレンジを返し、クライアントはそれを求めて再認可します。

このリポジトリが回避しているEntra特有の問題が2つあります。どちらも手動で設定すると不可解なエラーになります。Entra には ダイナミッククライアント登録 がなく、RFC 8414 のメタデータエンドポイントも存在しません。また、MCP URL は 必ず Application ID URI として登録しなければならず、そうしないとRFC 8707 の resource= が AADSTS9010010 で失敗します。

詳細は docs/AUTHZ.md と docs/CONNECT.md を参照してください。


ウィジェットが面白い理由

インンデータを持つ Vega-Lite スペックは30〜300KBです。これを単純にツールから返すと、チャートのたびにモデルのコンテキストに入ってしまいます。

代わりに:

  1. plot_dataset は約900バイトのハンドルを返します。エンコーディング、行数、警告のみです。スペックはありません。

  2. ホストは ui:// アプリをレンダリングし、そのハンドルを渡します。

  3. ウィジェットは render_chart (アプリ専用ツール) を呼び、実際のスペックを取得します。

ステップ3はモデルではなくアプリから発生するため、スペックが会話に入ることは決してありません。ドロップダウンを変えるには、サーバーへの小さなラウンドトリップ1回でトークンは0です。

ルールのストーもここでも成立します。render_chart と app_catalog があるのは、呼び出しが行われるたびに呼び出し元のティアを再導出するからです。モデルがもう登場しなくても、ウィジェットはトークンで許可されていないデータセットに到達することができません。

設計メモ: docs/DESIGN.md。


ローカル開発

「ウィジェットのHTML / JSはでいるか?」と「MCPサーフェスは足りてあるか?」の、2つの異なする質問に対する2つのテスト・ベッドです。

「ウィジェットのHTML/JSは正しい?」 — リアルな ui/ postMessage プロトコルを話す小型ホスト、すべてのメッセージをロギング、MCPクライアントは介しません:

uv run python scripts/preview_widget.py     # http://127.0.0.1:8765

実際のホストが適用するのと同じ制限的CSPを注入するので、CSPフェイラがそこでも再現します。制作だけではありません。--strip-structured-content は、doc/HOST-COMPATIBILITY.md に記さたホストの欠陥を模倣します。

「MCPのサーフェスは正しい?」 — MCP Appsリポジトリのレフアレ?ンス・ホストで実サーバーをHTTTPを介して動かすというもの。

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 でサーバーを起動すると、SDKの Origin チェックを緩和してCORSヘッダーを追加します。ブラウザベースのホストにはこれが必用で、認証がオンの間はオフになりします。

ウィジェットHTMLはサーバー構築時に一度だけ読み込ます。したがて編集時はサーバの再起動が必要です。


ホスト互換性

MCP Appsのサポート具合いはホストごとに異なり、見た目は同一の症状になります — 通常はどこエラーもした空白や折り畳んだウィジェットです。**docs/HOST-COMPATIBILITY.md は、実観察、各原因の切分け方た - サーバサー側で修正可なもの(3うちの1つ)と否定なものの合計を文書化している。

データセット

オープン4つ、オペレーション3つ、.internal 3つ — すべてVega dataset collectionの公開サンプルす。ビルド・タイムにイメージ内にベンダルため、実行中コンテナがデータのオリジンへのネトワークアセスを必要としません。テイアラベは認可セスモデルを具体的にするために用選た標示であり、実際の意味はありま。

オープン

オペレ一如ンズ

コンフイデんシャル (理由は察例)

iris

seattle-weather

diamonds — ユニット価格

ペンギン

us-employee

movies — コマーシャル収益

cars

gap姆イ 걸음

pite — 個ルレコ戸


レイアウト

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-COMPATIBILITY

Pythonパッケージは、リポジトリがmcp-demo-aad-vizであっても、mcp_dataviz (とMCP_ 環境プレフィックス) の名前を保っています。リネームするとAzureのすべてのリソース名と環境変数を無益に無意味に変えてしまうからです。

テスト

uv run pytest          # 168 tests, no Azure needed
uv run ruff check src tests scripts

tess/test_http.py は本物のuvironサーバーを起動して、401チャレンギ、PRM文書、403 insufficient_scopeのステップアップ、input_required のラウンドトップを検証します。

コスと

Container Appsはゼロにスケールので (min`Replicas: 0) 待機デモのコスとはほぼナシ。ACR基本とログ・アナリテイクスのみ定常課金オ月額ユーロ)です。

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