
MCP Apps: 보안 허점을 만들지 않고 MCP 도구에 인터랙티브 인터페이스를 추가하는 방법
요약
Model Context Protocol(MCP)의 확장인 MCP Apps를 통해 도구에 인터랙티브 UI를 추가하는 방법과 보안 가이드를 설명합니다. UI는 샌드박스된 iframe 내에서 실행되어 호스트의 데이터에 직접 접근하는 것을 방지하며, 사용자 승인 및 데이터 탐색을 돕는 용도로 설계되었습니다.
핵심 포인트
- MCP Apps는 대시보드, 양식, 테이블 등 인터랙티브 UI를 제공함
- 보안을 위해 UI는 샌드박스된 iframe 내에서 실행됨
- UI가 호스트의 DOM, 쿠키, 저장소에 직접 접근하는 것을 차단함
- 단순한 UI 제공이 아닌, 명확한 권한 검증과 보안 정책 준수가 필수적임
- 호스트가 지원하지 않을 경우를 대비한 텍스트 기반 폴백(fallback) 설계 필요
MCP Apps는 도구(tool)가 채팅 내에서 인터랙티브 인터페이스를 반환할 수 있도록 허용합니다. 이는 승인, 탐색 및 결정을 내리는 데 유용하며, 권한을 우회하거나 에이전트를 제어 장치 없는 임베디드 웹으로 만드는 용도가 아닙니다.
MCP Apps는 서버가 호환 가능한 채팅 호스트(host) 내에서 인터랙티브 인터페이스(대시보드, 양식, 테이블 또는 승인 흐름)를 반환할 수 있도록 하는 Model Context Protocol의 확장입니다. UI는 샌드박스 처리된 iframe 내에서 실행되며 제어된 메시지를 통해 호스트와 통신합니다. 호스트의 DOM, 쿠키 또는 저장소에 직접 접근할 수 없습니다.
TL;DR
핵심 키워드는
MCP Apps입니다. 의도는 실용적입니다: 언제 도구에 UI가 필요한지, 뷰(view)를 MCP 서버와 어떻게 연결하는지, 그리고 사용자나 실제 데이터 앞에 배치하기 전에 필수적인 보안 제한 사항은 무엇인지 이해하는 것입니다.나의 입장: MCP UI는 도구가 너무 강력해서 이를 꾸미기 위한 것이 아니라, 인간의 모호함을 줄일 수 있을 때 의미가 있습니다. 승인 양식, 필터링 가능한 테이블 또는 결과 탐색기는 수십 번의 턴(turn)을 방지할 수 있습니다. 네트워크 및 쓰기 도구에 자유롭게 접근할 수 있는 미니 애플리케이션은 공격 표면(attack surface)만 늘릴 뿐입니다.
MCP App이란 무엇이며 무엇이 아닌가
일반적인 MCP 서버는 도구(tools), 리소스(resources) 및 프롬프트(prompts)를 노출합니다. 도구의 응답은 대개 텍스트이며, 선택적으로
structuredContent를 포함합니다. MCP App은 일반적으로ui://로 식별되는 UI 리소스를 추가하며, 호스트는 이를 결과와 함께 렌더링할 수 있습니다. 뷰는 결과로부터 데이터를 받고 메시지 브릿지를 통해 호스트에 작업을 요청할 수 있습니다.이것은 공개적인 SPA를 만드는 새로운 방법이 아닙니다. 대화가 여전히 주요 컨텍스트이며, 인터페이스는 텍스트 목록으로 해결하기 어려운 부분을 위한 점진적 개선(progressive enhancement)입니다. 만약 호스트가 MCP Apps를 지원하지 않는다면, 도구는 유용한 텍스트 응답을 계속 반환해야 합니다. 이러한 폴백(fallback)은 단순한 세부 사항이 아니라 이식성 계약(contract of portability)입니다.
또한 이것은 암묵적인 권한 부여가 아닙니다. 뷰(view)에 버튼이 표시된다고 해서 해당 작업을 실행할 수 있다는 의미는 아닙니다. 서버는 일반적인 HTTP 클라이언트로부터 호출이 온 경우와 마찬가지로 사용자, 테넌트(tenant), 인자(arguments) 및 정책(policy)을 검증해야 합니다.
UI는 호스트(host)에 자유롭게 연결되지 않습니다. UI는 컨텍스트를 받고 브리지(bridge)를 통해 작업을 요청하며, 호스트는 어떤 기능을 허용할지 계속해서 결정합니다.
아키텍처: 서버, 호스트 및 뷰
세 가지 구성 요소가 있습니다. MCP 서버는 툴(tool)과 HTML 리소스(resource)를 등록합니다. 호스트는 이 둘을 발견하고 툴을 실행하며, 확장을 지원하는 경우 격리된 iframe에 리소스를 마운트(mount)합니다. 뷰(view)는 작은 클라이언트입니다. 초기화되어 툴의 입력과 결과를 받고, 자신의 기능에 따라 호스트에
tools/call, 리소스 또는 작업을 요청할 수 있습니다.
content와structuredContent사이의 분리는 중요합니다.content는 모델이 필요로 할 수 있는 설명이며 텍스트 폴백(fallback) 역할을 합니다.structuredContent는 ID, 데이터 시리즈, 상태 및 행(row)과 같이 렌더링을 위해 설계된 객체입니다. 단순히 테이블을 그리기 위해 필요한 3,000개의 행을 모델의 컨텍스트에 넣지 마세요. 텍스트 요약과 구조화된 데이터(structured data)를 UI에 전달하십시오.호스트는 신뢰 경계(trust boundary)입니다. 호스트는 호출, 외부 링크, 표시 모드 및 앱의 기능을 제한할 수 있습니다. 뷰를 설계할 때는 모든 권한이 있지 않으며 요청이 거부될 수 있음을 가정하십시오. 이는 불편한 제약이 아니라 건강한 속성입니다.
도움이 되고 있나요? 매주 한 번씩 전달됩니다
개발자를 위한 AI 도구, 에이전트(agents), MCP, 보안 및 워크플로(workflows)를 5분 분량의 이메일로 요약해 드립니다. 스페인어로, 잡음 없이 전달합니다.
도구(tool)에 UI가 필요한 시점
필터로 데이터를 탐색하거나, 옵션을 비교하거나, diff(차이점)를 검토하거나, 승인 양식을 작성하거나, 파이프라인(pipeline)을 시각화하거나, 결과가 수반되는 동작을 확인해야 할 때 MCP Apps를 사용할 것입니다. 이 모든 경우에는 시각적 상태(visual state), 인간의 선택, 또는 모델이 통제력을 잃지 않고 요약하기에는 너무 많은 정보가 존재합니다.
문서 검색, 결정론적(deterministic) 쿼리, 단일 행 동작, 또는 아무도 검사할 필요가 없는 워크플로(workflow)에는 사용하지 않을 것입니다. 텍스트 응답이나 structuredContent만으로도 충분하며 테스트하기에도 더 간단합니다. UI는 라이프사이클(lifecycle), 접근성(accessibility), CSP, 점진적 기능 저하(degradation), 그리고 호스트 매트릭스(matrix of hosts)를 도입하므로, 단순히 새 기술이라고 해서 추가하지 마십시오.
제품 관점에서의 질문은 구체적입니다: 이 결과를 보고 조작함으로써 어떤 인간의 결정이 개선되는가? 이에 답할 수 없다면 도구를 텍스트 형태로 유지하십시오. 만약 그 답이 검토, 선택 또는 승인이라면, 임베디드 UI(embedded UI)가 오류와 불필요한 턴(turn)을 줄여줄 수 있습니다.
server.ts — 텍스트 폴백(fallback)과 뷰(view)를 위한 데이터를 갖춘 도구
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer({ name: "release-dashboard", version: "1.0.0" });
...
이 예제는 두 가지 가시적인 결정을 보여줍니다. 리소스(resource)는 웹 URL을 임의로 생성하지 않고, 등록된 ui:// URI를 사용합니다. 또한 도구(tool)는 데이터를 읽기 전에 신원(identity)과 테넌트(tenant)를 확인하며, structuredContent에는 안전한 뷰 모델(view model)만 포함됩니다. @modelcontextprotocol/ext-apps 패키지는 도구/리소스(tools/resources)를 등록하고 뷰를 구축하기 위한 헬퍼(helpers)를 제공하지만, 이러한 검증 과정을 대체하지는 않습니다.
뷰(view): iframe을 신뢰할 수 없는 클라이언트로 취급하기
뷰(view)는 브리지(bridge)와 함께 초기화되어 호스트의 이벤트를 기다리고, 검증된 데이터만 렌더링해야 합니다. 뷰의 역할은 사용자 의도를 제시하고 수집하는 것이지, 권한을 결정하는 것이 아닙니다. 사용자가 승인(approve)을 누르면, 뷰는 특정 ID를 가진 제한된 도구(tool)를 호출합니다. 그러면 서버는 해당 사용자가 그 리소스를 승인할 수 있는지, 그리고 상태가 여전히 유효한지를 다시 한번 확인합니다.
리소스(resource)의 HTML에 비밀값(secrets), 수명이 긴 토큰(long-lived tokens) 또는 전체 문서를 전달하는 것을 피하십시오. 격리된 iframe은 권한을 축소하지만, 민감한 데이터를 무해하게 바꾸지는 않습니다. 필요한 최소한의 데이터만 전송하고, 테넌트(tenant)별로 데이터 마스킹(redaction)을 적용하며, 표시되는 모든 데이터는 권한이 있는 사용자라도 복사할 수 있다는 점을 고려해야 합니다.
쓰기 작업(write actions)의 경우, preview → confirm → execute와 같이 명시적인 전환 과정을 모델링하십시오. UI는 영향 범위를 알려주고 확인을 요청할 수 있으며, 서버는 멱등성 키(idempotency key)를 사용하고 반복된 작업이나 만료된 상태를 거부해야 합니다. 이는 결제 API에서 사용하는 것과 동일한 패턴이며, 다만 여기서는 트리거가 채팅 내부에서 발생했다는 점이 다를 뿐입니다.
CSP, 네트워크 연결: 흔히 간과되는 경계선
MCP App은 자신의 네트워크 요구 사항을 선언하며, 호스트(host)는 해당 정책을 적용할 수 있습니다. 제한적인 CSP(Content Security Policy)로 시작하세요. 필요하지 않다면 외부 연결을 차단하고, API나 에셋(assets)을 위해 구체적인 도메인을 지정하며, 편의를 위해 와일드카드(*)를 사용하지 마세요. 앱에 데이터가 필요한 경우, connect-src *를 여는 것보다 감사(audited)된 도구(tool)를 통해 요청하는 것이 더 바람직합니다.
티켓, 문서 또는 사용자로부터 유입된 HTML이나 마크다운(markdown)이 신뢰할 수 있는 마크업(markup)으로서 뷰(view)에 도달하게 두지 마세요. 데이터를 정화(sanitize)하고, 텍스트에는 textContent를 사용하며, URL을 제한하고, 동적 템플릿(dynamic templates) 주입을 피해야 합니다. 프롬프트 인젝션(Prompt injection)은 결과를 UI로 옮긴다고 해서 사라지지 않습니다. 외부 콘텐츠는 여전히 인간이나 후속 호출에 영향을 미치려고 시도할 수 있습니다.
외부 링크를 여는 것은 호스트의 명시적인 기능이어야 하며, 테이블 셀(table cell)을 렌더링하는 과정에서 발생하는 부수 효과(side effect)가 되어서는 안 됩니다. 어떤 동작이 사용자를 대화창 밖으로 나가게 할 때는 도메인과 목적지를 알려주세요. 내부 패널처럼 보이는 곳에서 조용히 리다이렉션(redirection)되는 것보다 약간의 마찰(friction)을 주는 것이 더 낫습니다.
점진적 향상(Progressive enhancement) 및 테스트
MCP App 지원 여부는 호스트마다 다르며 변경될 수 있습니다. 따라서 두 가지 출력 방식을 테스트하세요: UI가 있는 세션과 텍스트만 있는 세션입니다. 텍스트 콘텐츠는 인터페이스에 의존하지 않고도 결과, 한계 및 다음 동작을 설명할 수 있어야 합니다. 호스트가 뷰를 렌더링하지 않더라도, 도구(tool)가 막다른 길(dead end)이 되어서는 안 됩니다.
서버에서 계약 테스트(contract tests)를 자동화하세요: 입력 스키마(schema), 권한 부여(authorization), 테넌트별 필터링(tenant filtering), structuredContent 모델, 오류 및 이중 실행(double execution) 등을 포함합니다. 뷰(view)에서는 부분적, 빈 값 또는 거부된 응답이 채팅을 차단하지 않는지 테스트하세요. 그리고 실제로 지원할 호스트들과 수동으로 상호작용을 연습하세요. 로컬에서 작동하는 예시를 보았다는 이유만으로 호환성을 선언해서는 안 됩니다.
단순한 클릭 이상의 유용성: UI가 얼마나 많은 턴(turn)을 줄이는지, 얼마나 많은 승인이 취소되는지, 어떤 작업이 취소되는지, 결과가 나타나기까지 얼마나 걸리는지, 그리고 텍스트 폴백(fallback)이 얼마나 자주 사용되는지를 측정하세요. 만약 오류나 의사결정 시간을 줄이지 못한다면, 잘 설계된 텍스트 응답이 아마 더 나았을 것입니다.
프로덕션 체크리스트
- 도구(tool)는 호스트가 UI를 지원하지 않더라도 완전한 텍스트 응답을 반환해야 합니다.
- 리소스(resource)는 MCP Apps를 위해 등록된
ui://URI와 특정 MIME 타입을 사용해야 합니다. - 뷰(view)는 최소한의
structuredContent만 수신하며, 비밀 정보나 다른 테넌트(tenant)의 데이터를 포함해서는 안 됩니다. - 모든 읽기 또는 쓰기 도구는 서버에서 사용자, 테넌트, 스코프(scope) 및 상태를 재검증해야 합니다.
- 상태를 변경하는 액션(mutant actions)은 미리보기, 확인, 멱등성(idempotency) 및 감사(auditing) 기능을 갖추어야 합니다.
- CSP(Content Security Policy)는 필수적인 도메인만 선언해야 하며, 와일드카드나 검증되지 않은 원격 스크립트를 허용해서는 안 됩니다.
- UI는 모든 외부 콘텐츠를 데이터로 취급하고 표시하기 전에 새니타이징(sanitizing)해야 합니다.
- 각 대상 호스트에서 텍스트 폴백(fallback) 및 기능 저하(degradation) 상황을 테스트해야 합니다.
- 로그에는 ID, 작업, 결과 및 거부 사항을 저장하되, 기본적으로 민감한 콘텐츠는 저장하지 않습니다.
결론
MCP Apps는 실제적인 결핍을 해결합니다. 텍스트 대화만으로는 설명하기 어려운 결정들이 존재하기 때문입니다. 가치는 채팅창 안에 예쁜 대시보드를 넣는 것이 아니라, 이미 명확한 계약(contract)을 가진 도구에 작고 맥락적이며 되돌릴 수 있는 검토 영역을 제공하는 데 있습니다.
하나의 읽기 도구와 한 가지 일을 탁월하게 수행하는 뷰(예: 인시던트 필터링, 결과 검토 또는 계획 비교)로 시작하세요. 텍스트 폴백(fallback), 엄격한 CSP, 최소한의 데이터, 그리고 분리된 쓰기 호출(write calls)을 유지하십시오. 그것이 운영 가능해지면 확장하세요. 에이전트 환경에서 모든 인터랙티브한 픽셀은 곧 권한의 표면(permission surface)이기도 합니다.
자주 묻는 질문 (FAQ)
MCP Apps란 무엇인가요?
Model Context Protocol (MCP)의 확장 기능으로, MCP 서버가 일반적인 도구의 텍스트 및 구조화된 콘텐츠 외에도 호환 가능한 호스트 내에서 인터랙티브 인터페이스를 제공할 수 있도록 합니다.
MCP App은 모든 클라이언트에서 작동하나요?
아니요. 지원 여부는 호스트 (host)에 따라 달라집니다. 따라서 인터페이스를 렌더링할 수 없는 경우를 대비하여, 도구 (tool)는 유용한 텍스트 기반의 폴백 (fallback)을 계속 제공해야 합니다.
MCP Apps의 UI가 호스트의 DOM이나 쿠키에 접근할 수 있나요?
접근해서는 안 됩니다. 아키텍처는 샌드박스 처리된 iframe (sandboxed iframe)과 메시지 브리지 (message bridge)를 통한 통신을 사용하며, 호스트가 권한 (capabilities)에 대한 제어권을 유지합니다.
텍스트 응답 대신 언제 MCP Apps를 사용해야 하나요?
사용자가 데이터를 탐색하거나, 옵션을 선택하거나, 아티팩트 (artifact)를 검토하거나, 작업을 승인해야 할 때 사용합니다. 단순한 질의의 경우, 텍스트 (text) 또는 구조화된 콘텐츠 (structuredContent)가 일반적으로 더 견고합니다.
MCP App을 어떻게 보호하나요?
각 도구 (tool)에 대해 서버에서 권한 부여 (authorization)를 검증하고, 구조화된 콘텐츠 (structuredContent)를 제한하며, 엄격한 CSP (Content Security Policy)를 적용하고, 외부 데이터를 정화 (sanitize)하며, 쓰기 작업에 대해 확인을 요구하고, 기본적으로 비밀 정보를 저장하지 않으면서 작업을 기록하십시오.
기존 웹사이트를 MCP App으로 재사용할 수 있나요?
네, 뷰 (view)를 호스트의 라이프사이클 (lifecycle) 및 브리지 (bridge)에 맞게 조정하고, 리소스 (resources)와 CSP를 선언하며, 텍스트 출력을 유지한다면 가능합니다. 기존의 SPA (Single Page Application)가 변경 없이 MCP iframe 내에서 안전하게 작동할 것이라고 가정해서는 안 됩니다.
기존 도구를 위한 첫 번째 안전한 MCP App을 만드는 방법
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기