Inspiration
Product teams that expose refunds, account deletion, payouts, and administrative approvals to agents face a concrete operational problem: which actions an agent may take depends on live application state, and mistakes can be irreversible.
PIP the Mug is an executable reference for that contract. It derives an agent's capabilities from live state, validates targets with state-built input schemas, pauses irreversible work through requestUserInteraction when the client supports it, and returns requires_user_action without changing application state when it does not. It also keeps an auditable Agent-versus-Human record.
WebMCP lets the page itself express these rules. DOM automation alone cannot declare or audit them. The HR satire makes the mechanism inspectable in about one minute.
Agents are already on the web, and they are bad at pretending to be a mouse. Selector-driven automation couples an agent to the page's structure and makes its capabilities hard to discover and constrain safely.
WebMCP fixes this. The page gives the agent named, typed tools. Availability follows live state. Anything irreversible stops for a human, and both parties read the same record of what happened.
I wanted all of that visible on one screen. A real HR product would have been dreary. A mug that has held the same coffee for three weeks is not.
What it does
PIP the Mug is a top-down desk. Eight objects are employees with personnel files. You are upper management. Your browser agent is HR.
The page registers nine tools on document.modelContext:
list_staffget_personnel_filewrite_reviewput_on_pipresolve_pippromoterelocateterminateget_org_chart
Availability follows state. resolve_pip is registered only while at least one PIP is open, and terminate refuses alumni and anyone promoted in the current quarter.
PIP the Mug supports requestUserInteraction as its preferred confirmation path. A supporting client collects the human decision through that callback.
In the current Codex WebMCP shim, the callback is unsupported. The safe fallback returns requires_user_action, opens Form SEP-1 visibly on the page, and leaves employment unchanged. The WebMCP workflow stops and asks Upper Management to decide on the page.
This fallback is a cooperative handoff for clients without the callback, not a security boundary against separate DOM automation. The activity log attributes the request to Agent and the confirmation to Human, so the audit trail shows where the WebMCP workflow stopped and the person took over.
Everything also works by click and drag. If WebMCP is unavailable, a banner says so and the desk still runs. State is stored as one JSON document in localStorage.
How I built it
I built PIP the Mug with client-side TypeScript and Vite. It has no backend, model, analytics, or login.
Reviews, PIPs, and separation notices render as paperwork. Agent tool calls open the matching personnel file so a human watching the tab can follow along.
I generated the sprites with PixelLab. The application deploys as a static site to Vercel through vercel.json.
Challenges I ran into
The WebMCP getter moved from navigator.modelContext to document.modelContext. I detect both and prefer the document version.
State-dependent availability is easy to get wrong. If every state change tears down every tool, the agent can lose its tool list midway through a run. I rebuild only when a tool's registered metadata changes:
- Name
- Title
- Description
- Input schema
- Annotations
Read tools that can return text written by a human or agent carry untrustedContentHint.
The confirmation path was the hardest part. requestUserInteraction is not available in every client, and the current Codex WebMCP shim throws when it is called. My fallback had to leave employment unchanged, return a structured requires_user_action result, and open exactly one SEP-1 request.
Execution matters too. An agent that mutates JSON while the panel still says "Select an employee" looks like plumbing. Papers had to land on the desk, personnel files had to open, and captions had to remain visible.
Accomplishments I'm proud of
I ran the final verified demo through ChatGPT's in-app browser.
All nine tools appeared. relocate moved Phone Charger into the drawer on the open tab. Writes opened the personnel file and created visible paperwork, so I could watch HR work.
terminate returned requires_user_action. The activity log recorded the request as Agent and the confirmation as Human.
The joke survived contact with a real agent. The confirmation before termination is not a modal staged for screenshots. It is the visible handoff between what the WebMCP tool prepares and the decision Upper Management makes next.
What I learned
Tool descriptions are the agent UX. I learned to write them like instructions, not API comments.
If a write is irreversible, I mark it and return control for a human decision. If a tool should not exist in the current state, I do not register it. I also assume the runtime's confirmation API may be unavailable and design the fallback first.
A screen recording only works if the desk moves when the agent does. Logs alone are not enough.
What's next for PIP the Mug
I plan to keep PIP the Mug online as a compact reference and testbed for state-gated, human-approved WebMCP actions.
The pattern is the point:
- On a storefront,
terminatebecomesissue_refund. - In account settings, it becomes
delete_account. - In an admin panel, it becomes any destructive administrative action.
The agent gathers the file, drafts the paperwork, and requests a decision. Semantic tools replace DOM guessing. Availability follows state. Humans approve irreversible writes, and both sides share an audit trail.
The mug was just easier to feel sorry for.
Built With
- elevenlabs
- json-schema
- localstorage
- openai-imagegen
- pixellab
- svg
- typescript
- vercel
- vite
- web-audio-api
- webmcp
Log in or sign up for Devpost to join the conversation.