DEV Community

Sandeep Chakravartty
Sandeep Chakravartty

Posted on

Beyond DOM Scraping: Building "THE LAST TERMINAL" with WebMCP

Project Title: THE LAST TERMINAL — WebMCP Escape Room

GitHub Repository: https://github.com/scha54/WebMCP

Live Demo: https://webmcp-blush.vercel.app/


1. Executive Summary & The Problem with Web Agents Today

For the past several years, autonomous browser agents have interacted with web applications primarily through DOM scraping and visual inspection. A typical AI browser workflow involves taking high-resolution screenshots, feeding them into a Vision-Language Model (VLM), predicting pixel coordinates or CSS selectors, and firing synthetic click and keypress events.

This approach suffers from critical flaws:

  • Fragility: Minor CSS or markup refactors instantly break agent workflows.
  • Token Inefficiency: Uploading multiple high-res DOM trees and screenshots consumes tens of thousands of LLM tokens per action.
  • Latency: Visual feedback loops take seconds per step.
  • Ambiguity: Agents must "guess" button intents without formal input parameter types.

WebMCP (Web Model Context Protocol) is an emerging browser standard that solves this. Instead of forcing AI agents to reverse-engineer visual UIs, WebMCP allows web applications to directly register machine-readable tools (navigator.modelContext.registerTool).

To demonstrate this paradigm shift, we built THE LAST TERMINAL—a polished, browser-based cyberpunk escape room where human players and AI agents collaborate to solve interconnected facility puzzles using shared WebMCP tool capabilities.


2. System Architecture: The Shared Business Logic Principle

The core architectural principle behind THE LAST TERMINAL is Unified Execution Logic. The application never duplicates business logic for human interactions vs. agent interactions.

                 ┌──────────────────────────┐
                 │     WEBMCP AGENT /       │
                 │     DEMO SIMULATOR       │
                 └────────────┬─────────────┘
                              │
                      WebMCP Tool Calls
                              │
                              ▼
 ┌───────────────────────────────────────────────────────────┐
 │                   THE LAST TERMINAL                       │
 │                                                           │
 │  ┌─────────────────────────────────────────────────────┐  │
 │  │                 src/lib/webmcp/                     │  │
 │  │   tools.ts · schemas.ts · registry.ts              │  │
 │  └──────────────────────────┬──────────────────────────┘  │
 │                             │                             │
 │                             ▼                             │
 │  ┌─────────────────────────────────────────────────────┐  │
 │  │                  src/lib/game/                      │  │
 │  │   gameEngine.ts · gameState.ts · puzzles.ts         │  │
 │  └─────────────┬─────────────────────────┬─────────────┘  │
 │                │                         │                │
 │                ▼                         ▼                │
 │  ┌───────────────────────────┐ ┌───────────────────────┐  │
 │  │     Human Visual UI       │ │   Agent Activity      │  │
 │  │  (Facility Map & Systems) │ │   & Tool Trace Log    │  │
 │  └───────────────────────────┘ └───────────────────────┘  │
 └───────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

When a human user flips physical switches on the Power Control UI, the component triggers:

restorePower(['C', 'A', 'D', 'B'], 'human');
Enter fullscreen mode Exit fullscreen mode

When an AI agent invokes the WebMCP tool restore_power, the WebMCP execution context routes directly to the identical function:

restorePower(['C', 'A', 'D', 'B'], 'agent');
Enter fullscreen mode Exit fullscreen mode

Both invocations mutate the central GameState, trigger real-time UI updates, emit Web Audio synthesized feedback, and push a structured entry to the Agent Activity Feed.


3. WebMCP Tool Suite (10 Exposed Capabilities)

The application exposes 10 structured tools representing realistic facility subsystems:

# Tool Name Description JSON Input Schema
1 get_game_state Retrieves system lock statuses, discovered clues, and active view {}
2 inspect_system Inspects facility subsystems (security, power, archive, etc.) { "system": "surveillance" }
3 read_log Reads facility logs by ID (LOG-2049) { "logId": "LOG-2049" }
4 inspect_surveillance Inspects CCTV camera feeds (CAM-07) for frame anomalies { "cameraId": "CAM-07" }
5 decode_message Decodes encrypted ciphers into override candidates { "message": "SURVEILLANCE_ANOMALY" }
6 restore_power Restores auxiliary power using quad-switch sequence { "sequence": ["C", "A", "D", "B"] }
7 unlock_security Unlocks security terminal with 4-digit passcode { "code": "7319" }
8 open_archive Mounts archive database vault (requires auxiliary power) {}
9 inspect_archive Queries archive records matching search terms { "query": "exit protocol" }
10 unlock_exit Disengages emergency exit pneumatic doors { "code": "7319" }

JSON Schema & Code Example: restore_power

export const RESTORE_POWER_SCHEMA = {
  type: 'object',
  properties: {
    sequence: {
      type: 'array',
      items: { type: 'string' },
      description: 'Ordered list of switch labels, e.g. ["C", "A", "D", "B"].',
    },
  },
  required: ['sequence'],
  additionalProperties: false,
};

