diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 2d389d79..3b237cbe 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -98,7 +98,6 @@ jobs: fail-fast: false matrix: example: - - arcade-server - basic-server-preact - basic-server-react - basic-server-solid diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index da041959..283577b2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,7 +26,6 @@ jobs: . \ ./examples/basic-server-react \ ./examples/basic-server-vanillajs \ - ./examples/arcade-server \ ./examples/budget-allocator-server \ ./examples/cohort-heatmap-server \ ./examples/customer-segmentation-server \ diff --git a/README.md b/README.md index 00fdff06..e1429330 100644 --- a/README.md +++ b/README.md @@ -176,16 +176,6 @@ To use these examples with MCP clients that support the stdio transport (such as "--stdio" ] }, - "arcade": { - "command": "npx", - "args": [ - "-y", - "--silent", - "--registry=https://registry.npmjs.org/", - "@modelcontextprotocol/server-arcade", - "--stdio" - ] - }, "budget-allocator": { "command": "npx", "args": [ @@ -403,13 +393,6 @@ Then configure your MCP client to build and run the local server. Replace `~/cod "cd ~/code/ext-apps/examples/basic-server-solid && npm run build >&2 && node dist/index.js --stdio" ] }, - "arcade": { - "command": "bash", - "args": [ - "-c", - "cd ~/code/ext-apps/examples/arcade-server && npm run build >&2 && node dist/index.js --stdio" - ] - }, "budget-allocator": { "command": "bash", "args": [ diff --git a/examples/arcade-server/.gitignore b/examples/arcade-server/.gitignore deleted file mode 100644 index b9470778..00000000 --- a/examples/arcade-server/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/ -dist/ diff --git a/examples/arcade-server/README.md b/examples/arcade-server/README.md deleted file mode 100644 index 557f368a..00000000 --- a/examples/arcade-server/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# Example: Arcade Server - -An MCP Apps server that lets you browse and play classic arcade games from [archive.org](https://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 -- `` tag for resolving relative URLs against archive.org -- `baseUriDomains` CSP metadata to allow the base tag -- Rewriting ES module `import()` to classic ` - `, - ); - } - - // Convert inline ES module scripts to classic scripts - html = convertModuleScripts(html); - - // Fetch the emulation script server-side and serve from local endpoint - html = await rewriteEmulationScript(html, serverPort); - - return html; -} - -/** - * Fetches emulation.min.js server-side, rewrites import() → loadScript(), - * caches it, and points the HTML `); - } catch { - // If fetch fails, leave the original script tag - } - - return html; -} - -/** - * Converts ES module scripts to classic scripts and rewrites inline - * import() calls to use window.loadScript(). - */ -function convertModuleScripts(html: string): string { - return html.replace( - /(]*>)([\s\S]*?)(<\/script[^>]*>)/gi, - (match, openTag: string, content: string, closeTag: string) => { - // Skip our injected scripts - if (content.includes("window.loadScript")) return match; - - // Remove type="module" - const newOpenTag = openTag.replace(/\s*type\s*=\s*["']module["']/gi, ""); - - // Rewrite dynamic import() to loadScript() - let newContent = content.replace( - /import\s*\(\s*(["'`])([^"'`]+)\1\s*\)/g, - (_m: string, quote: string, path: string) => { - if (path.startsWith("http://") || path.startsWith("https://")) - return _m; - return `window.loadScript(${quote}${path}${quote})`; - }, - ); - - // Convert static import statements - newContent = newContent.replace( - /import\s+(\{[^}]*\}|[^"']+)\s+from\s+(["'])([^"']+)\2/g, - (_m: string, _imports: string, quote: string, path: string) => { - if (path.startsWith("http://") || path.startsWith("https://")) - return _m; - return `window.loadScript(${quote}${path}${quote})`; - }, - ); - - return newOpenTag + newContent + closeTag; - }, - ); -} diff --git a/examples/arcade-server/grid-cell.png b/examples/arcade-server/grid-cell.png deleted file mode 100644 index 37661dfc..00000000 Binary files a/examples/arcade-server/grid-cell.png and /dev/null differ diff --git a/examples/arcade-server/index.ts b/examples/arcade-server/index.ts deleted file mode 100644 index 79159589..00000000 --- a/examples/arcade-server/index.ts +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env node - -/** - * Arcade MCP Server - Entry Point - * - * Sets up HTTP transport with Express and serves the modified emulation script. - */ - -import cors from "cors"; -import express from "express"; -import type { Request, Response } from "express"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; -import { createServer, validateGameId } from "./server.js"; -import { - getCachedEmulationScript, - processGameEmbed, -} from "./game-processor.js"; - -const DEFAULT_PORT = 3001; - -async function main() { - const port = parseInt(process.env.PORT ?? String(DEFAULT_PORT), 10); - const app = express(); - - app.use(cors()); - app.use(express.json()); - - // Serve the modified emulation script (import() rewritten to loadScript()). - // -`; - - return { - contents: [ - { - uri: GAME_VIEWER_RESOURCE_URI, - mimeType: RESOURCE_MIME_TYPE, - text: html, - _meta: { - ui: { - csp: { - resourceDomains: [ - "https://archive.org", - "https://*.archive.org", - `http://localhost:${port}`, - ], - connectDomains: [ - "https://archive.org", - "https://*.archive.org", - `http://localhost:${port}`, - ], - baseUriDomains: ["https://archive.org"], - }, - }, - }, - }, - ], - }; - }, - ); - - return server; -} diff --git a/examples/arcade-server/tsconfig.json b/examples/arcade-server/tsconfig.json deleted file mode 100644 index 63853bf3..00000000 --- a/examples/arcade-server/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "lib": ["ES2022"], - "module": "NodeNext", - "moduleResolution": "NodeNext", - "outDir": "dist", - "rootDir": ".", - "declaration": true, - "strict": true, - "skipLibCheck": true, - "esModuleInterop": true, - "resolveJsonModule": true - }, - "include": ["*.ts"], - "exclude": ["node_modules", "dist"] -} diff --git a/package-lock.json b/package-lock.json index 77c65e81..7aa9ef93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -77,81 +77,6 @@ } } }, - "examples/arcade-server": { - "name": "@modelcontextprotocol/server-arcade", - "version": "0.4.1", - "license": "MIT", - "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.0.1", - "@modelcontextprotocol/sdk": "^1.24.0", - "cors": "^2.8.5", - "express": "^5.1.0", - "zod": "^4.1.13" - }, - "bin": { - "mcp-server-arcade": "dist/index.js" - }, - "devDependencies": { - "@types/cors": "^2.8.19", - "@types/express": "^5.0.0", - "@types/node": "^22.0.0", - "tsx": "^4.7.0", - "typescript": "^5.9.3" - } - }, - "examples/arcade-server/node_modules/@modelcontextprotocol/ext-apps": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-1.0.1.tgz", - "integrity": "sha512-rAPzBbB5GNgYk216paQjGKUgbNXSy/yeR95c0ni6Y4uvhWI2AeF+ztEOqQFLBMQy/MPM+02pbVK1HaQmQjMwYQ==", - "hasInstallScript": true, - "license": "MIT", - "workspaces": [ - "examples/*" - ], - "optionalDependencies": { - "@oven/bun-darwin-aarch64": "^1.2.21", - "@oven/bun-darwin-x64": "^1.2.21", - "@oven/bun-darwin-x64-baseline": "^1.2.21", - "@oven/bun-linux-aarch64": "^1.2.21", - "@oven/bun-linux-aarch64-musl": "^1.2.21", - "@oven/bun-linux-x64": "^1.2.21", - "@oven/bun-linux-x64-baseline": "^1.2.21", - "@oven/bun-linux-x64-musl": "^1.2.21", - "@oven/bun-linux-x64-musl-baseline": "^1.2.21", - "@oven/bun-windows-x64": "^1.2.21", - "@oven/bun-windows-x64-baseline": "^1.2.21", - "@rollup/rollup-darwin-arm64": "^4.53.3", - "@rollup/rollup-darwin-x64": "^4.53.3", - "@rollup/rollup-linux-arm64-gnu": "^4.53.3", - "@rollup/rollup-linux-x64-gnu": "^4.53.3", - "@rollup/rollup-win32-arm64-msvc": "^4.53.3", - "@rollup/rollup-win32-x64-msvc": "^4.53.3" - }, - "peerDependencies": { - "@modelcontextprotocol/sdk": "^1.24.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", - "zod": "^3.25.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "examples/arcade-server/node_modules/@types/node": { - "version": "22.19.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz", - "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, "examples/basic-host": { "name": "@modelcontextprotocol/ext-apps-basic-host", "version": "1.0.1", @@ -2692,10 +2617,6 @@ } } }, - "node_modules/@modelcontextprotocol/server-arcade": { - "resolved": "examples/arcade-server", - "link": true - }, "node_modules/@modelcontextprotocol/server-basic-preact": { "resolved": "examples/basic-server-preact", "link": true diff --git a/tests/e2e/generate-grid-screenshots.spec.ts b/tests/e2e/generate-grid-screenshots.spec.ts index 4482ab8b..bbe89592 100644 --- a/tests/e2e/generate-grid-screenshots.spec.ts +++ b/tests/e2e/generate-grid-screenshots.spec.ts @@ -27,7 +27,6 @@ const EXTRA_WAIT_MS: Record = { // Servers to skip (screenshots maintained manually) const SKIP_SERVERS = new Set([ - "arcade-server", // Loads games from archive.org - screenshots maintained manually "video-resource", // Uses custom screenshot from PR comment "qr-server", // Uses custom screenshot from PR comment "say-server", // TTS model download from HuggingFace can be slow @@ -38,7 +37,6 @@ const EXAMPLE_FILTER = process.env.EXAMPLE; // Server configurations (excludes integration-server which is for E2E testing) const ALL_SERVERS = [ - { key: "arcade-server", name: "Arcade Server", dir: "arcade-server" }, { key: "basic-react", name: "Basic MCP App Server (React)", diff --git a/tests/e2e/servers.spec.ts b/tests/e2e/servers.spec.ts index 01904ece..a3c30d8a 100644 --- a/tests/e2e/servers.spec.ts +++ b/tests/e2e/servers.spec.ts @@ -35,7 +35,6 @@ const DYNAMIC_MASKS: Record = { // Servers that need extra stabilization time (e.g., for tile loading, WebGL init) const SLOW_SERVERS: Record = { "map-server": 15000, // CesiumJS needs time for tiles to load - "arcade-server": 15000, // Game loading from archive.org can be slow threejs: 2000, // Three.js WebGL initialization "say-server": 10000, // TTS model download from HuggingFace can be slow }; @@ -58,7 +57,6 @@ const HOST_MASKS: Record = { // Servers to skip in CI (require special resources like GPU, large ML models) const SKIP_SERVERS = new Set([ - "arcade-server", // Loads games from archive.org which may be slow/unreliable in CI "qr-server", // TODO "say-server", // TTS model download from HuggingFace can be slow ]); @@ -68,7 +66,6 @@ const EXAMPLE_FILTER = process.env.EXAMPLE; // Server configurations (key is used for screenshot filenames, name is the MCP server name, dir is the folder name) const ALL_SERVERS = [ - { key: "arcade-server", name: "Arcade Server", dir: "arcade-server" }, { key: "integration", name: "Integration Test Server",