Interactive MCP server and UI widget that renders movie details from TMDB with the MCP Apps spec. The server runs on Cloudflare Workers; the UI is a sandboxed HTML resource served via ASSETS and rendered by MCP hosts.
- MCP server (
server/) exposing:ui://widget/movie-detail-widget.htmlresource (HTML + CSS + JS from ASSETS)- Tool
get-movie-detailthat searches TMDB by title and returns details + cast
- Widget UI (
web/) built with React/Tailwind:- Entry:
widget.html→movie-detail-widget.js/.css - MCP Apps lifecycle:
ui/initialize, tool input/result notifications,ui/open-linkfor showtimes - Showtimes link opens Google “<title> showtimes near me” via
ui/open-link
- Entry:
- Root landing page: friendly message at
/telling visitors to connect via/mcp
- Node 18+
- Wrangler CLI
- TMDB API token (v4 bearer) stored as a secret
npm installUse .dev.vars for local secrets:
TMDB_TOKEN=your_tmdb_bearer_token
Run dev server:
npm run devMCP endpoint will be at http://localhost:8787/mcp.
npm run web:buildOutputs to web/dist/:
movie-detail-widget.jsmovie-detail-widget.csswidget.html
These are served by the ASSETS binding for the MCP UI resource.
- Set the secret in your account:
wrangler secret put TMDB_TOKEN- Deploy:
npm run deployserver/lib/mcp.ts— registers resource + tool, uses ASSETS to serve built widget, pullstmdbTokenfrom env binding.server/index.ts— Hono entry, mounts/mcpand root landing page.web/src/movie-detail-widget.tsx— MCP Apps UI logic + render.web/vite.config.ts— builds widget assets with custom filenames for ASSETS.
- Tool:
get-movie-detail - Input:
{ "query": "<movie title>" } - Returns:
content: text fallbackstructuredContent.movie: movie payload (title, poster/backdrop URLs, runtime, genres, rating, cast, etc.)
- UI resource URI:
ui://widget/movie-detail-widget.html - MIME:
text/html+mcp - CSP: allows
https://image.tmdb.org/for posters/backdrops - Expects MCP Apps messages:
ui/notifications/tool-input/tool-input-partialui/notifications/tool-resultui/open-link(to open showtimes link)
Regenerate Cloudflare binding types if config changes:
npm run cf-typegen- Missing TMDB token: set
TMDB_TOKENin.dev.vars(local) and viawrangler secret put(prod). - Assets not loading: ensure
npm run web:buildwas run and ASSETS binding is present. - Host rejects
ui/open-link: host must support MCP Appsui/open-link; otherwise widget will show an error state.