Getting Started With WebMCP: Make Your Website Agent-Ready in 15 Minutes
Google and Microsoft just shipped something that changes how AI agents interact with the web. It's called WebMCP, it's a W3C standard, and you can start using it today with two HTML attributes.
This guide covers everything: the declarative API for quick wins, the imperative API for senior engineers who want full control, a Nuxt/Vue integration pattern, and why your CEO should care about this before your competitors figure it out.
What WebMCP Actually Is
WebMCP (Web Model Context Protocol) is a browser-native API that lets your website expose structured tools to AI agents. Instead of agents scraping your DOM, parsing screenshots, or guessing what buttons do — they call your tools directly with typed parameters and get structured responses.
Think of it as an API for the browser tab.
Before WebMCP: An AI agent takes a 2,000-token screenshot of your page, tries to OCR the text, guesses which element is a search box, attempts to type into it, hopes the CSS selector hasn't changed since last week.
After WebMCP: The agent calls searchProducts({ query: "wireless headphones", maxPrice: 150 }) and gets back a structured JSON response. 20 tokens. Instant.
That's an 89% reduction in token usage and it works even when you redesign your UI.
The Business Translation
For the executives reading this: WebMCP means your website shows up in AI agent workflows. When a customer tells ChatGPT "find me a web design agency that does AI work," the agent doesn't just read your homepage — it can interact with your site. Search your services. Check availability. Start a conversation. The sites that expose these tools get used. The ones that don't get skipped.
The Two-Attribute Version (Declarative API)
You already have HTML forms. You're about to make them agent-readable.
Before
<form action="/api/contact" method="POST">
<input name="name" type="text" required />
<input name="email" type="email" required />
<select name="service">
<option value="ai">AI Solutions</option>
<option value="design">Web Design</option>
<option value="marketing">Digital Marketing</option>
</select>
<textarea name="message"></textarea>
<button type="submit">Send</button>
</form>
After
<form action="/api/contact" method="POST"
toolname="request_consultation"
tooldescription="Request a free consultation with Pfaff Digital about AI, design, or marketing services">
<input name="name" type="text" required
toolparamdescription="Full name of the person requesting consultation" />
<input name="email" type="email" required
toolparamdescription="Email address for follow-up" />
<select name="service"
toolparamdescription="Which service area: AI solutions, web design, or digital marketing">
<option value="ai">AI Solutions</option>
<option value="design">Web Design</option>
<option value="marketing">Digital Marketing</option>
</select>
<textarea name="message"
toolparamdescription="Brief description of the project or question"></textarea>
<button type="submit">Send</button>
</form>
That's it. Two attributes on the form (toolname, tooldescription). One attribute per input (toolparamdescription). The browser automatically generates a JSON schema from the form's types, validation rules, and required attributes. The agent sees a structured tool and knows exactly how to use it.
The human stays in the loop by default. When an agent invokes a declarative tool, the browser brings the form into focus, populates the fields visually, and waits for the user to confirm. You can override this with toolautosubmit="true" for read-only operations like search — but never for actions that modify data.
The JavaScript Version (Imperative API)
For anything beyond simple form submissions — stateful interactions, multi-step workflows, API calls, conditional logic — you need the imperative API.
if ('modelContext' in navigator) {
navigator.modelContext.registerTool({
name: 'search_services',
description: 'Search available services and get details including pricing ranges and typical timelines',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'What the business needs help with'
},
category: {
type: 'string',
enum: ['ai', 'design', 'marketing', 'all'],
description: 'Service category to filter by'
}
},
required: ['query']
},
annotations: {
readOnlyHint: true
},
async execute({ query, category }) {
const results = await fetch(`/api/services?q=${query}&cat=${category || 'all'}`);
const data = await results.json();
return {
content: [{
type: 'text',
text: JSON.stringify(data)
}]
};
}
});
}
Key Details for Senior Engineers
The annotations object controls security behavior. readOnlyHint: true tells the browser this tool doesn't modify state, which may allow agents to invoke it without user confirmation. Always set this for search and query operations. Omit it for anything that writes data.
The execute function receives two arguments: input (the typed parameters) and client (a ModelContextClient object). The client gives you access to requestUserInteraction():
async execute(input, client) {
const confirmed = await client.requestUserInteraction(async () => {
return showConfirmationDialog({
action: 'Schedule a discovery call',
details: input
});
});
if (!confirmed) {
return { content: [{ type: 'text', text: '{"status":"cancelled"}' }] };
}
// Proceed with the action
}
This pattern is critical for any destructive or transactional operation. The agent pauses, the browser shows your UI, the human confirms or cancels. Full audit trail.
Tool lifecycle management: Use unregisterTool('tool_name') when context changes — for example, when a user navigates to a different section of your SPA. In a component-based framework, register on mount, unregister on unmount.
The Vue/Nuxt Pattern
Here's a composable that handles registration and cleanup:
// composables/useWebMCPTool.ts
import type { Ref } from 'vue'
interface WebMCPTool {
name: string
description: string
inputSchema: Record<string, unknown>
annotations?: { readOnlyHint?: boolean }
execute: (input: any, client?: any) => Promise<{ content: Array<{ type: string; text: string }> }>
}
export function useWebMCPTool(tool: WebMCPTool | Ref<WebMCPTool | null>) {
const registered = ref(false)
const register = async () => {
if (!import.meta.client) return
if (!('modelContext' in navigator)) return
const t = unref(tool)
if (!t) return
try {
await (navigator as any).modelContext.registerTool(t)
registered.value = true
} catch (e) {
console.warn(`[WebMCP] Failed to register tool "${t.name}":`, e)
}
}
const unregister = async () => {
if (!registered.value) return
const t = unref(tool)
if (!t) return
try {
await (navigator as any).modelContext.unregisterTool(t.name)
registered.value = false
} catch (e) {
// Tool may already be unregistered
}
}
onMounted(register)
onBeforeUnmount(unregister)
return { registered }
}
Use it in any component:
<script setup lang="ts">
useWebMCPTool({
name: 'get_portfolio',
description: 'Get recent project examples with descriptions and outcomes',
inputSchema: {
type: 'object',
properties: {
category: {
type: 'string',
enum: ['ai', 'design', 'marketing'],
description: 'Filter projects by service category'
}
}
},
annotations: { readOnlyHint: true },
async execute({ category }) {
const projects = await $fetch('/api/projects', { params: { category } })
return {
content: [{ type: 'text', text: JSON.stringify(projects) }]
}
}
})
</script>
Clean, composable, and the tool exists exactly as long as the component does.
What to Implement First
Week 1 — Read-only tools (lowest risk, highest signal):
- Site search
- Service/product catalog lookup
- Store/office locator
- FAQ query
Week 2 — Existing forms:
- Contact forms
- Newsletter signup
- Quote request
- Support ticket submission
Week 3 — Complex workflows:
- Multi-step booking
- Custom quote builder
- Onboarding wizard
Start with readOnlyHint: true tools. No state changes, no risk, immediate value.
Security: The Non-Negotiable Checklist
WebMCP is still a DevTrial. The API surface will change. But these security principles won't:
- Never expose admin actions without
requestUserInteraction() - Validate all inputs as untrusted — agent-provided parameters are user input
- Set
readOnlyHintaccurately — lying about it breaks trust with the browser - Keep tool count under 50 per page — focus on high-value interactions
- Log agent invocations —
SubmitEvent.agentInvokedlets you distinguish human from agent submissions server-side - HTTPS only — WebMCP APIs are restricted to secure contexts
The Competitive Angle
Here's the part your CEO needs to hear:
AI agents are becoming a traffic source. When a potential customer asks Claude "find me someone who builds custom AI applications," the agent navigates to websites. The sites with WebMCP tools get interacted with — the agent can search services, check case studies, and initiate contact. The sites without WebMCP get a quick text scrape and maybe a mention.
This is the SEO equivalent of structured data circa 2015. The early adopters got rich snippets while everyone else wondered why their click-through rates were dropping. WebMCP is the structured data of the agent era.
The window is now. Chrome 146 hits stable around March 2026. The developers who build familiarity with navigator.modelContext today will be ready. Everyone else will be reading tutorials later.
Try It Live
We built an interactive WebMCP demo that shows exactly what an AI agent sees when it visits an agent-ready page. It renders the tool registry in real time, shows the JSON schema the browser generates, and lets you invoke tools manually.