Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
95cac90
Create 2026-09-07-what-to-double-down-on.md
SouravInsights Sep 7, 2026
abdebae
to be added
SouravInsights Sep 9, 2026
c2b2c42
Sharpen direction doc: concrete journey machinery, skill file, eval plan
SouravInsights Sep 10, 2026
3f81790
Switch journeys to composition: steps inherit generated tools
SouravInsights Sep 10, 2026
d9f68e2
Add journeys docs: full-session walkthrough plus a plain-terms FAQ
SouravInsights Sep 10, 2026
4a85bc2
Sync with the official WebMCP spec (main @ 97da8f5)
SouravInsights Sep 10, 2026
eb05e7a
Emit title and consequentialHint, add exposedTo option (spec sync)
SouravInsights Sep 10, 2026
b37e089
Enforce Chrome's character budgets: 500/150 in generation and verify,…
SouravInsights Sep 10, 2026
3d86331
Scaffold the skill file at .agents/skills on every generate
SouravInsights Sep 10, 2026
ec5d1a2
Wire journeys: factory scaffolded, barrel registers them, raw callers…
SouravInsights Sep 10, 2026
5272d59
Add the skill-file eval harness: fixture, cases, deterministic graders
SouravInsights Sep 10, 2026
d064101
Group handshake endpoints into one coarse tool (the anti-1:1 step)
SouravInsights Sep 10, 2026
f024cc0
Remove the LLM layer (--llm, --suggest)
SouravInsights Sep 10, 2026
0beff21
Mark the direction doc's order of work shipped
SouravInsights Sep 10, 2026
4564f4f
Create 2026-09-11-pr-4-intent-surface.md
SouravInsights Sep 11, 2026
77616e5
Update 2026-09-11-pr-4-intent-surface.md
SouravInsights Sep 11, 2026
114c84e
fix(generated output): keep the 1.5K truncation notice on one line
SouravInsights Sep 11, 2026
a6699e3
fix(audit): treat character budgets as guidance, count journey tools
SouravInsights Sep 11, 2026
049a29c
fix(journeys): honest step hints, late-bound context, bounded descrip…
SouravInsights Sep 11, 2026
822809b
docs: bring the existing docs back in line with the shipped surface
SouravInsights Sep 11, 2026
95a918c
docs: add the missing guides for why, what happens next, and prompting
SouravInsights Sep 11, 2026
8ea1fbf
docs(site): close the frontmatter on devtools and troubleshooting
SouravInsights Sep 11, 2026
d404fc2
docs(site): make the docs agent-ready
SouravInsights Sep 11, 2026
7b13c7c
chore: remove dead code in the eval runner and silence a false positive
SouravInsights Sep 11, 2026
89d18b7
chore: add a changeset for the intent surface
SouravInsights Sep 11, 2026
cda5978
docs(reviews): record the post-review findings and the fixes made
SouravInsights Sep 11, 2026
dc1faad
docs(journeys): rewrite around a relatable example and a clear mental…
SouravInsights Sep 11, 2026
19105cc
docs(site): register the standard Fumadocs components
SouravInsights Sep 11, 2026
6c45c8b
docs: add the prompt cookbook, shape guide, and testing guide
SouravInsights Sep 11, 2026
5331339
docs: put the standard components to work in the guides
SouravInsights Sep 11, 2026
c9d74d7
docs(site): strip code annotations from llms-full.txt
SouravInsights Sep 11, 2026
dc6d90d
docs(site): centralize the site origin and move to the new domain
SouravInsights Sep 11, 2026
f75b905
chore: point the CLI and README docs links at the new domain
SouravInsights Sep 11, 2026
962f2dd
docs: keep the docs and generated output to plain ASCII
SouravInsights Sep 12, 2026
f780c30
chore: write the release notes in terms of what users get
SouravInsights Sep 12, 2026
7f2655b
docs: refresh the npm README for the current surface
SouravInsights Sep 12, 2026
b4dcdc1
docs: say who the tool is for, and why it generates from contracts
SouravInsights Sep 12, 2026
a59271a
docs(site): render Mermaid diagrams
SouravInsights Sep 12, 2026
2974724
docs: add a journey sequence and a tool-call lifecycle diagram
SouravInsights Sep 12, 2026
8eefb43
docs: remove duplicate headings and repeated content across the docs
SouravInsights Sep 12, 2026
1a49cad
docs: retitle the why page to say what it is about
SouravInsights Sep 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .changeset/intent-surface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
"@webmcp-stack/codegen": minor
---

