Agent-ready CNY reservation page for Malaysian Chinese restaurants
Challenge: Chinese New Year reunion bookings are the single biggest revenue spike of the year for Malaysian Chinese restaurants — Putien, Dragon-i, Tai Thong-tier banquet houses earn 25–40% of their annual revenue in the 6-week CNY window. Yet the booking flow is still phone-driven for most mid-tier operators. Modern shoppers want online booking. Future shoppers won't book themselves at all — they'll ask Auto Browser, Gemini, or ChatGPT Atlas to do it. Pixel-clicking agents can't reliably navigate a multi-step reservation flow with date pickers, dietary selectors, and seating-type variants — they abandon halfway. The merchant loses both the modern customer and the future agent-driven customer.
Solution: Ji Xing 吉星饭厅 is a CNY festival reservation page for a fictional Malaysian Cantonese banquet restaurant, built with the W3C WebMCP standard (navigator.modelContext). Six bilingual set menus (RM 688–RM 1,288), a 9-day slot grid (480 bookable tables across 14–22 February 2026), and a 7-tool typed contract that AI agents can invoke directly through the browser. Humans see a beautiful festival booking page that ranks on Google. Agents see "Here are my 7 tools — search, get details, check availability, reserve, get, modify, cancel — call them with structured parameters." No screenshotting. No abandoned reservations. No pixel-clicking dietary dropdowns.
reserveTable({setId, date, sitting, partySize, ...}) directly — no abandoned half-filled forms, no wrong dietary selections, no double-booked sittingscheckAvailability, getSetMenuDetails, getReservation answer pre-sale and post-sale questions without staff time — especially valuable during CNY peak when phones are jammedThe UI is a beautiful CNY booking page that looks completely normal. WebMCP runs invisibly underneath — a customer who isn't told would never notice the agent-callable layer is there. The 3 paths below let you experience the difference between a WebMCP-enabled reservation page and a regular one.
What it looks like: Browse 6 set menus, click "View Details" on any card, pick "Reserve This Set", choose a date and sitting (try 16 Feb Reunion Eve to see the peak-day rule), fill the form, confirm.
What's happening: Pure static interaction. No LLM involved anywhere. Get a booking reference (JX-CNY26-XXXXX), download the .ics calendar file, manage with the lookup form. The Inspector Panel sits unobtrusive at bottom-right — a normal customer won't notice it.
Cost: zero. Difference vs non-WebMCP page: none visible to the human user.
What it looks like: Open the Inspector Panel → Try as Agent tab → click any "▶ Run [tool]" button. Try checkAvailability for Reunion Eve dinner-2 with 10 pax — see the typed JSON response.
What's happening: The page invokes its own registered WebMCP tools with sample params and shows the typed JSON result in the Log tab. Still no LLM. This simulates exactly what a real AI agent would call when planning a CNY reservation.
Cost: zero. Difference vs non-WebMCP page: a non-WebMCP reservation page has nothing for these buttons to invoke — the agent layer simply doesn't exist there.
What it looks like: Visitor installs Auto Browser, points it at the demo URL, types natural language: "Book Ji Xing for CNY reunion dinner, Reunion Eve, 10 pax, no pork, private room, around RM 1,500 budget."
What's happening: Auto Browser does the LLM reasoning, decides which tools to chain (searchSetMenus → checkAvailability → reserveTable), calls them via navigator.modelContext, gets typed JSON back. The requestUserInteraction() modal pauses for explicit human confirmation before placing the booking. The LLM cost belongs to Auto Browser, not the merchant.
Cost: zero on the merchant side. Difference vs non-WebMCP page: Auto Browser cannot reliably book a multi-step reservation on a non-WebMCP page — it would screenshot the date picker, guess at the right sitting button, miss the dietary dropdown, and abandon the form mid-fill.
This is the strongest WebMCP value for Malaysian SMEs: agent-ready festival booking with zero ongoing AI cost — agents pay their own way, every year.
Single-file static HTML with a state-driven view router across home / set-detail / booking / confirmation / manage. No backend, no framework, no build step. URL params support deep linking (?set=10b, ?ref=JX-CNY26-XXXXX).
The killer WebMCP feature: tool surface morphs as the user navigates. Home shows 2 tools (search, getDetails). Set Detail shows 2. Booking shows 2 (availability + reserve). Confirmation shows 1 (read-only). Manage shows 3 (get, modify, cancel). Destructive actions live only where the user is in that intent.
Floating panel demonstrates WebMCP is real, not a label. Tabs: Tools (live JSON schemas), Try as Agent (run any tool with sample params), Log (timestamped invocations), Source (the actual provideContext() code per view).
The CNY equivalent of Sunny's checkFitment: checkAvailability({date, sitting, partySize, seatingType}) returns a typed JSON breakdown — main hall tables remaining, private rooms remaining, VIP room (10-pax Set B only). Pixel-clicking agents physically cannot reason about multi-bucket inventory like this.
The reserveTable, modifyReservation, and cancelReservation tools all trigger a custom "Confirm / Cancel" modal before mutating state. Even a runaway agent cannot book or cancel a reservation without explicit human approval — bilingual modal text, destructive-action flag, accessible.
Every description, FAQ entry, course name, dietary option, button, and form label is bilingual (English + 中文). Targets MY's bilingual market without forcing a language toggle. Captures both English and Chinese keyword searches simultaneously.
Three JSON-LD blocks: Restaurant (address, hours, price range, ReserveAction), FAQPage (15 Q&As marked up for rich snippets), and reservation lifecycle. Surfaces as rich SERP results — price range, hours, "Reserve" button visible directly in Google.
WebMCP is in W3C incubation — most browsers don't yet expose navigator.modelContext. The Inspector detects this, shows an honest "⚠️ not detected" status, and the booking flow still works fully for human visitors. Custom-modal polyfill handles requestUserInteraction() on browsers without native support.
Each tool registers with a JSON inputSchema describing parameters, types, and constraints. The agent reads the schema and calls checkAvailability with structured parameters — no screenshot interpretation of the date grid, no DOM scraping of the sitting buttons, no broken assumptions when a designer ships a redesign.
On every view change, navigator.modelContext.provideContext({tools}) swaps the active tool set. The agent on the booking form sees reserveTable but loses access to cancelReservation — destructive tools live only on the page where the user is in that intent. cancelReservation is exposed only on the Manage view.
Three tools call requestUserInteraction() before mutating state: reserveTable (places the reservation + 50% deposit), modifyReservation (changes existing booking), cancelReservation (with a destructive flag). The browser pauses agent execution and shows a bilingual confirmation prompt.
Per the WebMCP spec: typed tool calls consume 20–100 tokens. Screenshot + DOM parsing typically consumes 2,000–5,000+ tokens per page. A full booking flow (search → details → check availability → reserve) on this page = 4 tool calls = ~120–400 tokens vs ~8,000–20,000 tokens for pixel-clicking the same flow.
End-to-end tested on Chrome 147 with experimental web platform features enabled. All five test cases passed:
| Test | What was checked | Result |
|---|---|---|
| 1A — Per-view tool scoping | Inspector status badge updates: Home (2) → Set Detail (2) → Booking (2) → Confirmation (1) → Manage (3) | ✓ Pass |
| 1B — checkAvailability killer tool | Clicking ▶ Run checkAvailability on Set Detail returns typed JSON with mainHall / privateRoom / vipRoom counts for Reunion Eve dinner-2 |
✓ Pass |
| 1C — requestUserInteraction() | Running reserveTable from the Inspector triggers the bilingual confirmation modal before placing the reservation; cancelReservation shows the destructive-action variant |
✓ Pass |
| 1D — Peak-day rule enforcement | checkAvailability rejects lunch sittings on Reunion Eve (16 Feb) and Day 1 (17 Feb) with a typed error — kitchen prep + staff family time |
✓ Pass |
| 1E — VIP Room exclusivity | reserveTable with seatingType: "vipRoom" rejects all sets except 10-pax Set B (Lucky Star Signature) — preserves the bundled-tier exclusivity |
✓ Pass |
navigator.modelContext API is currently flag-gated even on Chrome 147 stable — the spec's "146+ stable" claim is aspirational; live API access requires chrome://flags/#enable-experimental-web-platform-features or a similar flag enabled.
requestUserInteraction(). The same code switches automatically to live agent integration the moment the browser enables WebMCP — no code change needed on this page.
chrome://flags → search "model context" or enable Experimental Web Platform Features → relaunch → reload the demo.
Realistic mid-tier Malaysian Chinese banquet restaurant — the actual Pau AI prospect profile:
For the full operating spec see restaurant outline.md; for the architecture spec see app outline.md — both shipped with the project folder.
VIEW_TOOLS map drives refreshWebMCPTools(viewName) on every showView() call — re-registers only the tools relevant to the current view.showView(name, params) function shows/hides view containers, calls the matching render function (renderSetDetail / renderBooking / renderConfirmation / renderManage), and triggers the WebMCP tool refresh.requestUserInteraction() isn't available natively, a bilingual modal overlay polyfills the experience — Confirm / Cancel buttons resolve a Promise the tool's execute() awaits. Destructive flag styles the confirm button in red.This demo runs entirely in the browser as a static page. For a real merchant deployment, the following would be added:
reserveTable's execute() hits a real payment processor server-side for the 50% deposit, returns booking confirmation