// WebMCP Tool Definition
{
  name: 'restore_power',
  description: 'Attempt to restore auxiliary power grid using an ordered 4-switch sequence.',
  inputSchema: RESTORE_POWER_SCHEMA,
  execute: (input: { sequence: string[] }, source = 'agent') => restorePower(input.sequence, source),
}
Enter fullscreen mode Exit fullscreen mode

4. Prerequisite-Driven State Machine

WebMCP tools should not be omnipotent "cheat codes." To demonstrate realistic agent reasoning, tools enforce environment prerequisites:

export function openArchive(source: ToolCallSource = 'agent') {
  // Prerequisite Guard
  if (currentGameState.power === 'offline') {
    const error = {
      code: 'PREREQUISITE_NOT_MET',
      message: 'Facility archive requires auxiliary power. Restore power first.',
    };
    recordToolCall('open_archive', {}, { error }, false, source);
    return { success: false, error };
  }

  currentGameState.archive = 'unlocked';
  // ... state progression
}
Enter fullscreen mode Exit fullscreen mode

If an agent attempts open_archive() while power is offline, WebMCP returns a structured JSON error response code PREREQUISITE_NOT_MET. The agent reads this structured output, inspects logs to discover that power must be restored, and calls restore_power first.


5. WebMCP Tool Registration & Browser Hydration

To register tools safely in React environments without re-registering on component re-renders, THE LAST TERMINAL implements an initialization guard module in src/lib/webmcp/registry.ts:

let initialized = false;

export function registerWebMCPTools(): boolean {
  if (typeof window === 'undefined') return false;
  if (initialized) return isWebMCPAvailableInBrowser;

  try {
    // Polyfill window.__webmcp for browser inspection and agent simulators
    (window as any).__webmcp = {
      tools: WEBMCP_TOOLS,
      callTool: async (name: string, input: any, source = 'agent') => {
        const tool = WEBMCP_TOOLS.find((t) => t.name === name);
        return await tool.execute(input, source);
      },
    };

    // Native WebMCP Imperative API
    const nav = navigator as any;
    if (nav.modelContext && typeof nav.modelContext.registerTool === 'function') {
      WEBMCP_TOOLS.forEach((tool) => {
        nav.modelContext.registerTool({
          name: tool.name,
          description: tool.description,
          inputSchema: tool.inputSchema,
          execute: async (input: any) => await tool.execute(input, 'agent'),
        });
      });
      isWebMCPAvailableInBrowser = true;
    }

    initialized = true;
  } catch (e) {
    console.error('Failed to register WebMCP tools:', e);
  }

  return isWebMCPAvailableInBrowser;
}
Enter fullscreen mode Exit fullscreen mode

6. Live Agent Activity Feed & Execution Trace Inspector

For hackathon judges and developers, transparency is vital. The interface provides two real-time inspection features:

  1. Agent Activity Feed: A live terminal log rendering request/response flow:
   23:51:04  AGENT  → inspect_surveillance("CAM-07")
   23:51:05  SYSTEM ← clue discovered: 7319
   23:51:09  AGENT  → unlock_security("7319")
   23:51:09  SYSTEM ← SECURITY UNLOCKED
Enter fullscreen mode Exit fullscreen mode
  1. Full Agent Trace Modal: A complete JSON log inspector displaying timestamps, source identifiers (human, agent, simulator), JSON inputs, return objects, and execution durations.

7. Zero-Dependency Audio & UI Polish

  • Web Audio API Synthesizer: Uses standard browser AudioContext to generate retro cyberpunk typing clicks, success chimes, failure buzzers, and power-up sweeps without hosting external MP3 files.
  • Demo Agent Simulator: Provides a 90-second deterministic agent automated execution mode so judges can experience the full room walkthrough without requiring external AI API keys or specialized browser extensions.

8. Setup, Testing & Deployment

Repository Setup

git clone https://github.com/scha54/WebMCP.git
cd WebMCP
npm install
Enter fullscreen mode Exit fullscreen mode

Running Tests

# Run isolated Game Engine test suite
npm run test:engine
Enter fullscreen mode Exit fullscreen mode

Production Build & Vercel

# Next.js static build check
npm run build

# Deploy to Vercel
vercel --prod
Enter fullscreen mode Exit fullscreen mode

9. Conclusion: The Future of Agentic Web Interfaces

WebMCP transforms web applications from passive visual UIs into agentic APIs. By exposing structured tools alongside standard visual components, websites become directly operable by AI models with 90%+ token reduction and near-zero latency.

THE LAST TERMINAL proves that building WebMCP-compatible web apps is clean, robust, and framework-agnostic.

  • Check out the source code on GitHub.
  • Explore the live demo and test the WebMCP tools yourself!

Top comments (1)

Collapse
 
marcusykim profile image
Marcus Kim

Routing both the physical power switches and the restore_power tool into the same restorePower function is the strongest architectural choice here; it keeps the human and agent experiences from drifting into two products. The PREREQUISITE_NOT_MET response for open_archive, paired with the timestamped trace modal, also gives agents a debuggable path through the state machine instead of a hidden failure. As this pattern moves beyond an escape room, I'd treat every tool like a public API: validate inputs at execution time, define authorization and idempotency semantics, and keep the trace useful without leaking sensitive state-the JSON.