Inspiration

Team Rocket started as six small AI builds for the DC DevFest 2026 Buildathon, where we took third place. When we heard about the OpenAI WebMCP Challenge we could not help ourselves. Every one of those builds already had a human interface worth using, and WebMCP asked a question we wanted to answer: what does it take to hand that same interface to an agent without taking it away from the person?

The answer we wanted was not a separate API. It was the same screen, driven from either side.

What it does

Team Rocket Command Center is six AI apps behind one Next.js site: a kanban board whose cards are segments of one effort-weighted progress bar, a group restaurant picker decided by counting swipes, a policy diff that ranks changes by who they land on, a resume-to-posting gap finder with a two-week plan, an ambient note taker that files what you say, and a reader that illustrates a chapter scene by scene. Each runs on the visitor's own Gemini key.

Every build is drivable by an AI agent through WebMCP. Each page registers its actions as tools on document.modelContext, 44 in all, and every tool calls the same function the button calls, so a person and an agent are always looking at the same page. An "Agent is driving" pill in the header names the running tool. A WebMCP button lists what an agent could do on the current page before one arrives. The full surface, with schemas, lives at teamrocket.website/webmcp and is rendered from the same definitions the pages register.

Anything destructive stops at an in-page confirm bar and waits for a human. No tool can read, accept or set an API key.

How we built it

A single client hook, useWebMCPTools, registers a page's tools while it is mounted and unregisters them through the registration's AbortSignal on unmount. It reads document.modelContext, the current spec and Chrome 153+, falls back to navigator.modelContext for Chrome 149 to 152, and degrades to a console.debug where neither exists so the site works in every browser.

Each build keeps a static tools.js with names, descriptions and JSON Schemas, and wires handlers in its component next to the state they touch. Handlers read live state through a ref, so a tool always sees the current board, deck or workspace without re-registering on every render. ok() and fail() build the result shape; failures always carry a next-step hint and nothing ever throws.

Four site-wide tools live in the shell: list the builds, open one, check which keys are set as booleans, and open the key panel for the human to type into. One tool is declarative: the name form in This or That carries toolname, tooldescription, toolautosubmit and a toolparamdescription, and answers agent-invoked submits with respondWith().

Destructive tools call gate(), which blocks on a confirm bar in the shell and resolves when the human presses Confirm or Cancel, the agent aborts, or a minute passes. The toolactivated and toolcancel events drive the header pill. Everything is committed in small steps in the open, MIT licensed.

Challenges we ran into

The spec moved under us. Articles still said navigator.modelContext and unregisterTool; the current draft says document.modelContext with an AbortSignal, and Chrome 149 and Chrome 153 disagree. We read the Chromium IDL rather than the blog posts and support both.

Agents arrive through different doors. The Codex Chrome extension drives the DOM and never saw our tools, while Codex's in-app browser called them directly. The first agent to reach the site used the buttons, which is exactly why the human UI had to keep working.

That agent also found a real bug. Giving plan() parameters for the tool meant React handed it the click event when a person pressed the button, and the event's DOM node broke JSON serialisation. One-line fix, and a good reminder that adding an agent path can break the human one.

Smaller ones: React's compiler lint rejects setting state in effects, so adopting saved state after hydration needed a useSyncExternalStore pattern instead; and transcribing a three-minute demo in one pass drifted ten seconds by the end, so captions are now generated in silence-aligned chunks.

Accomplishments that we're proud of

  • 44 tools across six real multi-stage pipelines, not a to-do list, each with the screen as the record of what the agent did.
  • A safety model you can see: the pill, the confirm bar, and zero ways for a tool to touch a key.
  • A tool catalog generated from the same code that registers the tools, so the documentation cannot drift from what is live.
  • A pattern small enough to copy: one hook, one tools file per page, handlers beside their state.
  • A clean lint run, a compile-checked build, and a commit history that shows the work as it happened.

What we learned

WebMCP is at its best when it is a second door into an existing room rather than a new building. Because our tools call the button functions, every agent action is visible, undoable where the UI allows it, and testable by clicking. The places where we were tempted to add tool-only shortcuts were the places bugs appeared.

Descriptions are the interface. An agent decides what to call from the description alone, so "when to use this" matters more than "what this does", and every error message is a chance to steer the next call.

And the confirm bar earned its place. Watching an agent stop and wait for a person on camera made the case for human-in-the-loop better than any paragraph could.

What's next for Team Rocket Command Center

Re-record the six walkthroughs against the current builds, and add an agent-driven clip for each the way Taskboard has now. Give the site's own player a proper caption track. Add outputSchema to the tools once the spec lands it, and revisit readOnlyHint and consequentialHint as agents start honouring them. Then keep using the site as what it has become: a worked example anyone can fork to wire their own app the same way.

Built With

Share this project:

Updates

Submission history