Enhancing e-commerce checkout with browser AI capabilities

1. Introduction

Modern web development provides an alternative to cloud-based round trips for AI usage. Chrome has started shipping built-in AI APIs that let you use everything from small expert models for translation to capable distilled full language models, all without token costs for you or your users, run privately on their device.

At the same time, AI agents offer a new way for users to interact with web applications, and making your web experience agent ready with WebMCP can help reduce token usage while providing more accurate results than traditional actuation that requires screenshots and DOM scraping.

In this codelab, you'll work on Mont-Royal Plein Air, an outdoor equipment e-commerce site. You'll learn how to build and integrate built-in AI APIs and WebMCP to:

  1. Infer customer intent from browsing history and dynamically build recommendations for them using on-device models.
  2. Expose tools agents and browser assistants can use to navigate the site, inspect product and recommendations, and manage a user's shopping cart.
  3. (Optional) Translate your site on-the-fly using small, on-device expert models.
  4. (Optional) Transform basic site search into an on-site agent powered using the same tools external agents use.

What you'll learn

  • How to use the Prompt API and enforce structured JSON output from it.
  • How to register WebMCP tools and strategies for thinking about tool composition and granularity.
  • (Optional) How to use the Translator API to translate content quickly using an on-device expert model.
  • (Optional) How to call WebMCP tools yourself.

Prerequisites

  • A personal Google Account and Google Cloud.
  • Google Chrome version 150+, or Canary, Dev or Beta channels, with the #enable-webmcp-testing and #devtools-webmcp-support flags enabled.
  • Familiarity with HTML, CSS, JavaScript, and TypeScript.
  • Familiarity with JSON Schema.

2. Project setup

Google account

If you don't already have a personal Google Account, you must create a Google Account.

Sign in to the Google Cloud console

Sign in to the Google Cloud Console using a personal Google account.

Enable billing

To set up a personal billing account, enable billing in the Cloud Console.

Create a project (optional)

If you don't have a current project you'd like to use for this lab, create a new project here.

Open Cloud Shell editor

  1. Click this link to navigate directly to Cloud Shell Editor.
  2. If you're prompted to authorize at any point today, click Authorize to continue. Click to authorize Cloud Shell
  3. If the terminal doesn't appear at the bottom of the screen, open it:
    • Click View.
    • Click Terminal.Open new terminal in Cloud Shell Editor

Set your project

  1. In the terminal, set your project using this command:
    gcloud config set project [PROJECT_ID]
    
    Example: gcloud config set project lab-project-id-example Set project id in Cloud Shell Editor terminal
  2. You'll see the following message: Updated property [core/project].

Local and workspace setup

  1. In your terminal (or Cloud Shell terminal), clone the project repository and navigate into the project directory:
    git clone https://github.com/GoogleChromeLabs/codelabs mont-royal
    cd mont-royal
    
  2. Navigate into the ecommerce-ai-enhancements/starter folder:
    cd ecommerce-ai-enhancements/starter
    
  3. Install the dependencies using pnpm:
    pnpm install
    
  4. Start the local development server:
    pnpm dev
    
    Open your browser to http://localhost:5173 to view the Mont-Royal Plein Air store.

(Optional) Install the WebMCP inspector extension

To make working with WebMCP easier, you can install the WebMCP Model Context Tool Inspector. Once installed, you need to get a Gemini API key to use it:

  • Go to AI Studio.
  • Click the Create API Key button to generate a new Gemini API Key tied to the project you set up earlier this step.
  • Open the WebMCP Model Context Tool Inspector (it opens in the side panel) and click the Update Gemini API Key button.
  • Paste your API key into the popup and save.

This extension has a basic agent that can use WebMCP, letting you try out your website through it instead of calling WebMCP tools directly through DevTools.

3. Built-in AI models on the web

What is the Prompt API?

The Prompt API gives web applications direct access to a small, multimodal language model running entirely on the user's device. Because inference executes locally:

  • Zero cloud latency: No network roundtrips or backend server queuing.
  • Private by default: Prompts, and any context passed to them, never leave the device
  • Zero cost: Inference runs on client hardware without token billing or cloud hosting expenses.
  • Offline capable: Works reliably even without an active internet connection.

In Chrome, the Prompt API is accessed via the global LanguageModel interface.

// 1. Set your expected inputs and outputs. This is multimodal, so you can
// include other types here, too
const languages = {
   expectedInputs: [{type: "text", languages: ["en"]}],
   expectedOutputs: [{type: "text", languages: ["en"]}],
}

