-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Vanilla Web Components that make a web page usable by an AI agent.
@machfivetechchicago/machvive-webmcp-ai is a small, dependency-free toolkit for
building and debugging agent-ready web pages. It gives your page a way to
publish tools an AI agent can call, plus the instruments to see what those tools
do when something actually calls them.
No framework. No build step. No runtime dependencies. Standards-based custom
elements that work anywhere customElements does.
Agents interacting with websites today mostly do it by pretending to be a person: reading the rendered page, guessing which element is the "Add to cart" button, and clicking it. That is slow, brittle, and breaks every time the markup changes.
WebMCP is a W3C Web Machine Learning Community Group proposal that takes the opposite approach. Instead of an agent reverse-engineering your UI, the page declares what it can do:
navigator.modelContext.registerTool({
name: 'add_to_cart',
description: 'Add a product to the shopping cart',
inputSchema: {
type: 'object',
properties: { sku: { type: 'string', description: 'Product SKU' } },
required: ['sku']
},
execute: async ({ sku }) => {
await cart.add(sku);
return { content: [{ type: 'text', text: `Added ${sku} to cart.` }] };
}
});The agent now has a named, documented, schema-validated function to call. Your cart logic runs your code — not a simulated click on a button that might have moved. If the page changes, the tool keeps working.
The catch: no browser ships navigator.modelContext yet. That is the gap this
package fills.
WebMCP empowers agents by giving them access to your core business functionality within a safe, semantic sandbox. That matters most where your page is already doing commercial work.
A Business Accelerator is a targeted, authoritative, conversion-optimized landing page with automated onboarding that facilitates a non-linear, multi-touch sales cycle. Exposing your Business Accelerators to agents makes perfect sense: it gives agents access to interactive experiences that deliver real business value — not just content. An agent that can call your onboarding flow, quote builder, or availability check is doing something categorically different from one that scraped your marketing copy.
But if agents are becoming part of your customer journey, you need to understand how they engage with your site. That is what the analytics component is for: record agent sessions and play them back to see exactly how they navigate, interact, and use your Business Accelerators. The result is detailed insight into agent behaviour — and into the broader ecosystem of agent-driven engagement.
Business Accelerators are a MachFiveTech Chicago practice. Learn more at machfivemagnet.com.
| Component | Tag | Purpose |
|---|---|---|
| Polyfill | <machvive-webmcp-polyfill> |
Provides navigator.modelContext so you can build against WebMCP today |
| Inspector | <machvive-webmcp-inspect> |
Lists your tools, builds a form from each schema, runs them |
| Analytics | <machvive-webmcp-analytics> |
Captures every call for listing, editing, export, replay, and dataLayer |
| Lorum Ipsum | <machvive-lorum-ipsum> |
Placeholder copy (unrelated utility, included for convenience) |
They compose: the polyfill provides the registry, the inspector drives it, and analytics observes everything flowing through it.
npm install @machfivetechchicago/machvive-webmcp-aiImport only what you need, or everything at once:
// Cherry-pick (smaller bundles)
import '@machfivetechchicago/machvive-webmcp-ai/webmcp-polyfill';
import '@machfivetechchicago/machvive-webmcp-ai/webmcp-inspect';
import '@machfivetechchicago/machvive-webmcp-ai/webmcp-analytics';
// Or everything
import '@machfivetechchicago/machvive-webmcp-ai';Importing a module registers its custom element automatically — there is nothing to call. Then use the tags in your markup.
TypeScript declarations ship with the package; no @types/* needed.
machvive-webmcp-polyfill implements navigator.modelContext following the W3C
proposal: registerTool, unregisterTool, and provideContext (which replaces
the whole toolset at once, for when app state changes what's available).
Two behaviors worth understanding:
It never overwrites a native implementation. If the browser ships WebMCP, the polyfill steps aside and your page talks to the real thing. Your code does not change when that day arrives.
It requires a secure context. The native API is [SecureContext], so the
polyfill matches — it installs on HTTPS and on localhost, and declines
elsewhere with a console warning. This is deliberate: it stops you developing
against a surface the browser will never hand you on a plain-HTTP page.
The polyfill is a registry, not a transport. It holds your tools and can
invoke them; it does not connect to an agent. Two non-standard additions exist so
your own code can bridge that gap: navigator.modelContext.tools (the descriptors
an agent would discover, minus the handlers) and
navigator.modelContext.callTool(name, params). Both are clearly marked as
extensions — the spec defines registration only.
You have declared some tools. Do they work?
machvive-webmcp-inspect reads each tool's inputSchema and builds a form from
it — text inputs, number fields, checkboxes, dropdowns for enums, JSON textareas
for object and array parameters. Fill it in, press Execute, see the result.
The important detail is type fidelity. An HTML number input hands you the
string "3". A tool that declared { type: 'integer' } expects 3. The
inspector coerces every value to the type its schema declares before calling, so
what you test by hand is what an agent will send. Required fields are enforced
before anything runs, and malformed JSON is reported on the field rather than
thrown at the tool.
<!-- inline: renders where you place it -->
<machvive-webmcp-inspect></machvive-webmcp-inspect>
<!-- floating: docks as an overlay with a toggle, no layout impact -->
<machvive-webmcp-inspect floating></machvive-webmcp-inspect>Use inline in a docs page or admin panel; use floating to debug a live app without disturbing it. The tool list stays live as tools are registered and removed.
machvive-webmcp-analytics records every tool invocation — parameters, result,
duration, and errors — and lets you list, edit, export, replay, or forward them.
Analytics wraps each tool's execute handler at registration time. It does not
hook callTool.
That distinction is the whole design. Hooking the caller would only see calls made
through this library. Wrapping the handler sees every invocation — including
ones from a native navigator.modelContext and from real agents, neither of which
route through our code.
The consequence you must plan for:
Import analytics before you register any tools. Tools registered earlier cannot be instrumented, because the polyfill deliberately hides handlers from
tools. The component warns in the console when it detects this rather than pretending it captured everything.
// analytics first
import '@machfivetechchicago/machvive-webmcp-ai/webmcp-analytics';
import '@machfivetechchicago/machvive-webmcp-ai/webmcp-inspect';
// then register toolsimport { callLog } from '@machfivetechchicago/machvive-webmcp-ai/webmcp-analytics';
await callLog.ready; // restoring from IndexedDB is async
callLog.entries; // captured calls, oldest first
callLog.toJSON(); // export
callLog.import(json); // merge a previously exported log
callLog.update(id, { params }); // edit before replaying
await callLog.replay(id); // re-run exactly as captured
await callLog.replay(id, { sku: 'OTHER' }); // re-run with edited parametersReplay with edited parameters is the feature to reach for when reproducing an agent's mistake: capture what it actually sent, change one field, run it again.
Calls persist to IndexedDB, so a log survives reloads and is not bound by the
~5 MB localStorage ceiling. Where IndexedDB is unavailable, capture degrades to
memory-only rather than failing. The default cap is 500 entries, oldest evicted
first.
Recording is strictly best-effort. A failure inside the log — a throwing subscriber, an unwritable store — can never change a tool's result or make a successful call appear to have errored. An observability tool that corrupts the thing it observes is worse than no tool at all.
Pushing to window.dataLayer is off unless you opt in, so importing the
component never emits tracking traffic on its own:
<machvive-webmcp-analytics datalayer></machvive-webmcp-analytics>Each call then pushes { event: 'webmcp_tool_call', webmcp_tool, webmcp_status, webmcp_duration_ms, webmcp_params }. Without the attribute, push individual
entries on demand with callLog.pushToDataLayer(id) or the per-entry button.
The UI components follow the viewer's OS colour preference automatically. Set
theme to override:
<machvive-webmcp-analytics theme="dark"></machvive-webmcp-analytics>theme is also a reflected property: el.theme = 'dark', or null to go back to
following the OS.
Colours are CSS custom properties on the host. Custom properties inherit through
shadow boundaries where ordinary styles do not, so you can restyle the components
from your own stylesheet without ::part or !important:
machvive-webmcp-inspect,
machvive-webmcp-analytics {
--mv-accent: #7c3aed;
--mv-bg: #ffffff;
--mv-fg: #111827;
}Every combination of OS preference and theme is verified to meet WCAG AA
(≥ 4.5:1). If you override tokens, re-check your own contrast.
Secure context required. The polyfill installs on HTTPS and localhost only.
A plain-HTTP staging box will silently have no navigator.modelContext.
Browser-only — these break under SSR. Every component extends HTMLElement at
module load, so importing any entry point during a server render throws
ReferenceError: HTMLElement is not defined. This never happens in a browser-only
setup like Vite. In Next.js, Nuxt, Astro, SvelteKit, or Remix, import from a
client-only path:
useEffect(() => { import('@machfivetechchicago/machvive-webmcp-ai'); }, []);A static top-level import will not work in those frameworks — the module is
evaluated during the server render, before any browser-only lifecycle hook runs.
Import order matters for analytics. See above; this is the single most common way to end up with an empty log.
The inspector needs the polyfill's extensions. tools and callTool are
non-standard additions. Against a future native implementation that lacks them,
the inspector detects this and explains itself rather than throwing.
- Package — npm
- Source — GitHub
- WebMCP proposal — W3C Web Machine Learning CG
Apache-2.0. Zero runtime dependencies.