본문으로 건너뛰기

MCP Apps: 도구 호출 안에서의 인터랙티브 UI

고급

MCP는 첫 해 전체 동안 텍스트 프로토콜이었습니다. 도구 호출은 JSON 또는 Markdown을 반환했고 클라이언트는 원하는 대로 렌더링했습니다. MCP Apps2026년 1월 26일에 최종 확정되어 **2026-07-28 코어 사양**에 편입된 최초의 공식 확장 — 는 UI 채널을 추가합니다. 이제 서버는 HTML 조각을 전송할 수 있고, 클라이언트는 이를 샌드박스 iframe에서 렌더링하며, iframe은 postMessage 위의 JSON-RPC 2.0을 통해 통신합니다. 모든 쓰기 작업은 여전히 다른 도구 호출과 동일한 감사 경로를 거칩니다. "Claude 안에서 전체 웹앱을 실행"하는 것이 아니라 인터랙티브 리포트 + 실행 전 확인이라고 생각하세요.

What you'll learn
  • MCP Apps가 실제로 무엇인지 — 그리고 의도적으로 아닌 네 가지
  • 와이어 형태: 기능 협상, ui:// 리소스, 도구 _meta.ui 링크, postMessage 브리지
  • 보안 모델 — 샌드박스, CSP, Permission Policy, 호스트 승인 — 그리고 여전히 노출된 지점
  • 일반 도구 결과 대신 App에 손을 뻗어야 할 때 (드물지만 가치 있는 패턴)
  • 이미 알고 있는 2026-07-28 스테이트리스 사양의 나머지 부분과 어떻게 결합되는지

한 문단 요약

서버는 하나 이상의 UI 리소스ui://<server>/<name> URI에 MIME 타입 text/html;profile=mcp-app으로 선언합니다. 도구는 스키마에 _meta.ui.resourceUri를 넣어 "UI가 있음"을 광고합니다. tools/call 시 서버는 일반적인 텍스트/데이터에 더불어 UI 리소스에 대한 참조를 반환할 수 있습니다. 클라이언트는 resources/read로 HTML을 가져와 샌드박스된 <iframe>에 넣고, iframe과 호스트는 JSON-RPC 2.0을 사용해 postMessage로 대화합니다. UI에서 시작된 도구 호출도 다른 도구 호출과 동일한 사용자 승인이 필요합니다 — App은 렌더링할 수 있고, 제안할 수 있지만, 조용히 실행할 수는 없습니다.

MCP Apps가 아닌 것

Watch out
  • 완전한 웹앱 런타임이 아닙니다. iframe은 기본적으로 네트워크 없음(`connect-src 'none'`), 최상위 네비게이션 없음, 서드파티 스크립트 없음입니다. CDN에서 React를 로드하고 자체 API를 호출해야 하는 아이디어라면 이것이 아닙니다.
  • 도구 호출 승인을 우회하는 방법이 아닙니다. 무언가를 변경하는 UI 액션은 여전히 호스트가 로그하고, 스로틀링하고, 명시적 사용자 동의를 요구할 수 있는 정상적인 JSON-RPC 도구 호출로 이동합니다.
  • 새니타이제이션이 아닙니다. 샌드박스는 UI가 할 수 있는 일을 제한하지, 사용자가 입력하도록 속이는 것을 제한하지 않습니다. 모든 App을 서드파티 코드처럼 취급하세요 — 어떤 서버가 UI를 렌더링할 수 있는지 허용 목록을 만드세요.
  • 영구적이지 않습니다. 세션이 없습니다. 도구 호출이 끝나면 iframe도 사라집니다. 호출 간에 필요한 상태는 나머지 스테이트리스 사양과 정확히 같이 반환하는 서버 측 핸들에 있습니다.

네 가지 움직이는 부품