// 2. Check availability. Always pass in your expected inputs and outputs,
// as some models may only support some inputs and outputs
const availability = await LanguageModel.availability(languages);
// 'available', 'downloading', or 'unavailable'

// 3. Create an isolated session with system prompt and language constraints
const session = await LanguageModel.create({
  ...languages,
  // Optional: add initial context to your session, like a system prompt
  initialPrompts: [
   {
      role: 'system',
      content: 'You are an expert outdoors sales assistant...'
   }
  ]
});

// 4. Define your expected output as JSON Schema
const schema = {
   //...
}

// 5. Prompt the model (supports plain text or structured models)
const result = await session.prompt('Recommend a 3-season tent footprint.', {
  responseConstraint: schema,
});

Exercise 1: Journey-based recommendations

E-commerce sites typically have carousels or upsells of "frequently bought together" items. For something like an outdoor site, it may be more useful to build those recommendations based on browsing history.

To accomplish this, you'll build a three-stage recommendation pipeline:

  1. Journey profiler: Using the Prompt API, you'll analyze the customer's chronological browsing timeline to attempt to infer the user's true adventure intent.
  2. Product query: From that intent, you'll make an API request to a fake product backend to get related items.
  3. Product recommendation: You'll pass those products back to the Product API to rank them, returning the five most cohesive companions to what the user's viewing.

Task 1.1: Prompt API initialization

Instead of initializing the Prompt API individually each time you want to use it, you centralize the calls into a single file, memoizing the creation by system prompt to reduce the startup time for multiple calls to the same agent.

Navigate to the file src/ai/prompt-api.ts and inspect getPromptSession() on lines 27–58:

export async function getPromptSession(
  systemPrompt: string = '',
  options: { signal?: AbortSignal } = {}
): Promise<LanguageModel>
  1. Reuse a cached base session. There's already a cache map on line 21, baseSessions, keyed by system prompt. Reusing base models instead of clone()ing new ones reduces startup time and inherits the core setup without each session interfering with each other. Return the clone—callers can interact with it, including destroy() it, freely without discarding your cached base.
  2. Feature-detect the API. Feature detect if the API exists, throw an error if it doesn't.
  3. Declare your modalities and languages. Built-in models can support multimodal input. You need to declare what modes you need and what languages you want to support. All of the content you pass to the LLM is in English.
  4. Check availability before creating anything. Even if the API exists, a model that supports your requirements may not. Check to see if one exists. If it doesn't, throw an error. If it's just not downloaded yet, report download progress.
  5. Create the session. Set up the options to create a new Prompt API session with, and the system prompt, if included

Task 1.2: Implement the journey profiler

Navigate to the file src/ai/journey-profiler.ts and inspect the target function on lines 42–100:

export async function inferJourneyProfile(
  context: ProfilerInputContext,
  catalogMap: ReadonlyMap<string, Product>
): Promise<JourneyProfile>
  1. Use the function from Task 1 to generate a new prompt session to use.
  2. Prompt the model with the provided prompt text, using journeyProfileSchema as the JSON Schema for the structured return.
  3. Parse the prompt output and deduplicate target categories.
  4. Replace the hardcoded string returns with the actual results from your prompt.
  5. Destroy the session when it's finished to free up memory.

Task 1.3: Implement the synergy re-ranker

Navigate to the file src/ai/synergy-reranker.ts and inspect the target function on lines 30–71:

export async function rankComplementaryGear(
  profile: JourneyProfile,
  candidates: readonly Product[],
  currentProduct?: Product | null,
  cartProducts?: readonly Product[],
  limit: number = 5
): Promise<readonly RecommendedItem[]>
  1. Use the function from Task 1 to generate a new prompt session to use.
  2. Prompt the model with the provided prompt text and generated schema.
  3. Parse the prompt output.
  4. Return the assembled recommendation list using assembleRecommendations.
  5. Destroy the session when it's finished to free up memory.

Verify the recommendation pipeline

  1. Open Chrome to http://localhost:5173/catalog/tent-mont-tremblant-3p.
  2. Scroll down to the For You section.
  3. Observe how the 5 companion cards update to reflect smart kit pairings (such as ground footprints, sleeping pads, and lightweight stakes) rather than random products.
  4. Open Chrome DevTools Console to inspect the real-time execution duration of both inference steps.

(Optional) Task 1.4: Explore more built-in AI models

There are several other built-in AI models available, including the Translator API expert model. The components in this codebase are already hooked up to a service that listens for changes in the current language and asks for their translation, and it's your job to hook it all up.

