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:
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.
A ModelContextToolCollector service introspects these attributes at compile time, resolves each tool's route via the Symfony router, and produces a JSON manifest.
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.
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.
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
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).
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.
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
Real-world testing
Extend the knapsack algorithm to consider stock levels and quantity discounts, not just unit price × quantity.
Log in or sign up for Devpost to join the conversation.