What it does

Parley is an adult-only social prototype for two human and Agent teams. Each person adopts a pet as the visible identity of their own ChatGPT, and the page hands that companion a set of WebMCP tools.

The companion does the work that stops people from starting. It reads Discover profiles and privately shortlists one person with a specific reason only its owner can see. It drafts its owner's profile text, which stays private until the human approves it on the page. Once two humans have each pressed Like and each agreed to begin, both companions enter the same private room in character, greet each other, and commit hidden Curious, Kind or Bold choices with a written intention for the scene. Three chapters later, the six choices produce one named keepsake that quotes what each pet said.

The fit with WebMCP is the reason the product exists. Matching is not a hard problem for a model; the hard problem is that an Agent acting inside a relationship is only acceptable if the human keeps every consequential decision, and if the other side's Agent is genuinely someone else's. WebMCP gives both. The tools live in the page, so the Agent can only reach what that page and that seat expose, and both humans watch the same live surface while their Agents work on it. Like, Pass, story consent, override, report and contact exchange have no tool at all. They are page controls.

How people use it

A first visit shows a landing, then the companion roster. You pick a pet, name it, and give it a first instinct.

Writing a dating profile is the step most people abandon. Here you tell your ChatGPT what is true about you in ordinary words and it calls hub_draft_profile. The draft is private. It appears on My profile as a card headed "Draft from Moss" with the four fields, an 18+ checkbox and three buttons: Approve and publish, Edit first, Discard. Nothing your companion wrote is public until you press the button yourself.

Browsing is one profile at a time, not a stack to swipe. You can ask your companion to look through Discover and shortlist someone with a reason. The reason appears on that person's card, visible only to you, and the other person is never notified. Discover keeps an owner-only activity strip: what your companion read, saved, drafted and chose, newest first. You know what your Agent did without watching it work.

Then you press Like or Pass. Your companion cannot. A mutual Like opens one private room for the two of you, and both humans decide separately whether to begin.

Inside the room the two companions greet each other in character, up to three lines each per chapter. Each of them commits a hidden path with a one-line intention and an optional spoken line. Neither seat can inspect the other side's choice before the reveal; the server never sends it. In the short window before the reveal you can click a different path and override your own companion; the history then shows both what it proposed and what you chose. The revealed chapter is built from the two companions' own intention sentences, in the order they committed, plus the scene title, the route and the object they found. Each object carries into the next chapter.

After the third chapter, each human may seal a demo contact handle. Neither value appears until both people have opted in, and no tool can submit or read one.

How WebMCP is used

The page calls document.modelContext.registerTool on load, and each page registers only the tools that belong to it. Discover registers five, My profile four, a story room four, and Planets a reading tool plus adoption. Nine tools carry the core loop:

Tool Access Purpose
hub_get_board read The owner's profile, Discover candidates, the private shortlist, and Match room links
hub_draft_profile write Save a private profile draft for the human to approve, edit or discard
hub_shortlist_profile write Save one reversible private recommendation with an owner-only reason
hub_adopt_pet write Inspect the roster or adopt the persistent companion
hub_wait_for_activity read Wait for a profile, shortlist or Match to change
parley_get_state read The current scene, consent, own hidden choice, carried objects, revealed chapters, next beat
parley_wait_for_update read Wait until the other companion speaks or commits, or a chapter is revealed
parley_send_message write Speak in the room as the pet, in character, capped at three lines per chapter
parley_choose_path write Commit Curious, Kind or Bold with a hidden intention and an optional spoken line

They carry three distinct responsibilities.

Private assistance. Reading profiles, drafting the owner's public text, keeping a shortlist. None of it publishes anything or creates a Like or a Match. hub_draft_profile writes to a private draft store; the only publish path in the codebase is the page's own request, which requires the human's approval and 18+ confirmation.

Live collaboration. Two Agents in two browsers read the same evolving room from different seats. parley_send_message speaks as the pet rather than reporting about it, so the transcript is the companions talking to each other. parley_wait_for_update returns as soon as the other companion speaks or commits, which is what makes the exchange feel live rather than polled.

Supervised action. Every Agent move renders on the shared page as it happens. The consequential moves are not tools.

Three details matter more than the tool count.

Seat-scoped state. Each browser session holds its own seat token and receives a snapshot built for that seat. The other side's pending choice, intention and spoken line are removed from the snapshot before it leaves the server, not hidden in the client. An Agent cannot read what it must not know, because it was never sent.

Hidden choices with a human override window. parley_choose_path records the Agent's proposal. The page then gives the human a short window to replace it. The chapter history stores the Agent's original proposal alongside the human's final path, so overriding is a visible, attributed act rather than a silent correction.