Navigate to the file: src/ai/translator.ts

Inspect the target function createTranslator on lines 29–45:

export async function createTranslator(
  targetLang: SupportedLanguage
): Promise<Translator | null>
  1. Check for support of the Translator API and ensure the target language isn't English (the content is in English by default).
  2. Build a language pair, from English to the target language, and check to see if that pair's available.
  3. Create options for the translator and attach a monitor show progress downloading the model.
  4. Return the initialized translator.

Next, inspect the target function translate on lines 50–58:

export async function translate(
  text: string,
  targetLang: SupportedLanguage
): Promise<string>
  1. Check to see if there's a translator available with getCachedTranslator passing in targetLang. If there isn't, return the clean text.
  2. If there is, use the translator to translate the clean text.

Navigate to the file: src/ai/translator.ts

Inspect the target function render on lines 204:

export async function translate(
  text: string,
  targetLang: SupportedLanguage
): Promise<string>
  1. Enable the Language Picker component

4. Using WebMCP to improve the agent experience

What is WebMCP?

As users start to use autonomous and co-browsing agents, it's become important to websites to adapt. Agents traditionally rely on actuation—taking screenshots and scraping the DOM and the accessibility trees—to determine how to interact with a site. This is slow, fragile and error-prone, and wastes user tokens.

WebMCP helps solve this by letting developers provide tools directly to agents to interact with the page they're on. Agents that support them can use those tools just like a tool in your coding harness, with clear, structured inputs and return values dictated by you.

// Register a tool directly on document.modelContext
document.modelContext.registerTool({
  name: 'get_product_specs',
  title: 'Get Product Specs',
  description: 'Retrieve technical specifications for the active product.',
  inputSchema: {
    type: 'object',
    properties: {
      // ...
    },
  },
  annotations: {
    readOnlyHint: true, // Informs the agent this tool causes no side-effects
  },
  execute: async (input) => {
    return {
      weight: '1.8 kg',
      capacity: '3 persons',
      seasons: '3-season',
    };
  },
});

When an agent connects to the webpage, it queries document.modelContext.getTools(). Each registered tool exposes:

  • name: Programmatic tool identifier used by agents when executing tools (e.g. 'get_product_specs').
  • title: Human-readable display label used by inspectors and agent interfaces (e.g. 'Get Product Specs').
  • description: Plain-text description informing the model what the tool does and when to call it. This is how an agent determines if it should call the tool, so be descriptive.
  • inputSchema: JSON Schema defining accepted arguments.
  • annotations: Behavior hints, such as { readOnlyHint: true } for read-only tools that cause no state side-effects.
  • execute: Asynchronous function executed when the tool is invoked.

Task 2.1: Recommendation and product tools

You start by registering a tool to let agents retrieve the recommendations you just built in the previous exercise.

Navigate to the file: src/components/product/ForYouSection.ts

Inspect registerWebMCPTools() on lines 71–94:

private registerWebMCPTools(): void
  1. Check to see if WebMCP's supported.
  2. If it is, register the tool.
  • Choose a title and a description that entices an agent to choose the tool—think prompt engineering here.
  • There's no input, so just include an empty object JSON Schema.
  • This is a read-only tool.
  • In the execute function, use the recommendationStore to get the current set of recommendations, then return the current product ID and the set of product recommendations.
  • Add an abort signal and catch it so it doesn't throw an error. This ensures the tool's removed properly when the component unmounts.

Navigate to the file: src/views/ProductView.ts

  1. Repeat the same process to register a get_current_product tool, this time returning the full product as well as the recommendations

Task 2.2: Cart drawer tools

Navigate to the file: src/components/cart/CartDrawer.ts

Inspect registerWebMCPTools() on lines 83–114:

private registerWebMCPTools(): void
  1. Register the view_cart tool:
  • Its inputSchema should be an object with a single boolean option, openDrawer.
  • It should be read only.
  • Read the input as part of its execute function, decide whether to show or hide the cart, refresh the recommendationStore, then return the cart summary
  1. Register the manage_cart tool:
  • Its inputSchema should be an object with items, which is an array of objects with the productID, add, remove, and update action enum, and integer quantity,
  • It's execute function should pass items to this.updateCart

Task 2.3: Language switching tool

Take WebMCP further by exposing a language tool to external agents.

Navigate to the file: src/components/navigation/LanguagePicker.ts

Inspect registerWebMCPTools() on lines 61–63:

