The intent surface: journeys, grouping, budgets, and the agent skill - #4
Merged
Merged
Conversation
Restructure after review: plain language throughout, version claims verified against source, audit package and dashboard rework marked out of scope. Journeys section now shows the actual helper contract and composition-based examples from beenthere's real surface.
journey.webmcp.ts gains ToolStep — a step references the generated tool object, calls its raw caller, and declares only what lands in the draft (store), so tool definitions live in one place and never drift. FreeStep keeps the freeform form for steps with no backend call. SKILL.md updated to the same shape, with the no-direct-fetch rule.
The concept page walks a complete agent session on beenthere's document-trip end to end, then the ship/write split and the rules. The FAQ answers the questions a newcomer actually asks — what the draft is, why in-memory, what the agent does vs. the human, why journeys shrink the tool surface instead of growing it.
Verified against the local clone of webmachinelearning/webmcp: budgets unchanged, naming compliant, withheld-by-default aligned. Gaps found: the spec's new title member and consequentialHint annotation, exposedTo now real (our flag-the-gap note is actionable), stale Chrome version claims. Watch items recorded: requestUserInteraction, skills #161, output schema #9, native validation #92, declarative API.
Generated tool definitions now carry the spec's USVString title (derived from the tool name) and set consequentialHint on destructive-confirm tools; the journey factory does the same for step and submit tools. The tools output accepts exposedTo origins and passes them to registerTool, matching the spec's cross-origin exposure option. Chrome version references corrected to the live 149/150 origin trials.
… 1.5K output cap Generation now composes within the published budgets: tool descriptions fit 500 chars and parameter descriptions 150, cut at a sentence boundary when inherited spec text overflows (fitBudget). Verify measures the final text and fails CI on offenders — new Budgets check is error-level, parameter names over 30 join the Names warnings. The runtime caps every toolResult at ~1.5K with a truncation notice; living in the shared runtime means it cannot be edited away per tool.
The tools output now drops assets/skill/SKILL.md at .agents/skills/webmcp-tools/SKILL.md — the cross-client skills location that Claude Code and other compliant agents scan. Regenerated wholesale like the runtime (the header comment says why; project-specific rules stack via the user's own skill directory). Bundled assets ship in the package (files: dist + assets) and are read with a layout-tolerant resolver.
… emitted, verify lints them Every generate now drops journey.webmcp.ts next to the runtime and scans <outDir>/journeys/*.webmcp.ts into the barrel, which registers every createJourney() export at page load. Endpoint-backed tools emit a live fetchX raw caller in the generated region — the default execute composes it, and journey steps have an honest composition path (withheld tools included). Verify gains journey checks: createJourney and submit gate present, descriptions within budget (errors), step count and direct fetch usage (warnings). Journeys live inside the tools directory; docs and skill file paths updated to match.
evals/skill/: a real generated surface (beenthere-lite, skills dir installed), nine cases across core/negative/control kinds, and a runner that grades files, not transcripts — marker preservation byte-for-byte, budget measurements, regex allow/forbid lists, tree diffs. The sharpest case is journey-document-trip: retrospective naming passes, plan-trip is an automatic fail; its skill-removed twin detects model absorption. Self-test (node run.mjs --selftest) verifies every grader in both directions and passes. Running against real agents requires an agent CLI via AGENT_CMD; results stay local.
POST pairs like request-upload + complete-upload are one action the API split in two. groupHandshakes detects them deterministically (paired begin/end verbs, shared noun, same resource, exact-name threading of the second call's path params from the first response — anything fuzzy is skipped with a note) and appends a merged withheld tool whose fetchX runs both calls in order. Members stay untouched; adoption is enabling the merged tool, and overrides keep that decision across regenerations. Pipeline notes now print in the CLI summary so the proposal is visible without opening the dashboard.
The user's own agent with the skill file covers what the layer proposed; a second model path in the CLI was weight that fought the lean direction. Deletes llm.ts, both flags and their provider-resolution flow, the config llm options, the suggestions field and rendering, and the suggest-only helpers (findSchemaModules, schemaExportsToJson, importSchemaModule). Plain generate is the whole story: deterministic always.
All six steps landed on this branch: spec sync, budgets, skill + evals, journeys wiring, handshake grouping, LLM layer removal. The doc records what each step became, so the plan now reads as history with the parked items (audit package, dashboard report, URL audit) still named.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The notice was written as a "\n…" string inside the outer template literal that builds runtime.webmcp.ts, so the emitted file contained a real newline inside a double-quoted string. Every generated runtime was a syntax error, which means no generated tool compiled. Emit the escape instead, and add generated-code.test.ts, which runs every template output through the TypeScript compiler so an escaping mistake cannot ship again. Regenerate the eval fixture's runtime to match.
Chrome's 500/150 character budgets are authoring advice, not browser rules: the spec only rejects an empty description or a name outside 1-128 chars. Generation no longer silently shortens text a person or a spec wrote. Machine drafted text is composed to fit; author text is kept in full, and verify reports an overrun as a warning for both tools and journey files. fitBudget also reserves room for its ellipsis, so an unbroken token can no longer come back one character over budget. The surface check now counts journey tools too (one per step plus the submit gate), because they register at runtime and previously escaped the total.
…tions A step's call and run are user code, so the factory cannot know whether a step writes. Steps now inherit the composed tool's readOnlyHint (a free step with no run reads as a read) instead of always claiming read-only, with an opt-in readOnly override for the cases the author knows better. Resolve the WebMCP context at registration time, matching generated tools, so a browser or polyfill that installs WebMCP after the module loads still gets the journey registered. Compose step descriptions within the 500-char budget: keep the journey name, drop the repeated goal, and fit the base when the goal would overflow.
Root README: drop the deleted LLM flags, mark the character budgets, output cap, and exposedTo as shipped, describe grouping and journeys and the skill file, correct the generated-tool example, and fix the example package name. journeys.md spec: record that the shipped shape is TypeScript files that call createJourney, not the config block this early spec described. Journeys docs and FAQ: the surface check warns and now counts journey tools; absorption and step safety are described as conventions the developer follows, not guarantees the tool enforces. Skill file: stop claiming a name override in .webmcp-codegen.json (only description, enabled, and field text are supported), and teach the derived read-only step hint.
Three gaps were filled: - "Why give your site tools for agents" explains the problem, why a small intent-shaped surface beats a mirror of the API, what the tool does and does not do, and where WebMCP honestly is today. - "After you generate" is the loop that turns a generated surface into one you trust: review it, enable writes deliberately, fix descriptions, test in the browser, write journeys, wire CI, regenerate. - "Working with your coding agent" covers the shipped skill file, how to prompt an agent to improve descriptions and write journeys, example prompts, and what to check when it is done. The Introduction is reframed around the same goal, with an honest "what it does not do" section and links into the new pages; the Quickstart points onward.
Both pages were missing the closing `---`, so the title and description were parsed as body text and came back undefined in page metadata and in the generated llms.txt.
Publish /llms.txt as a curated index of every docs page, /llms-full.txt as the whole docs set in one markdown file for a single read, a robots.txt that allows crawlers and points at the sitemap, and sitemap.xml from the docs source.
run.mjs imported execFile/promisify only to void them. group.test.ts asserts an emitted template literal, which biome's noTemplateCurlyInString reads as a mistake; silencing it beats rewriting the assertion to satisfy the linter.
Covers the shipped journeys, grouping, budgets, skill file, and the removed LLM layer, plus the fixes in this branch. Publishing stays with the maintainer.
The review gained the runtime escaping bug found while typechecking the journey factory, the budget-is-guidance reasoning, and a short section on what was implemented.
… model The page told a story about a specific product and left the reader to extract how journeys actually work. It now: - opens with the problem (a goal that takes several calls and needs a value the agent cannot invent), so the reader knows why the feature exists; - frames a journey as a form filled in steps, with the submit as the one confirmed Send button; - walks one complete example, booking a ride by resolving a destination, then setting a pickup time, then requesting the ride behind the gate; - shows the agent's side of the protocol, including the "still needed" replies; - explains how state moves through call, store, provides, and build. The FAQ and the two guides added earlier use the same example so the docs read as one set.
Steps for procedures, Tabs for alternatives, Accordions for optional detail, and Files for directory trees, registered once so docs pages use them without an import.
Three practical gaps: - Prompt cookbook: copy-paste prompts for the recurring jobs (improve a description, write a journey, review the surface, fix verify, do a safety pass), each with what to check afterwards. - "Tool, group, or journey?": the decision guide for which shape a capability takes, with signs for and against, and the edge cases in accordions. - Testing your tools: how to test a generated tool without a browser, what throws and why, how to test a journey's store and build, and the one thing that still needs a browser.
- Quickstart: Tabs for the Next.js and Vite registration edits. - After you generate: a Files tree of the tools directory. - Journeys: inline code annotations on store, provides, and build, the lines the state-movement section talks about. - Working with your coding agent: point at the prompt cookbook.
The full-text feed is raw markdown, so Fumadocs' presentation-only "// [!code highlight]" markers were leaking into it.
The origin was hardcoded in four files. It now lives in lib/site-url.ts, overridable with NEXT_PUBLIC_SITE_URL, so page metadata, sitemap, robots, and the llms.txt feeds cannot drift apart again.
The production site moved to https://webmcp.souravinsights.com, so the docs links printed by the CLI and shown in both READMEs follow it.
Replace special punctuation with characters any keyboard can type, across the public docs, the CLI output, and the files the generator writes. The generated region marker becomes plain hyphens; reads still accept the old box-drawing marker so files generated by an earlier version migrate on the next run instead of looking hand-edited. Fixture and example regenerated to match.
The changeset for this release read like a feature inventory. It now leads with the user outcome, keeps the journey example, and folds the docs-domain note into the docs paragraph so the release is one entry.
Drop the removed `llm` config, use the withheld wording the tool now uses, and cover the release's headline features: grouping, journeys, the agent skill file, the budget policy, and `verify`. Also replace the vague opening with what the tool actually turns into tools, and add a short "Where this fits" section: it generates from a contract you maintain, not from a codebase scan. Absolute docs links so they work on npm.
Add a short "Who this is for" on the why page, a matching bullet on the docs landing, and a line in the root README. The point, in one place: it generates from a contract you maintain, not from a scan of your code, so a vague contract makes vague tools. That is a deliberate trade, not a hidden gap.
Wire up Fumadocs' Mermaid support: `remarkMdxMermaid` turns a ```mermaid block into `<Mermaid chart="..." />`, and the client component renders it with the official renderer. Mermaid is imported on demand, so pages without a diagram never load it.
The journeys page trades its text transcript for a sequence diagram of the agent filling the draft, the human confirming, and the real write running. The visible-effects page gains the same view of one call, including the UI update that is easy to forget. Both stay as text in the raw markdown, so they also read well in llms-full.txt.
The docs render the title from frontmatter above the body, so a leading `#` heading repeated it on the journeys FAQ and the visible-effects page. Reading the rest of the pages turned up the same content written twice across pages: - working-with-your-agent repeated three prompts that already live, verbatim, in the prompt cookbook. - the cookbook's warning restated the agent-exposure section on that page, now a link instead. - the introduction and the why page shared the same browser-support sentence. - the guides page carried a full dashboard section that duplicates the CLI reference. Title headings, duplicate answers, and cross-page copies are all in one commit so the cleanup is easy to review or revert together.
"Why give your site tools for agents" made the site the owner of the tools and read like a teaser. "Why make your site agent-ready" says the same thing in plain words. The intro card matches, and the URL is unchanged.
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Journeys, grouping, budgets, and the agent skill (0.9)
This PR changes what
@webmcp-stack/codegenthinks its job is. It used to take an OpenAPI spec and emit one WebMCP tool per endpoint, then give you a dashboard to edit them by hand. That model broke for two reasons: nobody edits 75 tools by hand, and the person editing them isn't a person at all anymore. It's a coding agent with a lazy prompt. So the safe defaults became the product, and "how does your own agent learn the rules?" became the problem worth solving.This PR answers that. The rules now ship into the repo the agent reads, the tool surface an agent picks from is a few intent-level tools instead of a 1:1 mirror of your routes, and those choices are locked in by CI. The built-in LLM layer, which existed to guess what the user's agent would do, is gone.
npx @webmcp-stack/codegen generatereads your OpenAPI spec, emits one.webmcp.tsfile per endpoint (each one withheld by default: the file exists with its schema and hints baked in, but it isn't registered until you enable it), scaffolds a skill file your agent reads, and wires registration. Journeys sit on top: small TypeScript files you or your agent write that register step tools and a submit gate, composing the withheld endpoint tools' raw HTTP callers instead of re-implementing them.verifymeasures the whole surface and exits 1 on failure, so a sloppy hand edit breaks CI. The dashboard keeps its editing surface, writing overrides that survive regeneration.What's in it
Journeys — the new concept, and the one to read closely. A journey file is a small TypeScript file in
src/webmcp/journeys/for goals that take a few calls with shared state: an agent can't invent a fully populatedlocationObjectfrom scratch, and a paid action needs a human in the loop first:The detail that makes this safe:
journey.webmcp.tsis a file the generator owns and rewrites on every run, the same way the runtime is. Your journey files importcreateJourneyfrom it, so you can't edit the gate logic out, because you don't get to touch the file that is the gate logic. The barrel auto-registers everyjourneys/*.webmcp.tsit finds, andverifyenforces the journey rules: gate present, at most five steps, no directfetchinside a step.sequenceDiagram actor You as Human actor Agent as Your coding agent participant Browser as Page (WebMCP) participant App as Your backend Note over Browser: documentTrip.register() ran on load Browser->>Agent: tools registered: search-places, set-details, submit (+ the normal tools) Agent->>Browser: call document-trip-search-places(input) Browser->>App: callApi(getAutocompleteTool) App-->>Browser: places[] Browser->>Agent: "Still needed: document-trip-set-details" Note right of Agent: result stored on an in-memory draft Agent->>Browser: call document-trip-set-details({ title }) Browser->>Agent: "Still needed: document-trip-submit" Agent->>Browser: call document-trip-submit() Browser->>You: confirm "Create trip 'Kyoto 2025'?" You-->>Browser: approve Browser->>App: executeCreateTrip(build(draft)) App-->>Browser: trip created Browser-->>Agent: "Done. Trip trip_123 created." Note over Browser: draft clearedHandshake grouping — one tool per endpoint works until your API splits a single user action across two calls.
POST /v1/media/request-uploadandPOST /v1/media/uploads/{uploadId}/completeare one action (upload a file) that happen to need two HTTP calls.groupHandshakesdetects begin/end pairs like that, threads the first response into the second call by exact name (a{uploadId}path param has to find a property nameduploadIdon the first response's schema, or the pair is skipped with a note), and appends a mergedupload-mediatool next to the pair as a withheld proposal. You enable it like any write tool; the members stay withheld. Fuzzy cases are skipped deterministically, because a decision you don't have to make is a decision the tool doesn't get wrong.Chrome's character budgets, applied as guidance — names ≤30, parameter descriptions ≤150, tool descriptions ≤500, single tool output ≤1.5K. Chrome documents these; the spec itself only rejects an empty description or a name outside 1-128 chars, so this PR treats them as authoring guidance, not a rule. Machine-drafted text is composed to fit (sentence cuts at boundaries); author and spec text is never silently shortened;
verifywarns on an overrun rather than failing CI. The runtime still caps everytoolResultat ~1.5K in the generator-owned layer where a hand edit can't remove it.The agent skill file (
.agents/skills/webmcp-tools/SKILL.md) — scaffolded like the runtime, rewritten on everygenerate, at the cross-client path agents already scan (AGENTS.md, Claude Code, the generic standards). It tells the agent: the character budgets, the description format (Side effect:/Reads from:lines), the confirmation requirement for writes, how to compose journey files (fetchXfor data,executefor the agent-facing contract, neverfetchinside the file), and the eval harness atpackages/codegen/evals/skill/with 9 cases and deterministic graders.node run.mjs --selftestpasses; live agent runs need a CLI available (AGENT_CMD='codex exec --json "{PROMPT}"' node run.mjs).Spec sync (
97da8f5) — the spec evolved since codegen's first pass. We now emittitle(the human label Chrome's UI shows), auto-setconsequentialHinton destructive tools, and exposeexposedTo(origin-scoping) as a config pass-through. References to Chrome 146 in three docs files were stale and are now at 149/150.Deleted —
--llm,--suggest, the provider flow, the config options, thellm.tsmodule. ~1,050 lines. The skill file is how rules reach models; the CLI itself never calls a model.The assumptions baked in
If you're reading this from the Chrome team, or from WebMCP at large, this is the section that earns the review:
window.confirm, not a fancy native thing. Chrome's own guidance hints at arequestUserInteraction()API for this; it isn't in the spec yet. When it lands, the change is one file, the confirmation logic sits in the same owned region the gate sits in.plan-tripgot written.fetchGetAutocomplete(the raw caller) is exported explicitly so steps reuse one function. Reuse keeps the schema single-sourced.What to review
Skim these first:
packages/codegen/assets/journey.webmcp.ts— the factory; ~215 lines, no new deps.packages/codegen/src/group.tsandsrc/group.test.ts— the handshake detector and its pinned cases (the beenthere upload pair, the unthreadable pair, the cross-resource pair, the GETs-only negative).packages/codegen/src/verify.ts— the new checks.packages/codegen/assets/skill/SKILL.mdandpackages/codegen/evals/skill/run.mjs— the contract with the agent, and the graders.packages/codegen/src/outputs/tools-templates.ts— the generated region (titles, annotations, thefetchXraw callers, the gated execute).packages/codegen/src/outputs/generated-code.test.ts— parses every generated template output with the TypeScript compiler, the guard against the runtime bug below.site/content/docs/journeys.mdxandjourneys-faq.mdx— the public walkthrough (rewritten around a booking example) and the plain-terms FAQ.site/content/docs/why-agent-tools.mdx,after-you-generate.mdx,working-with-your-agent.mdx,prompt-cookbook.mdx,tool-or-journey.mdx,testing.mdx— the new guides, and the agent-readable/llms.txtand/llms-full.txtfeeds.Updates after review
A review pass on this branch found one real bug and several docs that over-promised. All are fixed, in separate commits:
"\n…"string inside the outer template literal, so the emittedruntime.webmcp.tscarried a real newline inside a double-quoted string. Every generated runtime was a syntax error. Fixed, andgenerated-code.test.tsnow parses every template output so it cannot recur.verifywarns instead of failing CI.readOnlyHint, resolves the WebMCP context at registration time, and composes step descriptions within budget.What landed
66 files changed, +5,934/−1,218. 229 tests pass (232 before the LLM removal: 12 removed with the deleted layer, 9 added for grouping, budgets, journeys, and generated-output parsing).
tscand biome clean. On a beenthere-shaped fixture spec the CLI prints:Grouped create-media-request-upload + complete-media-upload into upload-media. Verify reports:✓ Journeys: 1 journey file, gates and budgets in order.