Guided walkthrough1 of 4
  1. 클라이언트는 요청별 _meta.capabilities에서 역방향 DNS 네임스페이스 io.modelcontextprotocol/ui 아래에 확장을 광고하고 렌더링할 수 있는 MIME 타입을 나열합니다. 그러한 기능을 보지 못한 서버는 UI 채널을 건너뛰고 일반 결과를 반환합니다 — 확장 전체가 양쪽에서 옵트인입니다.

실제로 입력하게 될 와이어 형태

클라이언트 요청의 기능 광고.

{
"jsonrpc": "2.0",
"method": "tools/call",
"params": { "name": "get_weather", "arguments": { "location": "Milan" } },
"_meta": {
"capabilities": {
"extensions": {
"io.modelcontextprotocol/ui": {
"mimeTypes": ["text/html;profile=mcp-app"]
}
}
}
}
}

서버 측의 도구 스키마.

{
"name": "get_weather",
"description": "Get current weather and a 7-day forecast for a location.",
"inputSchema": {
"type": "object",
"properties": { "location": { "type": "string" } },
"required": ["location"]
},
"_meta": {
"ui": {
"resourceUri": "ui://weather-server/dashboard-template",
"visibility": ["model", "app"]
}
}
}

그 옆에 선언된 UI 리소스.

{
"uri": "ui://weather-server/dashboard-template",
"mimeType": "text/html;profile=mcp-app",
"_meta": {
"ui": {
"connectDomains": [],
"permissions": []
}
}
}

그리고 iframe이 다시 보내는 첫 번째 메시지.

UI → 호스트: postMessage 위의 JSON-RPC 2.0

window.parent.postMessage(
{
  jsonrpc: "2.0",
  id: 1,
  method: "ui/initialize",
  params: {
    toolName: "get_weather",
    toolArguments: { location: "Milan" },
    toolResult: /* whatever the server returned alongside the UI */
  }
},
"*"
);

그 이후의 모든 것 — 호스트에게 다른 도구를 호출하도록 요청하기, 리소스 구독하기, UI 측 이벤트 보내기 — 는 동일한 채널 위의 더 많은 JSON-RPC 2.0이며, 이미 서버 측에서 사용하는 와이어 프로토콜과 동일하게 구조화됩니다. 그것이 요점입니다: UI 개발자는 맞춤형 심을 배우는 대신 표준 @modelcontextprotocol/sdk를 사용할 수 있습니다.

한 화면으로 보는 보안 모델

What you'll learn
  • 샌드박스: 콘텐츠는 샌드박스된 <iframe>에서 렌더링됩니다. 기본: 최상위 네비게이션 없음, 임의 오리진으로의 폼 없음, 팝업 없음, 플러그인 없음.
  • 기본 CSP: connect-src 'none'. UI는 어디서도 fetch()할 수 없습니다. 특정 오리진을 허용하려면 서버가 리소스의 _meta.ui.connectDomains에 선언하고 호스트가 그 목록으로부터 CSP 헤더를 구성합니다.
  • Permission Policy: 리소스의 _meta.ui.permissions는 iframe의 allow 속성 — 카메라, 마이크, 지오로케이션, clipboard-write — 로 매핑됩니다. 암묵적으로 허용되는 것은 없습니다.
  • UI에서 시작된 모든 쓰기는 정상적인 도구 호출입니다. 호스트는 이를 검증하고, 사용자 승인을 요구할 수 있으며, 모델에서 시작된 호출과 동일한 감사 스트림에 로그합니다.
  • 템플릿은 프리페치 가능하고 해시 가능합니다. 특정 해시에 고정하는 호스트는 아래에서 스왑 공격을 잡아냅니다. 그렇지 않은 호스트는 그날 서버가 제공하는 것을 그대로 받습니다.

실제로 App에 손을 뻗어야 할 때