**Generate a small, intent-level surface, and teach your own coding agent the rules.**

This release changes what `@webmcp-stack/codegen` thinks its job is. It still writes safe, typed tools from your OpenAPI spec. What is new is the shape of the surface: instead of a one-to-one mirror of your routes, you get a few intent-level tools, actions your API split across calls, and journeys for goals that take several steps.

**Journeys: a real goal, not just an endpoint.** Some things a user asks for cannot be one tool call. Booking a ride is find the destination, set the pickup time, then confirm. A journey is a small file in `src/webmcp/journeys/` that composes the tools you already generated into one flow, with a shared draft and a single confirmed write:

```ts
export const bookRide = createJourney({
name: "book-ride",
goal: "Book a ride to a destination the user gives",
steps: {
"resolve-destination": {
tool: geocodeAddressTool,
call: (input, signal) => fetchGeocodeAddress(input as never, signal),
store: (place) => ({ destination: place }),
provides: ["destination"],
},
"set-pickup-time": {
description: "Set when the user wants to be picked up.",
input: { type: "object", properties: { pickupAt: { type: "string" } }, required: ["pickupAt"] },
provides: ["pickupAt"],
},
},
submit: {
description: "Request the ride and show the driver's details.",
build: (draft) => draft as RequestRideInput,
run: executeRequestRide,
},
});
```

`generate` scaffolds the `journey.webmcp.ts` factory next to the runtime, the barrel registers every journey file it finds, and `verify` lints them. The submit gate and the confirmation live in a file the generator owns and rewrites, so a journey file cannot edit them out.

**One tool for an action your API split in two.** When a begin/end pair like request-upload plus complete-upload is really one action, the generator merges them into a single withheld tool and threads the first response into the second by exact name. A pair it cannot thread is skipped with a note, never guessed.

**A skill file for your coding agent.** Every run writes `.agents/skills/webmcp-tools/SKILL.md`, where Claude Code, AGENTS.md, and the generic standard already look. It teaches the naming rules, the description budgets, the execute contract, and the journey pattern, so your own agent extends the surface in the shape this tool expects. The repo also ships an eval harness that runs prompts against a fixture and grades the result, so a wording change can be measured instead of guessed at.

**Your descriptions are never silently shortened.** Chrome's 500 and 150 character budgets are authoring guidance, not rules the browser enforces. Generation composes its own text to fit, keeps your text and your spec's text in full, and `verify` warns on an overrun instead of blocking. A description that reads well no longer gets cut down to pass a counter.

**Current with the WebMCP spec.** Generated tools carry `title`, the human label native UIs show, destructive tools carry `consequentialHint`, and the `tools` output accepts `exposedTo` to scope which origins your tools are shared with.

**The CLI no longer calls a model.** `--llm` and `--suggest` are removed, along with the provider flow and its config options. If you used them, the skill file is the replacement: it teaches your own coding agent the rules, and a plain `generate` never touches the network.

