MCP Apps: 도구 호출 안에서의 인터랙티브 UI
MCP는 첫 해 전체 동안 텍스트 프로토콜이었습니다. 도구 호출은 JSON 또는 Markdown을 반환했고 클라이언트는 원하는 대로 렌더링했습니다. MCP Apps — 2026년 1월 26일에 최종 확정되어 **2026-07-28 코어 사양**에 편입된 최초의 공식 확장 — 는 UI 채널을 추가합니다. 이제 서버는 HTML 조각을 전송할 수 있고, 클라이언트는 이를 샌드박스 iframe에서 렌더링하며, iframe은 postMessage 위의 JSON-RPC 2.0을 통해 통신합니다. 모든 쓰기 작업은 여전히 다른 도구 호출과 동일한 감사 경로를 거칩니다. "Claude 안에서 전체 웹앱을 실행"하는 것이 아니라 인터랙티브 리포트 + 실행 전 확인이라고 생각하세요.
- 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가 아닌 것
- 완전한 웹앱 런타임이 아닙니다. iframe은 기본적으로 네트워크 없음(`connect-src 'none'`), 최상위 네비게이션 없음, 서드파티 스크립트 없음입니다. CDN에서 React를 로드하고 자체 API를 호출해야 하는 아이디어라면 이것이 아닙니다.
- 도구 호출 승인을 우회하는 방법이 아닙니다. 무언가를 변경하는 UI 액션은 여전히 호스트가 로그하고, 스로틀링하고, 명시적 사용자 동의를 요구할 수 있는 정상적인 JSON-RPC 도구 호출로 이동합니다.
- 새니타이제이션이 아닙니다. 샌드박스는 UI가 할 수 있는 일을 제한하지, 사용자가 입력하도록 속이는 것을 제한하지 않습니다. 모든 App을 서드파티 코드처럼 취급하세요 — 어떤 서버가 UI를 렌더링할 수 있는지 허용 목록을 만드세요.
- 영구적이지 않습니다. 세션이 없습니다. 도구 호출이 끝나면 iframe도 사라집니다. 호출 간에 필요한 상태는 나머지 스테이트리스 사양과 정확히 같이 반환하는 서버 측 핸들에 있습니다.
네 가지 움직이는 부품
- 클라이언트는 요청별 _meta.capabilities에서 역방향 DNS 네임스페이스 io.modelcontextprotocol/ui 아래에 확장을 광고하고 렌더링할 수 있는 MIME 타입을 나열합니다. 그러한 기능을 보지 못한 서버는 UI 채널을 건너뛰고 일반 결과를 반환합니다 — 확장 전체가 양쪽에서 옵트인입니다.
- 서버는 ui://<server>/<name> URI에 MIME text/html;profile=mcp-app으로 하나 이상의 리소스를 노출합니다. 리소스는 미리 선언되므로 호스트는 도구가 실행되기 전에 프리페치하고, 해싱하고, 검토할 수 있습니다. 콘텐츠는 일반적인 resources/read 호출을 통해 제공됩니다 — 새 엔드포인트 없음.
- UI를 렌더링하려는 도구는 스키마에 _meta.ui = { resourceUri: 'ui://…', visibility: ['model' | 'app'] }을 부착합니다. visibility는 모델이 UI 참조를 '볼' 수 있는지(UI 어포던스에 대해 추론하는 모델에 유용) 또는 앱 전용인지 제어합니다.
- 샌드박스 iframe 내부에서 UI 코드는 postMessage를 통해 window.parent에 JSON-RPC 2.0을 사용합니다. 일반적으로 첫 번째 메시지는 도구 인수와 서버가 전달한 핸들을 포함한 ui/initialize입니다. 거기서부터 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를 사용할 수 있습니다.
한 화면으로 보는 보안 모델
- 샌드박스: 콘텐츠는 샌드박스된 <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어휘
출처 및 추가 자료
- SEP-1865: MCP Apps — Interactive User Interfaces for MCP — 사양 자체.
ext-apps저장소 — 동결된 2026-01-26 사양 텍스트: 기능 형태, CSP 기본값, postMessage 시맨틱.- MCP Apps: Extending servers with interactive user interfaces — 설계 근거를 안내하는 발표 게시물.
- The 2026-07-28 MCP Specification Release Candidate — App이 함께 배포되는 코어 릴리스. Extensions 프레임워크 섹션은 이제 모든 확장이 따르는 협상 패턴을 설명합니다.
- Anthropic and OpenAI Join Forces to Standardize Interactive AI Interfaces with MCP Apps — SEP-1865 뒤의 다중 벤더 협업에 대한 맥락.
다음
- MCP 2026-07-28: 스테이트리스 사양 — 모든 MCP Apps 호스트와 서버도 사용하는 코어 프로토콜.
- MCP 서버 보안 — UI에서 시작된 도구 호출을 수락하기 전에 갖춰야 할 서버 측 하드닝.
- MCP 및 도구 연결 — API 측 커넥터가 여러분이 렌더링하는 App의 서버에 실제로 어떻게 도달하는지.