private registerWebMCPTools(): void
  1. Register the switch_language tool:
    • The inputSchema should have a language property that is an enum of SUPPORTED_LANGUAGE_CODES.
    • Its execute function should call this.switchLanguage with the provided language.

Task 2.4: Store navigation tools

Enable autonomous agents to navigate the store, jump to product pages, browse categories, or initiate searches.

Navigate to the file: src/components/navigation/SiteHeader.ts

Inspect registerWebMCPTools() on lines 155–171:

private registerWebMCPTools(): void
  1. Check to see if WebMCP's supported.
  2. Register the search tool:
    • Input schema takes a query string.
    • In execute, call this.search(input?.query).
  3. Register the go_to_catalog tool:
    • Input schema accepts optional activity (enum of ACTIVITIES) and category (enum of PRODUCT_CATEGORIES).
    • In execute, call this.goToCatalog(input?.activity, input?.category).
  4. Register the get_categories_and_activities tool:
    • Read-only tool with no input parameters.
    • In execute, call this.getCategoriesAndActivities().
  5. Register the go_to_product tool:
    • Input schema requires a productId string.
    • In execute, call this.goToProduct(input?.productId).

Verify with the WebMCP inspector

  1. Reload http://localhost:5173/catalog/tent-mont-tremblant-3p in Chrome and navigate to a product page.
  2. Open Chrome DevTools > Application > WebMCP.
  3. You should see your WebMCP tools: get_recommendations, get_current_product, view_cart, manage_cart, switch_language, search, go_to_catalog, get_categories_and_activities, and go_to_product.
  4. Click one of them to see their details and their parameters.
  5. Choose one with parameters, choose some, and click Run Tool to see it in action.
  6. (Optional) If you installed and set up the WebMCP Model Context Tool Inspector, open it up—it opens in Chrome's side panel. Use the chat interface to tell the agent to take an action that you believe should call one of your tools to see how it responds.

5. (Optional) Building an on-site agent

Calling WebMCP tools in-page

WebMCP tools aren't just for external browser extensions or third-party bots—your own application can call them directly. By querying document.modelContext.getTools(), your client-side code can discover and execute tools dynamically.

You can use this to build an on-site AI shopping assistant, transforming the search bar into a product discovery tool based on natural language input. Instead of writing custom imperative code to coordinate numerous facet filters, your search bar can hand natural language queries to the Prompt API, which decides which in-page WebMCP tools to invoke.

To discover and invoke a WebMCP tool from JavaScript:

// getTools() returns a Promise — it must be awaited.
const tools = await document.modelContext.getTools();
const tool = tools.find(t => t.name === 'list_items');

const json = await document.modelContext.executeTool(tool, args);
const result = JSON.parse(json); // executeTool resolves with a DOMString

Task 3.1: Sidebar facet tools

Expose each catalog sidebar facet to WebMCP so agents (and your in-page search assistant) can toggle filters programmatically.

Each facet component in src/components/facets/ already provides a helper method on its class and an exported input schema in src/utils/filter-schemas.ts:

  1. In src/components/facets/ActivityFacet.ts:
    • Register activity_filter using activityFilterSchema.
    • In execute, call this.filterActivity(input?.activity, input?.selected).
  2. In src/components/facets/CategoryFacet.ts:
    • Register category_filter using categoryFilterSchema.
    • In execute, call this.filterCategory(input?.category, input?.selected).
  3. In src/components/facets/ConditionFacet.ts:
    • Register condition_filter using conditionFilterSchema.
    • In execute, call this.filterCondition(input?.condition, input?.selected).
  4. In src/components/facets/PriceFacet.ts:
    • Register price_filter using priceFilterSchema.
    • In execute, call this.filterPrice(input?.priceRange || input?.bracket, input?.selected).
  5. In src/components/facets/WeightFacet.ts:
    • Register weight_filter using weightFilterSchema.
    • In execute, call this.filterWeight(input?.weightRange || input?.bracket, input?.selected).
  6. In src/components/facets/RatingFacet.ts:
    • Register rating_filter using ratingFilterSchema.
    • In execute, call this.filterRating(input?.minRating || input?.rating, input?.selected).
  7. In src/components/facets/SearchFacet.ts:
    • Register keyword_filter using keywordFilterSchema.
    • In execute, call this.updateKeyword(input?.keyword).

Remember to check for WebMCP support and pass { signal: this.signal } with .catch(() => {}) as you learned in Step 2.

Task 3.2: Catalog view tools

Now register the catalog-level listing, filter reset, and semantic search tools on document.modelContext.

Navigate to the file: src/views/CatalogView.ts