**Docs.** The journeys guide is rewritten around a relatable example, with new guides for why this matters, what to do after you generate, working with your coding agent, a prompt cookbook, choosing between a tool, a group, and a journey, and testing your tools. The docs site publishes `/llms.txt` and `/llms-full.txt` for agents, and the CLI's docs links point at the new home, https://webmcp.souravinsights.com.
54 changes: 34 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div align="center">
<a href="https://webmcp-stack.vercel.app">
<a href="https://webmcp.souravinsights.com">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./brand/logo-mark.svg">
<source media="(prefers-color-scheme: light)" srcset="./brand/logo-mark-light.svg">
Expand All @@ -8,15 +8,15 @@
</a>
<h1>webmcp-stack</h1>
<p><strong>The open-source developer stack for WebMCP.</strong></p>
<p>Today: codegen. The goal is the whole agent-surface lifecycle in one stack: Generate, Understand, Review, Test, Control, Observe, Secure.</p>
<p>Today: codegen. It generates a safe, reviewable agent surface from the contract you already have, and it is built to grow into the rest of the lifecycle.</p>
<p>
<a href="https://www.npmjs.com/package/@webmcp-stack/codegen"><img alt="npm version" src="https://img.shields.io/npm/v/@webmcp-stack/codegen?style=flat-square&labelColor=0a0b0f&color=58a6ff"></a>
<a href="./LICENSE"><img alt="MIT license" src="https://img.shields.io/badge/license-MIT-blue?style=flat-square"></a>
</p>
<p>
<a href="https://webmcp-stack.vercel.app/docs">Docs</a> |
<a href="https://webmcp.souravinsights.com/docs">Docs</a> |
<a href="https://www.npmjs.com/package/@webmcp-stack/codegen">npm</a> |
<a href="https://webmcp-stack.vercel.app/brand">Brand</a> |
<a href="https://webmcp.souravinsights.com/brand">Brand</a> |
<a href="./docs/about.md">About</a>
</p>
</div>
Expand All @@ -35,6 +35,8 @@ npx @webmcp-stack/codegen generate

