Skip to main content
Glama
eomgerm

gempack-shop

by eomgerm

겜팩샵 — WebMCP + MCP 데모

닌텐도 스위치 게임팩 쇼핑몰. AI 에이전트가 웹 애플리케이션을 조작하는 두 가지 방식을 한 프로젝트에서 나란히 구현해 비교합니다.

  • WebMCP — 페이지가 document.modelContext 와 HTML 속성으로 툴을 노출. 브라우저 내장 에이전트, 같은 트리의 동일 출처 문서, exposedTo 로 허용한 iframe 에이전트가 호출

  • MCP — 서버가 /mcp 엔드포인트로 툴을 노출. 외부 클라이언트(Claude Code 등)가 호출

두 경로와 사람의 UI 조작이 같은 서버 상태를 공유하므로, 어느 쪽에서 바꿔도 열려 있는 모든 브라우저가 즉시 따라옵니다.

빌드 단계 없음. 프레임워크 없음. 의존성은 MCP SDK 와 zod 둘.


빠른 시작

npm install
npm start        # http://localhost:4173
npm test         # 로직 + 서버 상태 검증

브라우저에서 바로 쇼핑할 수 있습니다 — 검색, 발매일 범위 필터, 5가지 정렬, 장바구니, 데모 결제.

외부 MCP 클라이언트 연결

claude mcp add --transport http gempack-shop http://localhost:4173/mcp

연결하면 에이전트가 상품을 검색하고 장바구니를 조작할 수 있습니다. 그때마다 열려 있는 브라우저 화면의 해당 영역이 빨간 테두리로 표시되고 토스트가 뜹니다.

페이지 내 WebMCP 활성화

WebMCP 는 아직 Chrome 플래그 뒤에 있습니다.

  1. Chrome 149+ 에서 chrome://flags/#enable-webmcp-testingEnabled → 재시작

  2. http://localhost:4173 접속 (localhost 는 SecureContext 로 인정되어 https 불필요)

  3. 우측 상단 배지가 WebMCP 연결됨 이면 준비 완료

미지원 브라우저에서도 쇼핑몰과 서버측 MCP 는 정상 동작합니다. 선언형 HTML 속성은 그냥 무시됩니다.


Related MCP server: MCP E-Commerce Agent

아키텍처

        사람이 UI 조작 ─────┐
                            │
  WebMCP 폼 제출 ───────────┼──▶  state.js  ──SSE──▶  열려 있는 모든 브라우저
  (브라우저 내장 에이전트)   │   (서버 단일 상태)
                            │
  MCP tools/call ───────────┘
  (외부 에이전트, /mcp)

state.jsapply(action, args, by)유일한 변경 진입점입니다. by 가 출처를 구분합니다.

by

출처

하이라이트

human

사람이 버튼 클릭

없음

webmcp

에이전트가 채운 폼을 사람이 제출

제출 전에 :tool-form-active 가 이미 표시

mcp

외부 MCP 클라이언트

변경된 영역에 .mcp-touched + 토스트 브로드캐스트

브라우저는 필터·정렬을 직접 계산하지 않습니다. 서버가 완성된 스냅샷을 SSE 로 밀어주고 화면은 그것을 렌더링만 합니다.

하이라이트

AI 가 건드린 자리를 눈에 보이게 하는 것이 이 데모의 핵심입니다. 두 경로가 서로 다른 수단을 씁니다.

WebMCP 는 브라우저가 상태를 CSS 의사클래스로 노출하므로 스타일만 덮어씁니다.

