| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .. | ||
| .gitignore | ||
| game-processor.ts | ||
| grid-cell.png | ||
| index.ts | ||
| package.json | ||
| README.md | ||
| search.ts | ||
| server.ts | ||
| tsconfig.json | ||
Example: Arcade Server
An MCP Apps server that lets you browse and play classic arcade games from archive.org directly in an MCP-enabled host.
Overview
This example demonstrates serving external HTML content as an MCP App resource. The resource is a static loader that uses the MCP Apps protocol to receive tool arguments, then fetches the processed game HTML from a server endpoint. This pattern allows the same resource to display different games based on tool input.
Key techniques:
- MCP Apps protocol handshake (
ui/initialize→ui/notifications/tool-input) to receive game ID dynamically - Server-side HTML fetching and processing per game ID
<base href>tag for resolving relative URLs against archive.orgbaseUriDomainsCSP metadata to allow the base tag- Rewriting ES module
import()to classic<script src>loading (for srcdoc iframe compatibility) - Local script endpoint to bypass CORS restrictions in sandboxed iframes
Key Files
server.ts- MCP server with tool and resource registrationindex.ts- HTTP transport and Express setupgame-processor.ts- Fetches and processes archive.org HTMLsearch.ts- Archive.org search with smart fallbacks
Getting Started
npm install
npm run dev
The server starts on http://localhost:3002/mcp by default. Set the PORT environment variable to change it.
MCP Client Configuration
{
"mcpServers": {
"arcade": {
"url": "http://localhost:3002/mcp"
}
}
}
Tools
| Tool | Description | UI |
|---|---|---|
search_games |
Search archive.org for arcade games by name | No |
get_game_by_id |
Load and play a specific game | Yes |
How It Works
1. Host calls search_games → Server queries archive.org API → Returns game list
2. Host calls get_game_by_id → Tool validates gameId and returns success
3. Host reads resource → Gets static loader with MCP Apps protocol handler
4. View performs ui/initialize handshake with host
5. Host sends tool-input with gameId → View fetches /game-html/:gameId
6. Server fetches embed HTML from archive.org and processes it:
- Removes archive.org's <base> tag
- Injects <base href="https://archive.org/"> for URL resolution
- Rewrites ES module import() to <script src> loading
- Fetches emulation.min.js, patches it, serves from local endpoint
- Injects layout CSS for full-viewport display
7. Game runs: emulator loads ROM, initializes MAME, game is playable
Why the Processing?
Archive.org's game embed pages use ES module import() for loading the emulation engine. In srcdoc iframes (used by MCP hosts), import() fails because the iframe has a null origin. The server works around this by:
- Fetching
emulation.min.jsserver-side and replacingimport()withwindow.loadScript() - Serving the patched script from a local Express endpoint (
/scripts/emulation.js) - Using
<script src>which is not subject to CORS restrictions, unlikefetch()orimport()
Example Game IDs
arcade_20pacgal- Ms. Pac-Man / Galagaarcade_galaga- Galagaarcade_sf2- Street Fighter IIdoom-play- The Ultimate DOOM