No install, no config for the first run. It detects your app, writes one `.webmcp.ts` file per tool, and adds the registration call to your entry file (additive edits, always reported; if it can't find the entry point, it prints the two lines for you to paste).

It generates from the contract you maintain, not by scanning your app and guessing at intent. A vague spec or schema makes vague tools, so the source is the part worth getting right. If you have an OpenAPI spec or maintained schemas, this is built for you; if not, writing that contract is the first step, and the `schema` source lets you declare tools by hand in the meantime.

```bash
npx @webmcp-stack/codegen dev # local dashboard: browse, edit, toggle, and test tools
npx @webmcp-stack/codegen verify # check the tool set against the standard; exits 1 on errors
Expand All @@ -48,7 +50,9 @@ You can, and it works. What you get back is different every time, and nothing ch

- **Decides what agents may do.** Every endpoint is classified read, write, or destructive from the HTTP verb, corrected when the name disagrees (`POST /orders/{id}/cancel` is destructive, `POST /search` is a read). Reads work immediately. Everything else is generated but not registered, so enabling a write is a deliberate edit. Webhooks are skipped, auth and admin endpoints are flagged, and anything it cannot classify starts disabled.
- **Asks the user before any mutation.** Write and destructive tools confirm each call with the user (a plain dialog you can replace). The confirmation lives in the generated region of the file, so it cannot be edited away and survive regeneration.
- **Writes the text agents read.** Constraints become sentences ("A number from 30 to 600."), names come from intent (`generate-story`, not `post-trips-trip-id-story-generate`), and every description says what the tool returns. Your own text always wins; machine-written text is marked and flagged in the audit.
- **Writes the text agents read.** Constraints become sentences ("A number from 30 to 600."), names come from intent (`generate-story`, not `post-trips-trip-id-story-generate`), and every description says what the tool returns. Your own text always wins; machine-written text is marked and flagged in the audit. Chrome's character budgets are treated as guidance, not law: machine text is composed to fit, author text is never silently shortened, and `verify` warns on an overrun.
- **Groups actions the API split.** A begin/end pair like `request-upload` plus `uploads/{uploadId}/complete` is one action, not two. The generator detects the pair, threads the first response into the second by exact name, and adds one withheld coarse tool next to the members. A pair it cannot thread is skipped with a note.
- **Scaffolds journeys and an agent skill.** `journey.webmcp.ts` plus `journeys/*.webmcp.ts` express a multi-step flow: a page-scoped draft, one tool per step, and a submit gate that refuses until every step is done and confirms with the human before the real write. The barrel registers them and `verify` lints the files. `.agents/skills/webmcp-tools/SKILL.md` teaches your own coding agent the rules.
- **Says what can be trusted.** Free-text outputs get `untrustedContentHint`; PII-looking fields are named in the report and in a comment in the file; mutating tools on authenticated endpoints carry a session warning; `execute` calls your real endpoint, so your own validation still runs.
- **Annotates real forms.** A tool that maps to a visible `<form>` can annotate it in place, so the agent fills the same controls the user sees, and the user reviews and submits the write.

Expand All @@ -65,11 +69,23 @@ A real tool from a real app: `create-trip`, one of 70+ tools generated for [been
*/
export const createTripTool = {
name: "create-trip",
title: "Create Trip",
description: "Create a new trip. Returns the trip.",
inputSchema: createTripInputSchema, // title, dates, location, theme, field notes
annotations: { readOnlyHint: false, untrustedContentHint: true },
annotations: {
readOnlyHint: false,
untrustedContentHint: true,
consequentialHint: false,
},
};

// Journeys and your own code compose this raw caller; executeCreateTrip wraps it
// in the agent-facing result shape.
export async function fetchCreateTrip(input: CreateTripInput, signal?: AbortSignal) {
const data = await callApi("/v1/trips/", { method: "POST", body: { ... }, signal });
return data;
}

// A write tool, so it is withheld: the registration is generated but commented
// out, including the built-in user confirmation, until you enable it:
// const confirmed = await requestUserConfirmation(
Expand All @@ -79,9 +95,8 @@ export const createTripTool = {

export async function executeCreateTrip(input: CreateTripInput, signal?: AbortSignal) {
return toolDisabled("create-trip.webmcp.ts");
// Uncomment to go live:
// const data = await callApi("https://api.beenthere.page/v1/trips/", { method: "POST", ... });
// return toolResult(data);
// Uncomment to go live, and uncomment the registration above:
// return toolResult(await fetchCreateTrip(input, signal));
}
```

Expand All @@ -90,9 +105,9 @@ The bar the ecosystem is converging on (Chrome's WebMCP best-practices and tool-
| The bar | Today |
|---|---|
| Verb-first, intent-shaped names, 30 characters or fewer | Enforced on every run |
| Descriptions say what the tool does and when, positively, within 500 characters; every parameter described within 150 | Assembled on every run; length budgets measured by `verify` next |
| Outputs within 1.5K characters; errors that help recovery; user-written content marked `untrustedContentHint` | Errors and annotations enforced; output budget next |
| Exposure as a decision: `readOnlyHint`, registration only where usable, `exposedTo` origin scoping | Annotations and withheld-by-default enforced; `exposedTo` lands as the runtime stabilizes |
| Descriptions say what the tool does and when, positively, within 500 characters; every parameter described within 150 | Assembled on every run; author text is never shortened, and `verify` warns on overruns |
| Outputs within 1.5K characters; errors that help recovery; user-written content marked `untrustedContentHint` | Errors, annotations, and the output cap enforced; the cap lives in the generated runtime |
| Exposure as a decision: `readOnlyHint`, registration only where usable, `exposedTo` origin scoping | Annotations and withheld-by-default enforced; `exposedTo` is a config pass-through to registration |
| The human in the loop: visible page effects, confirmation on consequential actions | Confirmation enforced, in the generated region where it can't be edited away; visible-effect hook scaffolded |
| No steering: descriptions never instruct the agent or encode flow control | Flagged in the audit |
| The schema is not the security boundary; the app still validates at run time | Stated in the output; `execute` calls your real endpoint |
Expand All @@ -101,24 +116,23 @@ The bar the ecosystem is converging on (Chrome's WebMCP best-practices and tool-

## Changing things later

- **`.webmcp-codegen.json`**: per-tool overrides for descriptions, names, enabled state, fields. Applied last, so your text always wins.
- **`.webmcp-codegen.json`**: per-tool overrides for descriptions, enabled state, and field text. Applied last, so your text always wins.
- **Dashboard** (`dev`): edit descriptions, toggle tools, run tools against real endpoints. Writes back to the overrides file.
- **Audit**: problems reported in plain language every run: missing descriptions, mislabeled verbs, PII in outputs, agent-instruction smells, oversized surfaces. Errors block file writing; warnings do not.
- **`verify`**: the standard, checked locally. Built for CI.
- **LLM help if you want it**: `--suggest` proposes tools worth declaring, `--llm` drafts descriptions. Advisory only: it never classifies risk, never changes exit codes, and a plain `generate` never makes a network call.
- **Skill file**: `.agents/skills/webmcp-tools/SKILL.md` teaches your own coding agent the rules (naming, description budgets, journeys). Regenerated on every `generate`; add your own skill directory to stack project-specific rules on top.

## Where this is going

Codegen first:

- **More sources.** OpenAPI and validation schemas today, tRPC on the list. The rule holds: contracts, not codebases, and the CLI never scans app code.
- **Journeys.** One tool per CRUD operation is rarely the right shape; agents do better with a few coarse, intent-level tools. Declare a flow once (draft store, step tools, a submit gate), run it in the dashboard, review the generated Mermaid diagram.
- **The dashboard becomes the review surface.** LLM suggestions arrive as an accept/reject queue that writes to the overrides file. In progress now.
- **Evals for the LLM layer.** A prompt change becomes a visible regression, not a vibe.
- **The dashboard becomes the review surface.** A browse-and-score report over the generated surface, with the editing UI kept for the overrides it writes. Parked until the audit package exists.
- **Skill-file evals.** Shipped at `packages/codegen/evals/skill/`: a prompt set, a generated fixture, deterministic graders, and a control case that runs the sharpest prompt with the skill removed, so you can tell when a model has absorbed the rules.

Then the stack around it: **audit** (point it at a URL, get a report on the surface a visiting agent would find) and **telemetry** (how agents actually use your tools). The goal is one stack where each tool covers one stage of the lifecycle and they compound. The bet underneath: websites are growing an agent-facing surface the way they grew APIs, and that surface needs the same kind of tooling, with higher stakes, because the caller is a model acting as your user, inside your page.

The guarantees hold through all of it: the repo pins the WebMCP draft it targets and watches for spec drift; your overrides, execute bodies, and review decisions survive every regeneration; breaking changes print the exact fix. Deterministic where possible, LLM where useful, honest always.
The guarantees hold through all of it: the repo pins the WebMCP draft it targets and watches for spec drift; your overrides, execute bodies, and review decisions survive every regeneration; breaking changes print the exact fix. Deterministic where it can be, honest always.

## Principles

Expand All @@ -134,7 +148,7 @@ The guarantees hold through all of it: the repo pins the WebMCP draft it targets
| `packages/codegen` | `@webmcp-stack/codegen` | The CLI and the generation pipeline: sources (OpenAPI, validation schemas), outputs, the safety audit, the dev dashboard. |
| `examples/openapi-petstore` | private | Example app with tools generated from the Petstore OpenAPI spec. |
| `site/` | private | Landing page and documentation (Next.js + Fumadocs). |
| `docs/` | - | Design specs (`specs/`), decision notes (`notes/`), and [what this project is](./docs/about.md). |
| `docs/` | - | [About](./docs/about.md), design specs (`specs/`), decision notes (`notes/`), research (`research/`), and reviews (`reviews/`). |
| `scripts/` | - | Committed git hooks (lint on commit, lint + typecheck + test before push). |
| `brand/` | - | Logo and brand assets. |

Expand All @@ -152,7 +166,7 @@ pnpm lint:fix
```

```bash
pnpm --filter openapi-petstore dev # example app
pnpm --filter example-openapi-petstore dev # example app
pnpm --filter site dev # landing page & docs on :3001
```

Expand Down
Loading
Loading