Vercel Apps MCP Server (vercel-apps.py)
The infrastructure built to test the Issue #38 hypothesis. Before you can ask whether an LLM can solve a Sudoku puzzle through state and actions alone, you need a working system that gives it both — live app state readable via MCP, and a mechanism to issue actions back into the running app. vercel-apps.py is that system. Lives in devtools-playground [private].
Architecture
Claude Desktop
├── MCP Client (stdio) — tool/resource discovery via FastMCP
└── Iframe — renders apps, calls HTTP endpoints
vercel-apps.py (dual-server)
├── MCP Server (stdio/FastMCP) — tools: open_funnel(), open_sudoku(), etc.
│ resources: state://, vercel://, ui://
└── HTTP Server (port 3102/FastAPI) — JS/CSS serving, API proxy, state tracking
External Apps (Vercel-hosted, all Stencil except juris-sudoku)
├── wc-funnel.vercel.app
├── wc-autos.vercel.app
├── wc-sudoku.vercel.app
├── wc-tinker.vercel.app
└── juris-sudoku.html (local, Juris.js)
Key features
- State tracking — apps declare expected localStorage keys in HTML metadata; the server captures reads/writes via a proxy pattern and exposes them as
state://vercel/{app}resources - Event bus — mitt.js pub/sub under
window.protoAppfor real-time notifications and toast feedback - API proxying — fetch calls intercepted and routed through localhost:3102 to bypass CORS in the iframe context
- External JS/CSS — all glue code served as external files (faster iteration, better DevTools debugging, 28% smaller server code)
Why dual-server?
MCP stdio transport is required by Claude Desktop for tool/resource discovery. A separate HTTP server is required by the iframe for loading external scripts, proxying API calls, and receiving state updates from the browser. Direct HTTP between browser and server avoids MCP protocol overhead for high-frequency state updates.
Status relative to the hypothesis
State observation is fully working — the LLM can read the live Sudoku board via state://vercel/sudoku at any point. The remaining piece is invoke_action: once that's wired up, the full experiment becomes runnable. The LLM will have everything it needs to attempt a solve with zero DOM knowledge — just the board state and a declared action to place a digit. (Status as of March 2026.)