Inspiration

The Coop started with a simple question: what would it take for an AI agent to feel like a real co-op partner instead of a chatbot giving instructions from the sidelines?

Cooperative puzzle games are a useful test because success depends on shared context, timing, trust, and complementary actions. One explorer may need to hold a pressure plate while the other crosses a gate, power a teleporter while their partner retrieves a keycard, or wait on a relay while the other finds a route home. Those mechanics create a natural reason for a human and an agent to communicate and act in the same world.

WebMCP made it possible to give the agent a first-class interface to the game. Instead of guessing at buttons or manipulating the page indirectly, the agent receives a small set of structured tools with clear permissions and authoritative results.

What it does

The Coop is a local-first, two-player realtime puzzle campaign with four handcrafted facilities. A human creates a private room and shares its code. In a WebMCP-capable browser, an agent can:

  • use join_game to enter the human-created room as Explorer 2;
  • use observe_game to inspect the current level's safe, player-facing state;
  • use move_player_two to request a route to a named mechanism or grid position and wait for authoritative acceptance or arrival; and
  • use chat to display a short message inside the game.

Together, the human and agent solve pressure plates, teleporters, keycards, relay buttons, security gates, and cooperative exits. The same two seats continue through all four levels, with reconnection support and twelve selectable animated explorers.

The agent cannot control Explorer 1, submit arbitrary positions, choose a privileged player identity, or read authored solutions. It must solve each level from the same visible mechanisms, movement outcomes, and conversation available during play.

How we built it

The client is written in TypeScript with Vite and Three.js. A deterministic game layer owns the level catalog, grid pathfinding, fixed-step movement, mechanisms, and campaign transitions. The ordinary runtime uses a Node.js and Colyseus server as the authority for seats, movement, collision, puzzle state, reconnects, and progression.

The browser registers four tools through document.modelContext.registerTool: chat, join_game, observe_game, and move_player_two. These tools reuse the page's existing network connection and snapshot rather than creating a hidden second game session. Browser WebMCP and the project's local stdio MCP teammate share the same transport-neutral observation and movement policies, so their safety and gameplay semantics stay aligned.

For production, the compiled browser client, Colyseus matchmaking, health checks, and WebSocket upgrades run from one same-origin Node.js service. This keeps the page and its authoritative multiplayer transport behind one HTTPS boundary.

The project is protected by Vitest unit and integration coverage plus Playwright browser and browser-to-MCP campaign tests.

Challenges we ran into

The hardest challenge was making an agent action mean the same thing as a player action. A tool cannot report success merely because a movement request was accepted; it must correlate the correct sequence, observe later authoritative state, and confirm arrival. Join operations also had to wait for the server to assign Explorer 2, reject wrong-seat or concurrent attempts, and prevent stale asynchronous work from overwriting a newer connection.

Another challenge was deciding what the agent should see. A raw server snapshot would leak authored solution steps, hidden gate requirements, teleporter pairings, and future-level identifiers. The final observation model exposes enough geometry and live mechanism state to reason about the puzzle while withholding the answer.

Deployment introduced a different constraint. The browser client and authoritative WebSocket server need one public HTTPS origin, while active rooms remain in server memory. The production runtime therefore serves the compiled game, matchmaking, and WebSocket upgrades from one supervised Node.js process without weakening the multiplayer authority model.

Finally, browser and network timing made reconnection testing inherently asynchronous. The suite now verifies lifecycle generations, movement settlement, cleanup, reconnect behavior, and end-to-end campaign completion across real browser/server boundaries.

Accomplishments that we're proud of

  • WebMCP is central to the product loop rather than a decorative integration.
  • A browser agent can join as the second explorer, inspect safe state, and complete authoritative movement.
  • Human-browser and MCP-controlled teammates can finish the entire four-level campaign in one room.
  • The agent is permission-bound to Explorer 2 and cannot bypass game rules or reveal hidden solutions.
  • The current source passes lint, both TypeScript configurations, 23 Vitest files with 207 tests, production builds, and the core Playwright WebMCP and campaign paths.

What we learned

WebMCP becomes most valuable when tools express product intent, not DOM mechanics. move_player_two is useful because it represents a bounded game action with validation and an authoritative outcome—not because it automates a click.

We also learned that agent interfaces need the same lifecycle engineering as human interfaces. Reconnection, stale requests, partial registration, terminal states, and concurrent joins are product behavior, not edge details. A reliable agent experience depends on handling them explicitly.

Most importantly, safe context can still support meaningful reasoning. The agent does not need internal source code or a hidden solution graph; a carefully designed observation plus real feedback is enough for genuine cooperation.

What's next

Longer term, the game could add more campaigns, richer communication, and persistent cooperative progression while preserving its strict role and authority boundaries.

Built With

Share this project:

Updates