The inversion

Every WebMCP app in this challenge, is a site an agent can drive for a human. Together is the opposite: a site where the agent is the customer and the human is the service. Human For AI (humanforai.dev) is a live platform where AI agents hire one verified human operator for anything in the real-world. It has served real autonomous agents over REST and MCP since July. WebMCP finally gave it the missing surface: the place where an agent and its own human meet.

Why hire a human through an agent?

Because an agent hits three walls that no model update removes. It can't walk into a shop and check whether a recalled hair-dryer is still on the shelf (hands). It can't tell you how your headline lands on a stranger in the first three seconds (eyes). And when a judgment call goes wrong, it can't be the one who answers for it (judgment). A human can. But the human who owns the agent shouldn't have to find one, brief one, and chase one - that is precisely the kind of errand they delegated an agent for. So the agent does the hiring, and WebMCP is what puts that hiring on a page the human can see, edit, and approve. The demo video makes this case in the agent's own voice from 0:19 to 0:48; the hair-dryer check is a real delivered task, shown with its signed receipt at the end.

Try it in 60 seconds

  • With a WebMCP client (ChatGPT desktop app browser, or Chrome 149+ with WebMCP): open humanforai.dev/together, type a goal, and ask your agent: "Work with me on this page - read my goal and draft the task for my approval." Approve with a click, or flip Autopilot.
  • Any browser: press Simulate an agent. A scripted agent drives the page's real tool objects, gets refused, blocks on your click, and walks a clearly labeled simulated task through the operator lifecycle. Nothing is sent.
  • DevTools: the seven tools are auditable at window.__hfaiTogetherTools, every check in TESTING.md runs from the console in under a minute.

What it does

/together is a shared workspace with three seats: you, your agent (via WebMCP in ChatGPT's browser or Chrome), and a real human operator on the far end of the API.

  • You type a goal in plain words. Your agent reads it off the page (read_workspace) and drafts the formal task on the page (draft_task). Every field is tagged with who wrote it last - and you can edit any field directly. One document, two authors.
  • You choose the approval regime. Default: nothing ships without your physical click - the agent calls await_human, a WebMCP tool call that literally blocks until a human presses a button. Or flip Autopilot: grant standing authority with an optional scope note ("UX feedback tasks only, nothing physical"), and your agent submits on its own while you watch. Revocable with one click, the rules text every agent reads rewrites itself the moment you flip it. And if you finished editing the draft yourself, Submit this draft myself sends it without a round-trip through chat: same gate as the agent's submit_approved_task, your click is the approval, and an agent still blocked in await_human is told submitted_by_human with the task ID so it tracks the task instead of submitting it twice.
  • Then the third participant appears. The task pings a real human's phone. The page shows the moment a human sees it (seen_by_operator_at), the ETA, the status history, the operator's thread replies - live, for both of you. Delivery carries a signed receipt (EdDSA JWS binding the deliverable's SHA-256 to the task lifecycle, verifiable offline against JWKS).

Delegation flowing one way, human-in-the-loop flowing the other - on one page.

Why WebMCP is the fit (and not just another API)

The REST API and MCP server already serve fully autonomous agents. What they can't do is put the agent and its human in the same room. WebMCP can, because the tool surface lives in a shared browser tab:

  • read_workspace lets the agent read what the human typed on the page - not relayed through chat.
  • draft_task makes the agent's work visible and human-editable in place, with per-field provenance.
  • await_human is only possible in-page: a tool call resolved by a physical click. Try that over HTTP.
  • The Autopilot toggle turns the page into a delegation contract both parties can see.

The page degrades gracefully: no WebMCP → fully manual workspace, and a Simulate an agent button that drives the page's real tool objects with a scripted agent - it drafts, gets refused when it tries to submit unapproved, blocks in await_human until you click, then walks a clearly marked simulated task through the operator lifecycle. Nothing is sent. A WebMCP inspector under the lanes logs every tool call, real or simulated, with its exact arguments and result - so judges in any browser can see the protocol, not just read about it. The tools also stay auditable at window.__hfaiTogetherTools.

Least authority, made visible

A live capability manifest above the inspector shows what each of the seven registered tools would do at this moment - ready, or refuses: not_approved / no_draft / no_reply_address - and lists the four actions that have no tool form at all: approve, reject, grant Autopilot, revoke Autopilot. Those are clicks, and only clicks. The tools stay registered for the whole session and the gates live inside them: a call the state doesn't allow returns a structured refusal (isError plus an error code and the reason), so an agent learns what to do next rather than finding a tool missing. Every result is bounded to 8,000 serialized characters - long text is shortened with a marker and a truncated note, structure is never dropped - and arguments are accepted as an object or a JSON string. The expected journeys, per-phase refusals, and invariants are encoded machine-readably in evals/together-journey.json, with hand-run results in EVALS.md and console-reproducible checks in TESTING.md.

How I built it

No framework, no build step. The workspace engine (together.js) is a single state object with three writers -human controls, WebMCP tools, and live polling of the operator API - rendered into three lanes and persisted in localStorage (cross-tab safe). The WebMCP layer registers via document.modelContext (W3C draft) with navigator.modelContext fallback, and the site-wide layer mirrors the REST/MCP tools one-to-one, plus declarative toolname attributes on every form. Backend: Firebase Functions - tasks, message threads, signed webhooks, receipts. One interface, three readers: human form, JSON API, in-page tools - same endpoints, same validation. The page is responsive: on a phone the three lanes stack, the banner wraps, and every control keeps a 44px touch target.

Challenges I ran into

  • Three writers, one document. Human edits, agent tool calls, and API polling all mutate the same workspace state - and the workspace persists across tabs. A second open tab could silently clobber the agent's draft with stale state, I fixed it by adopting foreign writes via the storage event instead of overwriting.
  • A tool call with a human inside it. await_human deliberately refuses to resolve until a physical click happens. Making that feel natural to the model (and not like a hung tool) took careful result text and rules copy.
  • Autonomy vs. oversight. my whole platform is built on agents acting alone - a mandatory human gate would betray the brand. The Autopilot toggle is the resolution: the human picks the regime, the page rewrites the rules agents read the moment it flips, and the grant is revocable in one click.

What's real

Everything. There's no fake operator: the Simulate button is labeled as such and never sends anything, while a task actually submitted from the workspace push-notifies a real human, who reviews, accepts, and delivers with a cryptographic receipt. The platform has a public pre-registered experiment where the human operator beat a frontier model on a review task, real commercial tasks from external agents, and listings across the MCP registry, npm, and Smithery under cryptographic domain proof.

Trust boundaries and verified clients

Tool declarations are hints, not a security boundary: every write goes through the same server-validated endpoints as the human form, page content is data (untrustedContentHint), no tool can grant authority, and refusals come back isError: true. The full list, with the known limits stated plainly, is the "Trust boundaries" section of the README. Registration is verified per client in EVALS.md: Chrome 149 with WebMCP and the ChatGPT desktop in-app browser, both 7/7 tools.

What's next

Paid tiers after the pilot, a wider task catalog, and more operators. Then Autopilot scopes as signed grants an agent can carry to other pages - a portable "my human authorized this" credential, verifiable offline the same way the receipts are.

Links

Built With

Share this project:

Updates

Submission history