의도적으로 기준이 높습니다 — 모든 App은 여러분에게는 클라이언트 표면적이고 사용자에게는 공격 표면입니다. UI가 채팅 턴을 명확한 차이로 이길 때만 손을 뻗으세요:

  • 데이터 그리드 확인. "제가 업데이트하려는 47개 행이 있습니다 — 원하지 않는 것은 체크 해제하세요." 그것을 채팅으로 렌더링하는 것은 거대하거나 정직하지 않습니다. 체크박스가 있는 그리드는 정직하고 빠릅니다.
  • 차트 기반 승인. "여기 쿼리 플랜 / 비용 예측 / 트레이스 워터폴이 있습니다. 승인 또는 거절하세요." 차트는 HTML에서 저렴하고 ASCII로는 끔찍합니다.
  • 구조화된 선택기로 입력의 형태가 텍스트가 아닌 경우. 미니 캘린더가 있는 날짜 범위, 파일 시스템 또는 조직도 위의 트리 선택, 경계 상자가 있는 지도 선택기.
  • 인플레이스 편집기로 diff 뷰 + 수락/거절이 전체 답변을 다시 생성하는 것보다 나은 작은 단계용.

여러분의 아이디어가 "전체 대시보드를 임베드하게 해주세요"라면 여기에 속하지 않습니다 — 외부 링크를 사용하세요. 여러분의 아이디어가 "샌드박스에서 임의의 사용자 코드를 실행하게 해주세요"라면 확실히 여기에 속하지 않습니다.

2025-11-25 호환성 계약

MCP Apps는 2026-07-28 코어 사양에 도입된 확장 프레임워크 위에서 동작하지만, 확장 자체는 2026-01-26부터 Final 상태입니다. 실용적 의미: 이전 2025-11-25 개정판과 이 확장만 사용하는 클라이언트는 이미 App을 렌더링할 수 있습니다. 스테이트리스 2026-07-28 클라이언트는 요청별 기능 맵의 하나의 항목으로 이를 받아들입니다. 확장이 양쪽에서 옵트인이기 때문에, 이를 추가하는 서버는 오래된 클라이언트를 절대 깨뜨리지 않습니다 — 단지 UI 분기를 건너뜁니다. 이것이 모든 미래의 MCP 확장이 취할 모양이므로, 이 핸드셰이크를 이해하는 것은 앞으로 올 것들에 대한 부채를 갚는 것입니다.

AILmanac 지도에서 MCP Apps의 위치

  • MCP 2026-07-28: 스테이트리스 사양 — App이 동작하는 코어 프로토콜. 특히 Extensions 프레임워크 섹션(SEP-2133).
  • MCP 및 도구 연결 — API 측 커넥터. iframe을 렌더링하는 것은 클라이언트입니다. 오늘날의 커넥터는 전송을 추상화하므로, App은 변경 없이 흐릅니다.
  • Claude Code의 MCP — Claude Code의 MCP 지원이 향하는 곳. UI 렌더링은 클라이언트 기능이지 서버 기능이 아니므로, 이것이 여러분에게 어떤 App이 렌더링될지 결정하는 표면입니다.
  • MCP 서버 보안 — 그 iframe의 반대쪽에 있는 서버에 대한 하드닝 패턴.

빠른 확인

Check yourself

0/4
  1. MCP Apps iframe은 어떻게 호스트에게 다시 통신하나요?
  2. 기본적으로 MCP Apps iframe은 어떤 네트워크 접근을 가지나요?
  3. 도구가 UI를 렌더링하려고 합니다. 일반 도구를 UI를 실어 나르는 도구로 바꾸는 메타데이터 한 조각은 무엇인가요?
  4. iframe 안의 UI가 서버에서 무언가를 변경하려고 합니다. 어떻게 되나요?

어휘

MCP Apps 용어
Enter 또는 스페이스 키를 눌러 카드를 뒤집습니다. 좌우 화살표 키로 카드를 이동할 수 있습니다.용어가 표시되었습니다.
1 / 9

출처 및 추가 자료

다음