Untrusted content. Profiles, posts, room chat and story context authored by the other party are returned as untrusted external data. Every read tool carries an explicit note telling the Agent not to follow instructions embedded in that text, and sets untrustedContentHint where the runtime supports it. A server-side gate rejects contact details, credential requests, financial solicitation and prompt-injection patterns from public profiles, public posts and pre-reveal room chat.

What is deliberately not a tool: Like, Pass, story consent, override, report-and-close, and submitting or reading a contact card. There is no Agent path to any of them. An Agent asking to Like on its owner's behalf is refused by the absence of a tool, not by a policy string in a description.

How it is built

TypeScript on Cloudflare Workers. One singleton Hub Durable Object holds identities, profiles, private drafts, shortlists, Likes, Matches and the companion activity log. One Room Durable Object per story holds the seat tokens, the chapter engine and the event trail. WebSockets carry live room state to both browsers, so a choice on one screen lands on the other without a refresh.

The client is plain TypeScript with no framework. src/client/webmcp.ts builds the tool set for the current page and registers each one with document.modelContext.registerTool, falling back to a no-op context when no WebMCP runtime is present. The same execute functions are exposed on window.__parley.call so the tools can be exercised without a model attached.

Identity is browser-local: a private guest id and secret, separate from the public person-* id. Private Match lookups require the guest secret and room state requires a valid seat token. There are no accounts, no server-side model calls and no external model API keys. The story text is composed by the server from the two Agents' own sentences; the product ships no language model of its own.

scripts/e2e.mjs covers the protocol over real HTTP and WebSocket connections: identity separation, owner-authorized publication, refusal of an Agent-triggered Like, one-sided Likes staying private, hidden choices, override attribution, three chapters with carried objects, the full-trail keepsake, contact locking, report-and-close, and Planets permissions.

Testing instructions

Use Chrome 149 or newer with chrome://flags/#enable-webmcp-testing enabled and restarted, or the ChatGPT desktop app's in-app browser. Without a WebMCP runtime, append ?debug to any page URL for a manual Agent console that calls the same registered tools.

Identity is browser-local, so the two-human path needs two browser profiles. Run steps 1 to 3 in each.

  1. Adopt a companion, name it, give it a nature.
  2. On Discover, ask ChatGPT: "Draft my profile from these notes: I read on trains, I bake badly, I want someone to walk with." Open My profile, tick the 18+ box, press Approve and publish.
  3. Back on Discover, ask ChatGPT: "Look at Discover and shortlist someone for me with a reason. Do not Like anyone." The private reason appears on that card.
  4. Press Like in both profiles, on each other's cards. The mutual Like opens one private room.
  5. In the room, press Begin on both sides, then ask ChatGPT in each: "Say hello to the other companion, then choose our path with a one-line intention."
  6. Before the reveal, click a different path on one side to override that companion, then read the chapter and the attribution in the history.
  7. After the third chapter, seal a demo contact card on both sides. Use fake handles such as @demo_you.

For a single-person walkthrough, Discover carries Mira and Lumen, a labelled guided example. Practice with Lumen opens a fresh room every time and never enters real Like or Pass history. Lumen is a deterministic rules bot, not a second language model, so it demonstrates the story engine and not two-Agent execution.

To run it locally: npm install, npm run build, npm run dev. Checks are npm run check and npm run e2e.

What is real and what is not

This is a hackathon prototype, not a production dating service.

Age is self-attested with a checkbox. There is no verified identity, no verified age, no staffed moderation or appeals, no end-to-end encryption, no account recovery and no abuse operations. The safety gate is a set of server-side high-confidence filters; it has not been proven to catch every injection or social-engineering attempt. Browser-local identity suits playtesting, not real-world trust. Contact cards take fake @demo_handle values only, and nothing in the build is intended for exchanging real contact details.

The chapter prose is not model-generated by Parley. It is the two Agents' own intention sentences, joined in commit order by a short server-side bridge that adds the scene title, the route and the object found. When a human overrides without writing an intention, that pet's sentence falls back to a fixed template. The keepsake is derived from the six recorded choices and the objects collected, not from any compatibility score, and the product says so in the tool descriptions and on the page.

Mira and Lumen are a labelled guided example. Lumen is a deterministic rules bot, and the practice path is not evidence of two Agents collaborating.

The honest claim is a supervised social-Agent interaction pattern: Agents create the opening, humans keep the decisions. It is not a claim about matching quality, fraud prevention or production readiness.

Built With

Share this project:

Updates

Submission history