MCP 서버에서 이제 시각적 UI가 가능해졌습니다

발행: (2026년 2월 3일 오전 12:18 GMT+9)
4 분 소요
원문: Dev.to

Source: Dev.to

Cover image for Visual UIs Are Now Possible in MCP Servers

MCP 서버는 이제 Claude Desktop 채팅 창에서 직접 인터랙티브 UI를 렌더링할 수 있습니다. 단순 텍스트 응답이 아니라 실제 HTML, JavaScript, 지도, 차트 등 모든 것이 가능합니다.

ISS Tracker demo

변경 사항

@modelcontextprotocol/ext-apps 라이브러리는 MCP 도구가 시각 UI를 반환하도록 합니다. 도구를 호출하면 텍스트만 받는 것이 아니라 대화에 인라인으로 렌더링되는 인터랙티브 iframe을 받게 됩니다.

이는 AI 어시스턴트가 단순히 설명하는 것이 아니라 실제로 보여줄 수 있음을 의미합니다.

Resources

Source:

작동 방식

아키텍처는 두 부분으로 구성됩니다: 데이터를 가져오고 UI를 선언하는 서버와 이를 렌더링하는 클라이언트‑사이드 앱.

서버 측

UI 메타데이터가 포함된 도구를 HTML 리소스에 연결하여 등록합니다:

import { registerAppTool, registerAppResource } from "@modelcontextprotocol/ext-apps/server";

const resourceUri = "ui://iss-tracker/mcp-app.html";

// UI 리소스(번들된 HTML) 등록
registerAppResource(server, resourceUri, "text/html", () => APP_HTML);

// UI 메타데이터와 함께 도구 등록
registerAppTool(server, "where_is_iss", {
  description: "실시간 지도에 ISS 위치 표시",
  uiResourceUri: resourceUri,
  csp: {
    connectDomains: ["https://*.openstreetmap.org", "https://unpkg.com"],
    resourceDomains: ["https://*.openstreetmap.org", "https://unpkg.com"],
  },
  execute: async () => {
    const [iss, path, geo] = await Promise.all([
      fetch("https://api.wheretheiss.at/v1/satellites/25544").then(r => r.json()),
      fetch(`https://api.wheretheiss.at/v1/satellites/25544/positions?timestamps=${timestamps}`).then(r => r.json()),
      fetch("http://ip-api.com/json/").then(r => r.json()),
    ]);
    return { iss, path, user: { latitude: geo.lat, longitude: geo.lon, city: geo.city } };
  },
});

csp 필드는 매우 중요합니다—UI가 접근해야 하는 외부 도메인을 선언합니다. 이를 지정하지 않으면 Leaflet 타일과 스크립트가 차단됩니다.

클라이언트 측

UI는 도구 결과를 받아서 렌더링합니다:

import { App } from "@modelcontextprotocol/ext-apps";

const app = new App({ name: "ISS Tracker", version: "1.0.0" });

app.ontoolresult = (result) => {
  const data = result.structuredContent;
  // 데이터를 사용해 UI 업데이트
  updateMap(data.iss, data.user);
};

app.connect();

핵심 주의사항: 동적 스크립트 로딩

정적 <script> 태그는 srcdoc iframe에서 작동하지 않습니다. 외부 라이브러리를 동적으로 로드해야 합니다:

async function loadLeaflet(): Promise<void> {
  if (typeof L !== "undefined") return;

  // CSS 로드
  const cssLink = document.createElement("link");
  cssLink.rel = "stylesheet";
  cssLink.href = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css";
  document.head.appendChild(cssLink);

  // JS 로드
  return new Promise((resolve, reject) => {
    const script = document.createElement("script");
    script.src = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js";
    script.onload = () => resolve();
    script.onerror = () => reject(new Error("Failed to load Leaflet"));
    document.head.appendChild(script);
  });
}

많은 개발자가 이 부분에서 난관을 겪었습니다—Leaflet은 이렇게 주입하지 않으면 로드되지 않습니다.

직접 해보세요

git clone https://github.com/JasonMakes801/iss-tracker-mcp
bun install && bun run build

Claude Desktop 설정에 추가 (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "iss-tracker": {
      "command": "/path/to/bun",
      "args": ["/path/to/iss-tracker/dist/index.js", "--stdio"]
    }
  }
}
  1. Claude Desktop을 재시작합니다.
  2. 다음과 같이 물어보세요: “ISS는 어디에 있나요?”

다음은 무엇인가요

지도는 시작에 불과합니다. 대시보드, 차트, 양식, 데이터 시각화—HTML로 만들 수 있는 모든 것이 이제 AI 대화 안에서 실현될 수 있습니다.

당신은 이것으로 무엇을 만들고 싶나요?

Back to Blog

관련 글

더 보기 »