Journey questions, answered

The draft, the lifecycle, what the agent does and what you do, and the edge cases, answered in plain terms.

The journeys page explains the feature. This page answers the questions people ask once they read the code. None of them are dumb questions; they are the ones almost everyone has.

Is journey.webmcp.ts a journey?

No, and this is the most common confusion. That file never creates a journey. It is the factory: a reusable createJourney() function that journey files call. Think of a Lego baseplate and the models built on it. journey.webmcp.ts is the baseplate, shared and owned by the generator. book-ride.webmcp.ts is a model someone builds on it. The engine lives in one file; the journeys live elsewhere and are written per product.

What are the three parts, in plain terms?

  1. The draft is one plain object in memory, created when the page loads. Steps write into it; the submit reads everything out of it.
  2. Step tools are ordinary WebMCP tools, one per step (book-ride-resolve-destination). When the agent calls one, it does the step's work, stores the result on the draft, and replies "Stored. Still needed: X" or "Stored. The journey is ready." That reply is how the agent knows what to do next. There is no hidden orchestration; the agent works one call at a time and reads the messages.
  3. The submit gate is one final tool (book-ride-submit) that does four things in order: check nothing is missing and refuse if it is, ask the human to confirm, run the real write with the finished draft, and clear the draft.

Why is it called a "draft"?

Same reason as a draft email. It is the version in progress, built up piece by piece, and nothing is sent until the end. It is a draft because it is not the real thing: nothing reaches your backend from it until the submit runs.

What does "storing something in the draft" actually mean?

Adding a labeled value to that one object, like destination: {...} or pickupAt: "2026-03-14T19:30:00Z". It is like writing another line on a sticky note. There is no database, no file, and no network call in the storing itself.

What does "in memory" mean if I am not technical?

Picture a whiteboard in a room versus a filing cabinet down the hall. The whiteboard is right there, fast, and everyone in the room can read it, but the moment the room closes it is gone. The filing cabinet keeps things after everyone leaves. The draft is the whiteboard: it lives only inside the browser tab's running memory. Close the tab, refresh, or navigate away and it is wiped. Nothing durable, ever.

What is the shape of the draft?

The simplest possible: a flat bag of labeled values, for example { destination: {...}, pickupAt: "2026-03-14T19:30:00Z" }. Nothing enforces nesting, and any step can write any field.

Will it hold data reliably for a session?

It depends what you mean by session:

  • Within one continuous page view (tab open, agent working, no refresh): yes, completely. It is a variable in memory and nothing drops it mid-flow.
  • Across a reload, a closed tab, or a new tab: no, deliberately. It is never meant to outlive one uninterrupted visit.

How does it store results from the real backend?

Not every step touches the backend. A step like set-pickup-time has no logic; it stores whatever the agent passed in, verbatim, with no network call. A step like resolve-destination calls the app's real geocoder and stores the real answer. So the draft ends up holding a mix: some fields are "what the agent said", some are "what the server said". Either way, nothing is created or changed for real until the submit.

Why is the draft created only once per page load?

Because createJourney(), the function that sets up the draft, runs once at page load, not per attempt. There is one shared scratchpad per journey per page visit. That is fine for the realistic case: one agent, one user, one thing at a time. The unhandled edge case is two parallel attempts at the same journey on one page, which would share and overwrite the same draft. Accepted, not handled.

What is a "half-finished journey", and why would it happen?

The agent resolves the destination, then the person closes the laptop or the tab crashes, before the rest of the steps and the submit. Some steps done, nothing sent. Keeping the draft in memory means a reload always starts clean, with no stale, half-filled attempt from three days ago reappearing with an outdated result. Throwing away incomplete progress is the feature, not a gap.

Does a journey reuse the tools codegen already generated?

Yes, directly, and it never reimplements anything. A tool-backed step calls the same underlying request the standalone tool uses, and the submit's run is the existing write tool's execute. The journey wraps them with the gate and the confirmation. Bonus: a risky write like request-ride, normally generated withheld, never has to be enabled as a standalone tool at all. The journey's submit becomes its only door, with the prerequisite checks and a human confirmation built in. Wrapping a risky tool in a journey is a safer way to expose it than enabling it directly.

How does the agent know these tools exist?

There is no special channel: ordinary WebMCP discovery. On page load, every registered tool, including journey steps and submits, lands in one flat list the browser exposes. The agent asks the page what it can do and gets the whole list, with no formal distinction between a normal tool and part of a journey. The only grouping signal is in the text: each step's name carries the journey prefix (book-ride-...), and each description ends with something like "Part of book-ride: Book a ride to a destination the user gives." The agent reads that and infers the connection, the same way it figures out anything else. The factory stitches it mechanically, so it cannot be forgotten.

What does the agent do, and what do I do?

The agent's job is orchestration and conversation: work out what to call next from the replies, and ask the user for anything it cannot get from the backend. Your job is exactly one thing: the final yes or no on the confirmation dialog before anything real happens. The agent cannot click that dialog for you. A human click is the only way through.

Can the agent call the steps out of order?

The order is not enforced by code, so an agent could call set-pickup-time before resolve-destination. It mostly self-corrects: a step that needs an earlier step's draft field fails with a readable message naming what is missing, which nudges the agent back. One honest caveat: that self-correction depends on the step failing usefully when called early, which is on whoever writes the journey, not something the shared machinery guarantees.

What happens if I decline the confirmation?

The agent is told "The user declined this action", and the draft is left untouched, not wiped. You can change your mind, adjust something, and submit later without starting over.

Why can't codegen just generate the journeys for me?

Because the flow is not in your API spec. An OpenAPI file says POST /v1/rides exists; it does not say that booking a ride is really resolve the destination, then set the pickup time, then confirm. It also cannot know that your product wants a person to approve the ride rather than let an agent request it directly. That knowledge lives in the product. A CLI guessing flows produces plausible-sounding garbage, tools that pass every mechanical check and are still wrong. So codegen stops at a hint in its report and the bundled skill file teaches your own coding agent how to write the journey file well. Your agent can ask you about the product; the CLI cannot.

Won't journeys make my tool list even bigger?

They can, if you let them. verify counts journey tools in the surface total, one per step plus the submit gate, and warns when the whole surface grows past what is good for agents. It is a warning, not a failed build: the browser has no limit on how many tools you register, only agent quality does.

Keeping the surface small is a convention you follow, not something the generator does for you. A read tool that a journey uses still registers on its own, so if it only makes sense inside the flow, withhold it in .webmcp-codegen.json or exclude it with safety.exclude. Writes reachable through a journey submit stay withheld as standalone tools unless you enable them, so leave them withheld and the submit is their only door.

What if two steps use the same draft field name?

The second overwrites the first, silently. The draft is a flat bag with no protection, so give each step's fields distinct names when you write the journey.