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) — 차트가 대화 내에 렌더링되는 대화형 위젯으로 도착하며, 위젯을 조정하는 비용은 토큰 0개입니다.

이 두 가지가 합쳐져서 볼 만한 것을 만듭니다: 데이터셋 드롭다운에 Entra 그룹이 허용하는 데이터셋만 정확히 나타나고, 위젯과의 모든 상호 작용마다 서버 측에서 강제되는 Altair 차트 빌더입니다.

MCP 2026-07-28 및 Python SDK mcp 2.0을 대상으로 구축되었습니다. Azure Container Apps에 배포합니다. MIT 라이선스입니다.

알아두세요: 데모이지 제품은 아닙니다. 권한 부여 스토리를 이해하기 쉽게 하려고 10개의 공개 샘플 데이터셋과 의도적으로 단순한 계층 모델을 제공합니다.

Palmer penguins 데이터셋의 산점도 옆에 데이터셋/축/마크 컨트롤이 표시된 대화형 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

그러면 모든 호출자가 세 개의 데이터셋 계층을 모두 보유한 것으로 간주됩니다. http://localhost:3001/mcp에 MCP Apps 호스트를 연결하세요. ui/ 프로토콜 전체를 실시간으로 보여 주는 브라우저 기반 호스트는 로컬 개발을 참조하세요.

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 기능

위치

확인할 수 있는 것

권한 부여(OAuth 2.1 RS)

auth.py

그룹 멤버십이 카탈로그의 크기를 바꿈

MCP Apps(io.modelcontextprotocol/ui)

chart_builder.html

드롭다운을 바꾸면 그 자리에서 차트가 다시 렌더링됨

앱 전용 도구(visibility: ["app"])

render_chart

위젯을 다시 렌더링할 때 토큰 0 소모

input_required

plot_dataset

위젯이 없는 호스트는 대신 양식을 받습니다

범위 상향(403 insufficient_scope)

export_chart

첫 내보내기에서 재동의가 트리거됩니다

리소스 + 템플릿

data://catalog

권한에 따라 필터링됩니다

자동 완성

dataset 인자

열 수 없는 데이터셋은 자동 완성에 나타나지 않습니다

프롬프트

explore_dataset

안내되는 첫 탐색

이 개정 버전에서 사양이 한때 사용되지 않는 것으로, 따라서 이 서버가 피하는 것 두 가지: samplinglogging 기능(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

diamonds, seoul, titanic

엔지니어와 과학자는 같은 개수의 데이터셋을 갖지만, 같은 데이터셋을 갖고 있지는 않습니다. 그래서 같은 질문을 하는 두 동료가 서로 다른 답을 얻습니다.

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

--now는 역할을 사용자에게 직접 할당합니다. 그룹 변경은 새 토큰에 반영되는 데 몇 분이 걸릴 수 있지만, 직접 할당은 약 20초면 충분합니다.

역할은 하드 거부입니다. 요청해서 그룹에 들어갈 수는 없습니다. 당신의 계층 밖 데이터셋은 tools/list 결과, resources/list, 자동 완성, 그리고 위젯의 드롭다운에 존재하지 않습니다. 즉, 나열되었다가 거부되는 것이 아니라 아예 없는 것입니다.

범위는 소프트 거부입니다. 누락된 Datasets.Export403WWW-Authenticate: Bearer error="insufficient_scope" 챌린지를 반환하며, 클라이언트는 이를 요청하며 재인가합니다.

이 저장소가 우회하는, 즉 손으로 구성하면 어려운 오류를 만드는 Entra 특유의 함정이 두 가지 있습니다. Entra에는 동적 클라이언트 등록(dynamic client registration)이 없고 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번 단계는 모델이 아니라 앱에서 발생하기 때문에 스펙은 대화에 포함되지 않습니다. 드롭다운을 바꾸는 것은 한 번의 작은 서버 라운드트립이며 토큰은 0입니다.

권한 부여 스토리도 여기서 그대로 적용됩니다. render_chartapp_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-contentdocs/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로 서버를 실행하면 SDK의 Origin 확인도 완화되고 CORS 헤더도 추가됩니다. 브라우저 기반 호스트에 필요하고 인증이 켜져 있는 동안에는 꺼져 있습니다.

위젯 HTML은 서버 구성 시 한 번만 읽힌다는 점을 유의하세요. 편집하려면 서버를 다시 시작해야 합니다.

호스트 호환성

MCP Apps 지원은 호스트마다 다르지만 결과 증상은 매우 비슷해 보입니다 — 주로 오류도 없이 위젯이 비어버리거나 접혀 있는 것입니다.

**docs/HOST-COMPATIBILITY.md**이 실제 관찰 내용, 각 원인을 어떻게 구분했는지, 셋 중 어느 것이 서버 측에서 고칠 수 있는 것처럼 아니고 그렇지 않은 것까지 기록합니다.

데이터셋

공개 4, 작업 3, 기밀 3 — 모두 Vega 데이터셋 컬렉션의 공개 표본 데이터입니다. 이미 이미지를 만드는 빌드 시점에 내장되므로 실행 중인 컨테이너는 어떤 데이터 소스에도 네트워크가 필요하지 않습니다. 계층 이름은 명확한 접근 모델을 위해 의도적으로 선택된 예시입니다.

open

operations

confidential (설명용 이유)

iris

seattle-weather

diamonds — 단가 정보

penguins

us-employment

movies — 상업 수입

cars

gapminder

titanic — 개인 식별 기록

barley

구성

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_DATAVIZ_)라는 이름을 유지합니다. 이름을 바꾸면 모든 Azure 리소스 이름과 환경 변수가 이득 없이 바뀌기 때문입니다.

테스트

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

tests/test_http.py는 실제 uvicorn 서버를 실행하고 401 챌린지, PRM 문서, 403 insufficient_scope 상향, 그리고 input_required 왕복을 검증합니다.

비용

Container Apps는 0으로 스케일링되므로(minReplicas: 0), 유휴 데모 비용은 거의 없습니다. ACR Basic과 Log Analytics만이 계속해서 부과되는 비용입니다(월 수 유로).

az group delete --name rg-mcp-dataviz --yes && ./scripts/entra-teardown.sh

라이선스

MIT

-
license - not tested
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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.

View all MCP Connectors

Latest Blog Posts

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