Inspiration

Southern Horticulture is a garden center in St. Augustine, Florida, half a mile from salt spray. The staff spend most of the day answering one question in different clothes: "will this plant live in my yard?" The honest answer depends on light, salt exposure, irrigation, whether a deer eats the leaves, and how much the person actually wants to water. A website cannot ask all of that, so every plant catalog on the internet shows the same grid to everyone and lets the shopper guess.

Agents changed what is possible. A shopper will happily tell an assistant about the dog and the sprinkler that broke last year, because it is a conversation rather than a form with nineteen fields. WebMCP was the missing half: a way for that conversation to reach into the page the shopper is looking at, instead of ending in a wall of text.

What it does

The Living Garden Center is a plant catalog that rebuilds itself around one specific yard or purpose.

You tell your agent what the space is really like. It calls describe_garden, and the page next to you recomposes: a verified pet friendly section rises to the top because a dog shares the yard, salt-tolerant privacy plants group together, and other plants with no verified toxicity record move into a section that says plainly that a missing record is not evidence of safety.

Every plant carries a verdict from a deterministic evaluator, from excellent down to insufficient data, and every verdict is explained. Tap a plant and you get why it landed there: the strengths, the compromises, and the care routine it actually asks for, on dials rather than in adjectives. Some plants get an honest no, with a "what would need to change" answer that names the single facet that would flip the verdict.

The agent and the shopper share one live page and one context. Say the salt is worse than you first thought, and the sections reshuffle in front of you. Ask why bougainvillea is a compromise, and the agent answers from the same evaluation the page is showing. Add plants to a saved list, then take a link with you.

The catalog never invents stock. Availability language comes from one recorded statement, and the tools say when we have no record rather than guessing.

How we built it

The core is a pure, deterministic evaluator. A GardenContext carries site conditions, household, and preferences, along with an assumption ledger that marks every value as known, derived, assumed, or unknown. Evaluating a plant against that context produces a fit with strengths, compromises, blockers, and unknowns. A rule table composes sections from those fits, and a page manifest renders through a fixed component registry. No language model writes copy at runtime, which is why the page recomposes instantly and says the same thing twice.

WebMCP sits on top as a thin layer. Registration checks for the API and registers every tool once at load:

document.modelContext.registerTool({ name: "describe_garden", description: "Interpret the shopper's words into structured garden conditions", inputSchema: { /* ... / }, execute: async (input) => { / ... */ } });

Eighteen tools in the public app, eleven read and seven write, each with explicit safety annotations so the browser can show which ones make changes and which three are destructive. Writes go through the same validation the human form uses. Context updates take a revision number and fail loudly on conflict, so the agent and the shopper cannot silently overwrite each other.

Two details made it robust. The tools return a share URL that encodes the yard, so an agent working in a browser you cannot see can hand you a link that opens the same personalized page anywhere. And an agent activity panel logs every tool call beside the page, so a human can see exactly what the agent did and undo it.

The app is Next.js 16 on Vercel. The repo ships a 60-plant sample catalog and reads a fuller catalog from an environment variable, so the demo is complete without publishing our whole inventory.

Challenges we ran into

The hardest problem was not the code. It was discovering where site tools actually run. Our first test failed because ChatGPT used its remote agent browser, so the tools executed in a browser we could not see and the visible tab never changed. Then Chrome reported no tools at all, which turned out to be the WebMCP flag being off. Then a regular ChatGPT chat said it could use the page and could not. Site tools live in the desktop app's built in browser under only a Local ChatGPT Work session, on GPT-5.6 Sol or Terra. Nothing on our page could fix any of that, so we made the page say it: a status chip that reads either "Site tools ready: 18 tools this agent can call" or a one line explanation of what to change.

Chrome 151 has no way to unregister a tool, and registering a duplicate name is rejected. So tools register exactly once and read live state at execute time rather than closing over it.

The subtler challenge was honesty. It is easy to build a recommender that sounds confident. We had to write an explicit contract: an unknown never becomes a negative, a missing toxicity record is never reported as safe, and no tool ever states a live stock count. That constraint produced the most distinctive part of the product.

Accomplishments that we're proud of

The page genuinely rebuilds itself around a conversation, and it does so from a deterministic evaluator rather than a generated layout, so it is fast, repeatable, and auditable.

The honest no. Most catalogs cannot afford to say "we would skip this one for your yard." Ours does, with the reason and the condition that would change it. A garden center that tells you not to buy something is the one you come back to.

The evidence gap. We surface the count of plants with no verified pet safety record and keep them visibly uncertain instead of quietly filtering them out or, worse, implying they are fine.

Reaching parity between the human and the agent. Same page, same context object, same validation, revision checked so neither one clobbers the other, and a visible log of what the agent touched.

And it works. In the ChatGPT desktop app under a local Work session, the arrow turns blue and the catalog reorganizes while you watch.

What we learned

Tools should carry judgment, not just data. A search_plants that returns rows makes the agent guess. A tool that returns a verdict plus the reasons behind it lets the agent explain, and the explanation matches what the human sees on screen.

Annotations matter more than they look. Before we marked the write tools explicitly, the browser listed sixteen read and zero write tools, and a client has no good way to reason about a tool it cannot classify.

Shared state needs concurrency control. Two actors editing one context is a distributed systems problem in miniature, and revision checking with an explicit conflict result solved it in about thirty lines.

The failure mode nobody warns you about is invisibility. A page that offers tools looks identical to a page that does not when the browser lacks the API. Telling the user which one they are looking at was one of the highest value changes we made all day.

What's next for Garden Center Web-MCP

Real availability. We have a specification for inventory evidence flowing from the point of sale into a per variation availability state, which would let the agent answer "ten Podocarpus in seven gallon and two fifteen gallon Ligustrum, can you hold them" with a number instead of a hedge. The honesty contract already routes every availability sentence through one function, so this lands without touching a single tool.

Filling the unknowns. Style and garden use tags are sparse, which is why some plants report facets we have not recorded. Backfilling them makes the fit explanations richer.

Carrying the plan further. The share link already encodes the yard and the saved list. The next step is bringing it into the store on a phone, so staff see the same evaluation the agent produced and can walk someone to the right row.

And the part we are most curious about: this pattern is not about plants. Any catalog where fit depends on the buyer's circumstances, and where the honest answer is sometimes no, can be built this way.

Built With

Share this project:

Updates

Submission history