About This Code Showcase
This curated code showcase walks through the WebMCP integration in Sunny Car Accessories — from how 7 typed tools are defined, to how the per-view tool surface swaps as the user navigates, to the Inspector Panel that proves the demo is genuinely active.
The entire app runs client-side in a single static HTML file. The WebMCP layer is purely additive — humans see a normal storefront; agents see a typed contract via navigator.modelContext.
Core: Tool Definitions (TOOLS object)
Each tool is a typed contract — name, human-readable description, JSON inputSchema for the agent to read, and an async execute() handler that runs the business logic and returns structured JSON.
const TOOLS = {
checkFitment: {
name: 'checkFitment',
description: 'Check whether a part fits a specific vehicle.',
inputSchema: {
type: 'object',
properties: {
sku: { type: 'string', description: 'Product SKU to check' },
vehicleMake: { type: 'string', enum: ['Honda','Toyota','Perodua','Proton'] },
vehicleModel: { type: 'string', description: 'e.g. Civic, Myvi, Saga' },
year: { type: 'number', description: 'Vehicle model year' }
},
required: ['sku', 'vehicleMake']
},
execute: async ({ sku, vehicleMake, vehicleModel, year }) => {
const p = CATALOG.find(x => x.sku === sku);
if (!p) return { type: 'text', text: JSON.stringify({ error: 'SKU not found' }) };
if (p.fitment === 'universal') {
return { type: 'text', text: JSON.stringify({ fits: true, reason: 'Universal fit' }) };
}
const fits = Array.isArray(p.fitment) && p.fitment.includes(vehicleMake);
return { type: 'text', text: JSON.stringify({ fits, sku, vehicleMake, compatibleMakes: p.fitment }) };
}
}
};
Per-View Tool Scoping (the killer feature)
WebMCP's design choice: agents see only the 3–5 tools relevant to where they currently are. Tool surface morphs on every view change. provideContext() is called fresh per view.
const VIEW_TOOLS = {
home: ['searchAccessories'],
category: ['searchAccessories', 'getProductDetails'],
product: ['getProductDetails', 'checkFitment', 'addToCart'],
cart: ['getCart', 'removeFromCart'],
checkout: ['getCart', 'checkout'],
track: []
};
function updateWebMCPContext() {
const viewToolNames = VIEW_TOOLS[state.currentView] || [];
const tools = viewToolNames.map(n => TOOLS[n]).filter(Boolean);
webMCP.provideContext({ tools });
}
function showView(viewName, params = {}) {
state.currentView = viewName;
if (viewName === 'home') renderHome();
updateWebMCPContext();
window.scrollTo(0, 0);
}
WebMCP Wrapper (real API + compatibility fallback)
The webMCP wrapper uses navigator.modelContext when present, falls back to a local registry otherwise. This makes the Inspector Panel work identically across browsers — a real Chrome 146+ install vs a compatibility-mode browser show the same UI behavior.
const webMCP = {
tools: [],
log: [],
isSupported: () => 'modelContext' in navigator,
provideContext({ tools }) {
this.tools = tools;
if (this.isSupported() && navigator.modelContext.provideContext) {
try {
navigator.modelContext.provideContext({ tools });
} catch (e) { console.warn('WebMCP error:', e); }
}
updateInspectorPanel();
},
async invokeTool(name, params = {}) {
const tool = this.tools.find(t => t.name === name);
if (!tool) return { error: 'Tool not registered on this view' };
const entry = { ts: timeStamp(), name, params, result: null };
this.log.push(entry);
updateInspectorPanel();
try {
const result = await tool.execute(params);
entry.result = result;
updateInspectorPanel();
return result;
} catch (e) {
entry.result = { error: e.message };
return { error: e.message };
}
},
async requestUserInteraction({ title, message }) {
return new Promise(resolve => {
document.getElementById('confirm-title').textContent = title;
document.getElementById('confirm-message').textContent = message;
document.getElementById('confirm-modal').classList.remove('hidden');
window._confirmResolve = resolve;
});
}
};
requestUserInteraction() — Human-in-the-Loop on Checkout
The destructive checkout tool gates execution behind a mandatory human confirmation. Even if an agent invokes it autonomously, the order cannot proceed without explicit user approval — Allow once / Deny.
checkout: {
name: 'checkout',
description: 'Place the order. Triggers requestUserInteraction() before charging.',
inputSchema: {
type: 'object',
properties: { paymentMethod: { type: 'string', enum: ['DuitNow','FPX','COD'] } },
required: ['paymentMethod']
},
execute: async ({ paymentMethod }) => {
if (state.cart.length === 0)
return { type: 'text', text: JSON.stringify({ success: false, error: 'Cart is empty' }) };
const total = cartTotal();
const confirmed = await webMCP.requestUserInteraction({
title: 'Confirm purchase',
message: `Agent is about to place order: RM ${total.toFixed(0)} via ${paymentMethod}. Approve?`
});
if (!confirmed)
return { type: 'text', text: JSON.stringify({ success: false, reason: 'User declined' }) };
const result = placeOrder(null);
return { type: 'text', text: JSON.stringify(result) };
}
}
Inspector Panel — 4 Tabs of Live Introspection
The Inspector Panel is the proof layer that turns the invisible WebMCP API into a visible demonstration. Status badge updates per view; 4 tabs show live tools, run sample invocations, log results, and reveal the source code that produced the surface.
function updateInspectorPanel() {
const status = document.getElementById('inspector-status');
if (webMCP.isSupported()) {
status.innerHTML = `✅ WebMCP active · ${webMCP.tools.length} tool${webMCP.tools.length !== 1 ? 's' : ''} on <strong>${state.currentView}</strong> view`;
} else {
status.innerHTML = `⚠️ WebMCP not detected · running in compatibility mode · ${webMCP.tools.length} tools on ${state.currentView}`;
}
const activeTab = document.querySelector('.inspector-tab.active')?.dataset.tab || 'tools';
renderInspectorPane(activeTab);
}
function renderInspectorPane(tab) {
const pane = document.querySelector(`[data-pane="${tab}"]`);
if (tab === 'tools') {
pane.innerHTML = webMCP.tools.map(t => `
<details class="tool-entry">
<summary><strong>${t.name}</strong> — ${t.description}</summary>
<pre>${JSON.stringify(t.inputSchema, null, 2)}</pre>
</details>`).join('');
} else if (tab === 'try') {
pane.innerHTML = webMCP.tools.map(t => {
const sample = sampleParamsFor(t.name);
return `<button onclick='runFromInspector("${t.name}", ${JSON.stringify(sample)})'>
▶ Run ${t.name}</button>`;
}).join('');
} else if (tab === 'log') {
pane.innerHTML = webMCP.log.slice(-12).reverse().map(l => `
${l.ts} ${l.name}(${JSON.stringify(l.params)}) → ${JSON.stringify(l.result)}`).join('');
} else if (tab === 'source') {
pane.innerHTML = `<pre>${getSourceForView(state.currentView)}</pre>`;
}
}
agentInvoked Detection
Per WebMCP spec slide 13: submitEvent.agentInvoked tells the form handler "this submission came from an agent, not a human." Useful for analytics splits (agent traffic vs human traffic) and merchant-side fraud / bot detection.
document.addEventListener('submit', (e) => {
if (e.agentInvoked) {
console.log('[WebMCP] Form was submitted by an agent, not a human');
webMCP.log.push({
ts: timeStamp(),
name: '(form submit)',
params: { agentInvoked: true },
result: { detected: 'agent submission' }
});
updateInspectorPanel();
}
});
Catalog Data Model
The 18-SKU catalog is a JS array. Each product has SKU, name, brand, category, price, image, description, variantType (color / size / brand / scent / null), variants array, fitment (universal | array of compatible vehicle makes), stock, rating, reviews. Same shape parameterizes any retail vertical via the WebMCP webstore template.
const CATALOG = [
{
sku: 'INT-001',
name: 'Premium Leather Seat Cover Set',
brand: 'AutoLuxe',
category: 'interior',
price: 280,
image: 'images/products/int-001-leather-seat-cover.jpg',
description: '5-piece premium leather seat cover set...',
variantType: 'color',
variants: ['Black','Beige','Grey','Brown','Red'],
fitment: 'universal',
stock: 12,
rating: 4.5,
reviews: 87
},
{
sku: 'INT-002',
name: '3D Floor Mats',
brand: 'TerraGuard',
category: 'interior',
price: 180,
variantType: null,
variants: [],
fitment: ['Honda','Toyota','Perodua','Proton'],
stock: 25,
rating: 4.7,
reviews: 156
}
];
Key Design Decisions
- Single-file static HTML over framework: No build step, deployable to any static host (GitHub Pages, S3, Cloudflare). Per-view scoping demonstrated via JS state machine, not React/Vue routing.
- webMCP wrapper over direct API: Lets the Inspector Panel work identically with or without browser support — critical for the "compatibility mode" honest disclosure
- Per-view tool scoping over monolithic surface: Demonstrates the killer WebMCP feature; better security posture (destructive tools only on relevant pages); lower agent token cost (3-5 tools at a time, not 7)
- Custom confirm modal over native dialog(): Brand-consistent UI, async Promise-based, polyfills
requestUserInteraction() when the spec API isn't yet available
- localStorage cart over server session: Zero backend; cart survives page reload; works on file:// for offline demo testing