Inspect registerWebMCPTools() on lines 125–140:

private registerWebMCPTools(): void
  1. Check to see if WebMCP is supported.
  2. Register the list_items tool:
    • It takes no input parameters and is read-only.
    • Its execute function should call this.getItemsSummary().
  3. Register the reset_filters tool:
    • It takes no input parameters.
    • Its execute function should call this.resetFilters().
  4. Register the semantic_catalog_filter tool:
    • Use semanticCatalogFilterSchema.
    • Its execute function should call interpretAndApplySemanticFilter(input.query).

Task 3.3: Orchestrating the semantic filter agent

Now it's time to wire it all up. This task transforms search into a semantic pipeline that powers the semantic_catalog_filter tool:

Navigate to the file: src/ai/catalog-semantic-filter.ts

Inspect prewarmSemanticFilterSession() on lines 84–88:

export async function prewarmSemanticFilterSession(): Promise<void>
  1. Check if warmSession already exists; if so, return early.
  2. Prewarm a Prompt API session with SEMANTIC_FILTER_SYSTEM_PROMPT using getPromptSession() and store it in warmSession. Wrap in a try/catch block so search creation can still fallback gracefully if prewarming fails.

Inspect inferSearchFacets() on lines 93–134:

async function inferSearchFacets(
  searchTerm: string,
  cachedJourney: JourneyProfile | null
): Promise<{ parsed: SemanticFilterResponse; cacheHit: boolean }>
  1. Claim the prewarmed session from warmSession if available (and reset warmSession to null), or create a new session using getPromptSession(SEMANTIC_FILTER_SYSTEM_PROMPT).
  2. Prompt the model with promptText, using semanticFilterSchema as the responseConstraint.
  3. Parse the returned JSON, store the parsed result in the persistent cache with cacheKey, and destroy the session in a finally block.

Inspect applyFacetsViaWebMCP() on lines 139–270:

async function applyFacetsViaWebMCP(
  searchTerm: string,
  parsed: SemanticFilterResponse
): Promise<{ appliedFilters: SemanticFilterResult['appliedFilters']; toolsExecuted: string[] }>
  1. Discover registered tools on document.modelContext using await document.modelContext.getTools().
  2. Reset filters to start with a clean slate by calling await executeFacet('reset_filters', {}).
  3. Concurrently apply active facets using await Promise.all(facetTasks).

Inspect interpretAndApplySemanticFilter() on lines 272–316:

export async function interpretAndApplySemanticFilter(
  searchTerm: string
): Promise<SemanticFilterResult>
  1. Infer search facets by calling inferSearchFacets(searchTerm, cachedJourney).
  2. Apply the inferred facets to the catalog using applyFacetsViaWebMCP(searchTerm, parsed).

Verify the in-page agent

  1. Navigate to http://localhost:5173/catalog.
  2. In the top search bar, enter: "lightweight 3 season tent for wet hikes"
  3. Watch the Prompt API parse the request, invoke the activity_filter (Hiking), category_filter (Tents), and condition_filter (Wet), instantly narrowing the catalog down to matching gear without any manual checkbox clicks.

6. What you learned

Congratulations. You have transformed an e-commerce application into an intelligent, agent-forward experience powered entirely by on-device AI and standard web platform APIs.

What you built:

  • On-device Prompt API: Synthesized user browsing timelines with LanguageModel to generate real-time JourneyProfile context, and built a rankComplementaryGear synergy engine using structured JSON schemas (responseConstraint).
  • WebMCP component tools: Registered modular, encapsulated tools across UI components (get_recommendations, get_current_product, view_cart, manage_cart) via document.modelContext.
  • Agent-forward architecture: Tested and verified tool execution using the Chrome WebMCP Inspector extension.
  • On-site in-page agent: Orchestrated client-side tools directly in the browser to power multi-facet natural language catalog search from the site header search bar.
  • Specialized on-device AI models: Explored dynamic on-device translation using Translator.

Key takeaways:

  1. Zero-latency & high privacy: On-device models like Gemini Nano eliminate cloud API keys, network latency, and data exfiltration concerns.
  2. Structured outputs are essential: Using JSON schemas with responseConstraint turns creative text generation into reliable, type-safe application logic.
  3. WebMCP prepares the web for AI agents: Registering tools directly in the DOM empowers both internal assistive features and external autonomous browser agents with verified, deterministic actions.
  4. Let the model propose, not execute: Constraining the model to an enum of your registered tools and dispatching the result yourself keeps agentic behaviour auditable and prevents it from inventing actions.