form:tool-form-active { outline: 3px solid #e60012; }   /* 에이전트가 채운 폼 */
:tool-submit-active   { animation: aiPulse … }          /* 그 폼의 제출 버튼 */

toolautosubmit의도적으로 쓰지 않습니다. 그래서 에이전트가 툴을 호출하면 폼이 채워진 채 하이라이트되고 멈추며, 사람이 제출 버튼을 눌러야 실행됩니다. 그 시점에 SubmitEvent.respondWith() 가 결과를 에이전트에게 돌려줍니다. 결제를 AI 가 단독으로 완료할 수 없는 것이 설계 의도입니다.

MCP 는 의사클래스가 없으므로 서버가 변경 영역 이름(filters, list, cart, order, game-<id>)을 스냅샷에 담아 보내고, 브라우저가 3.4초간 클래스를 붙입니다.

코드를 수정할 때 주의할 점

WebMCP 선언형 API 의 세 가지 제약이 이 코드의 구조를 결정했습니다.

  1. 스키마는 폼이 DOM 에 삽입되는 순간 합성됩니다. <select> 를 JS 로 채우므로, HTML 에 toolname 을 두면 빈 select 로 합성되어 enum 이 비어버립니다. 그래서 data-toolname 으로 두고 옵션과 리스너를 모두 붙인 뒤 마지막에 toolname 을 답니다.

  2. respondWith() 는 이벤트 디스패치 중 동기적으로 호출해야 합니다. await 뒤로 밀리면 invocation 이 실패합니다. 인자로 프로미스를 받으므로 핸들러는 동기로 두고 프로미스를 넘깁니다.

  3. form.reset() 은 실행 중인 invocation 을 취소합니다. 성공 메시지를 넘긴 뒤 폼을 정리하려 reset() 을 부르면 에이전트는 결과 대신 취소를 받습니다. 그래서 어느 핸들러도 reset() 을 부르지 않습니다.

추가로 <option disabled> 은 쓰지 않습니다. HTML 스펙상 "선택됐지만 disabled 인 option" 은 FormData 에서 제외되므로, 품절 상품을 그렇게 막으면 파라미터가 undefined 로 도착합니다. 품절은 제출을 받고 애플리케이션 로직이 이유를 돌려주는 방식으로 처리합니다.


서버측 MCP (/mcp)

힌트

설명

list_games

readOnly

게임팩 18종의 id·제목·발매일·가격·재고·장르

search_games

idempotent

이름 부분일치 + 발매일 범위 + 정렬. 빈 문자열로 조건 해제

view_cart

readOnly

장바구니 항목과 합계

set_cart_item

idempotent

수량 지정. 0 이면 제거. 담기·변경·제거를 하나로 처리

checkout

destructive

데모 결제. 주문 생성 후 장바구니 비움

입력은 zod 로 검증합니다. gameId 는 18개 id 의 enum 이고, 잘못된 값은 JSON-RPC -32602 로 막힙니다. 재고 초과나 빈 장바구니 같은 비즈니스 오류는 isError: true 로 돌려주므로 에이전트가 이유를 읽고 재시도할 수 있습니다.

페이지 내 WebMCP

방식

하이라이트

search_games

선언형 (<form toolname>)

검색 폼

set_cart_item

선언형

장바구니 폼

checkout

선언형

결제 폼

list_games

명령형 (registerTool)

— (읽기 전용)

view_cart

명령형

— (읽기 전용)

선언형 툴의 JSON Schema 는 브라우저가 HTML 에서 합성합니다. toolparamdescription 이 설명이 되고, type="date"format: "date" 로, <select> 옵션은 enum 으로, min/max/stepminimum/maximum/multipleOf 로, requiredrequired 배열로 변환됩니다.


프로젝트 구조

shop.js       게임팩 18종 + 순수 로직 (필터·정렬·장바구니·결제 계산)
state.js      서버 단일 상태 + 액션 + 구독. 모든 변경이 여기를 지난다
mcp.js        MCP SDK 툴 정의, Streamable HTTP 트랜스포트
server.js     정적 파일 + /events(SSE) + /action + /mcp
index.html    UI 전체 + WebMCP 툴 등록 (Bootstrap 5 · Bootstrap Icons, CDN)
covers/       게임팩 패키지 이미지 18장
test.js       node test.js

shop.js 는 순수 함수라 서버와 테스트가 함께 씁니다. 브라우저는 렌더링에 필요한 만큼만 가져다 씁니다.


보안

MCP Streamable HTTP 스펙의 요구사항을 구현했습니다.

  • Origin 헤더 검증 — DNS 리바인딩 공격 차단. 허용 목록 외 Origin 은 403

  • 루프백 바인딩127.0.0.1 만 LISTEN. 모든 인터페이스에 노출하지 않음 (HOST 환경변수로 변경 가능)

  • 입력 검증 — zod 스키마

  • 레이트 리밋 — 툴 호출 60회 / 10초

정적 파일 서빙은 경로 탈출을 차단하고, 요청 본문은 1MB 로 제한합니다. 결제는 데모이므로 카드번호 입력 필드를 아예 만들지 않았습니다.


테스트

npm test

필터·정렬 경계, 재고 초과 거절 시 장바구니 불변성, 결제 검증, by 별 하이라이트 규칙, 커버 이미지 존재 여부를 확인합니다.

에이전트 없이 WebMCP 툴을 직접 실행해보려면 DevTools → Application → WebMCP 패널을 쓰면 됩니다. 툴 목록과 합성된 스키마를 보고, 파라미터를 입력해 Run tool 로 실행하고, 호출 로그를 확인할 수 있습니다.


제약

이 프로젝트는 프로토콜 비교를 위한 데모입니다.

  • 결제는 가짜입니다. PG 연동 없음, 실제 청구 없음, 가짜 주문번호만 발급

  • 재고가 차감되지 않습니다. GAMES 는 상수이고 결제가 재고를 건드리지 않습니다

  • 상태는 프로세스 메모리에 전역 1개. 서버를 재시작하면 초기화되고, 사용자 구분이 없어 모든 탭이 같은 장바구니를 봅니다

  • WebMCP 는 W3C Community Group Draft 이며 Chrome 플래그 뒤에 있습니다. API 가 바뀔 수 있습니다 (navigator.modelContext 는 Chrome 150 에서 deprecated → 이 프로젝트는 document.modelContext 사용)


왜 MCP 엔드포인트를 함께 두는가

WebMCP 툴의 기본 노출 대상은 스펙상 문서 자신, 같은 트리의 동일 출처 문서, 브라우저 내장 에이전트 세 가지입니다. exposedTo 로 작성자가 지정한 교차 출처 iframe 에도 열 수 있고, 페이지나 iframe 에 직접 박은 author-provided agent 도 툴을 발견하고 실행할 수 있습니다.

스펙이 정의하지 않는 것은 브라우저 밖 프로세스를 위한 전송 계층입니다. 불가능하다는 뜻은 아닙니다 — 확장 프로그램(페이지 툴을 로컬 MCP 서버로 중계)이나 CDP 기반 브리지로 외부 MCP 클라이언트를 연결하는 방식이 이미 널리 쓰입니다. 표준화되지 않았을 뿐입니다.

이 프로젝트가 서버측 /mcp 를 함께 두는 이유는 그 방식과의 트레이드오프 때문입니다.

확장/CDP 브리지

서버측 /mcp (이 프로젝트)

설치

확장 설치 필요

없음

수명

탭이 열려 있는 동안

서버가 사는 동안

상태

탭별

서버 공유 (모든 탭이 같은 장바구니)

툴 정의 위치

페이지

서버

두 방식은 배타적이지 않습니다. Chrome 문서의 권고도 "가장 효과적인 에이전트 애플리케이션은 MCP 와 WebMCP 를 모두 쓴다" 입니다.


이미지 출처

covers/ 의 패키지 이미지는 영문 위키백과 각 게임 문서의 박스아트입니다 (MediaWiki pageimages API, pilicense=any).

저작권은 닌텐도 및 각 퍼블리셔에 있습니다. 위키백과에서 비자유 저작물로 공정 이용되는 자료이며, 그 공정 이용 근거는 위키백과의 맥락에 한정됩니다. 이 저장소는 학습·시연 목적이며 상업적 이용이나 재배포를 의도하지 않습니다. 게임 제목과 상표 역시 각 권리자의 것입니다.

Related MCP Connectors

Related MCP Servers