Inspiration

Planning a gift for someone means opening a dozen browser tabs, clicking filters that never quite match your intent, mentally adding up prices, then abandoning the cart when you realize the total blew past your budget.

We asked ourselves: what if the storefront could understand "birthday for my nephew" and do the search, curation, and budget math for you while still giving you, the human, full control over what gets added to your cart?

What it does

Wishlist Concierge exposes 12 imperative WebMCP tools on a Sylius 2.0 storefront, covering the full gift-planning lifecycle:

  • Find products by theme (tags); hard match on tags first, then soft match on product name, then fallback to all channel products
  • Build & curate themed wishlists, add or swap products, reorganize at will
  • Apply a budget constraint using a knapsack algorithm over quantity × ChannelPricing, with active catalog promotions discounts factored in; Return a human-readable explanation of what was chosen and why
  • Move selected items into the Sylius cart and hand you the token to proceed

How we built it

The entire project is a Sylius 2.0 plugin with the Sylius Wishlist plugin providing the persistence layer. The key architectural choice was single-source-of-truth tool definition:

  1. Each tool is declared once via a #[ModelContextTool] attribute on its shop controller action with a name, description, input DTO class, path params, confirm/destructive hints, all in one place.

  2. A ModelContextToolCollector service introspects these attributes at compile time, resolves each tool's route via the Symfony router, and produces a JSON manifest.

  3. The frontend registry.js fetches that manifest and calls document.modelContext.registerTool(tool, {signal}) for each — so adding a tool means writing a controller method and a DTO, not touching JavaScript at all.

  4. Server-side, a ToolContractValidator kernel.request listener deserializes every incoming payload against the DTO's #[Assert] constraints and returns deterministic 422 envelopes on failure. As a bnous, the schema the agent sees is the schema the server enforces.

  5. The frontend uses two Stimulus controllers. A toolbox modal that dynamically renders a run form for every registered tool, and a toast controller for live feedback via webmcp:* custom events.

Accomplishments that we're proud of

The single-source-of-truth tool definition. A tool lives once as a #[ModelContextTool] attribute and that single declaration drives the manifest, the frontend registration, the server-side validation, and the toolbox UI. No duplicate schema, no drift, no manual sync.

What we learned

  1. WebMCP changes the agent & web relationship. Instead of agents screen-scraping your UI, you hand them structured tools with typed schemas. That means better agents (clear contracts) and safer apps (no ambiguous click paths).

  2. Single source of truth is everything for developer experience. When a tool is defined in one place and everything else is derived, adding a new tool is a 5-minute task.

  3. Structured error shapes make agents better. Once the server returns {"error":"Validation failed", "message":"...", "violations":[{field,message}]}, the agent can reason about what went wrong and fix its next call - the feedback loop closes.

What's next for Wishlist Concierge

  1. Real-world testing

  2. Extend the knapsack algorithm to consider stock levels and quantity discounts, not just unit price × quantity.

Built With

Share this project:

Updates

Submission history