All articles

Voice & Browser8 min read

WebMCP Data Extraction Agent: Replacing Fragile Scrapers with Chrome's Tool Protocol

Using Chrome 146+ WebMCP flag, Chrome DevTools Protocol, and Gemini planning to extract structured data from websites that expose native AI tools — no CSS selectors

  • WebMCP
  • Chrome DevTools Protocol
  • Gemini
  • Node.js
  • TypeScript
  • Web Automation

Web scraping has a fundamental fragility problem: it relies on CSS selectors and HTML structure that website teams change without notice. The WebMCP protocol (available as a Chrome flag in Chrome 146+) introduces a different model: websites voluntarily expose typed, named tools that AI agents can discover and call directly through the browser — structured like an API, available without any server-side integration.

How WebMCP Works

A website registers tools using navigator.modelContext. The registration happens in JavaScript running in the page context — no extension or server required:

javascript
1// Website registers tools in its own JavaScript
2// (runs in the browser page context)
3navigator.modelContext.registerTool('searchFlights', {
4  description: 'Search for available flights between two airports',
5  parameters: {
6    origin: { type: 'string', description: 'IATA airport code (e.g., OTP)' },
7    destination: { type: 'string', description: 'IATA airport code (e.g., LHR)' },
8    date: { type: 'string', description: 'Departure date (YYYY-MM-DD)' },
9    passengers: { type: 'number', default: 1 },
10  },
11  execute: async (params) => {
12    // Actual flight search logic — native to the website
13    const results = await flightSearchAPI.query(params)
14    return { flights: results }
15  }
16})
17
18// The AI agent discovers this via:
19const tools = await navigator.modelContext.getTools()
20// Returns: [{ name: 'searchFlights', description: ..., parameters: ... }]

Agent Architecture

Chrome (146+) with --remote-debugging-port=9222
  + chrome://flags/#enable-webmcp-testing enabled
  ↓
Node.js Agent (TypeScript)
  ↓
CDP Connection (chrome-remote-interface)
  ↓
Tool Discovery Phase
  execute: navigator.modelContext.getTools()
  → full schema: name, description, parameters, outputSchema
  ↓
Gemini Planning Phase (gemini-2.0-flash)
  Input: tool schemas + user's extraction goal
  Output: execution plan [toolName, params] sequence
  ↓
Tool Execution Phase (via CDP)
  navigator.modelContext.callTool(name, params)
  → result validated against expected JSON schema
  → retry with corrected params on schema mismatch
  ↓
Structured JSON output

CDP Tool Discovery and Execution

typescript
1import CDP from 'chrome-remote-interface'
2
3async function discoverTools(client: CDP.Client): Promise<Tool[]> {
4  const { result } = await client.Runtime.evaluate({
5    expression: 'JSON.stringify(await navigator.modelContext.getTools())',
6    awaitPromise: true,
7    returnByValue: true,
8  })
9  return JSON.parse(result.value) as Tool[]
10}
11
12async function callTool(
13  client: CDP.Client,
14  toolName: string,
15  params: Record<string, unknown>
16): Promise<unknown> {
17  const expression = `
18    JSON.stringify(
19      await navigator.modelContext.callTool(
20        '${toolName}',
21        ${JSON.stringify(params)}
22      )
23    )
24  `
25  const { result } = await client.Runtime.evaluate({
26    expression,
27    awaitPromise: true,
28    returnByValue: true,
29  })
30
31  if (result.type === 'undefined' || result.value === undefined) {
32    throw new Error(`Tool '${toolName}' returned no result`)
33  }
34
35  return JSON.parse(result.value)
36}

Gemini Plans the Execution Sequence

typescript
1async function planExtraction(
2  tools: Tool[],
3  userGoal: string,
4  outputSchema: object
5): Promise<ExecutionStep[]> {
6  const toolDescriptions = tools.map(t =>
7    `${t.name}: ${t.description}\nParameters: ${JSON.stringify(t.parameters)}`
8  ).join('\n\n')
9
10  const prompt = `
11You are an extraction planner. Given these available tools and a user's goal,
12create an execution plan.
13
14AVAILABLE TOOLS:
15${toolDescriptions}
16
17USER GOAL: ${userGoal}
18
19EXPECTED OUTPUT SCHEMA: ${JSON.stringify(outputSchema)}
20
21Return a JSON array of steps: [{ "tool": "toolName", "params": {...}, "reason": "..." }]
22Chain tools when needed: e.g., search → filter → list → extract.
23`
24
25  const response = await model.generateContent(prompt)
26  return JSON.parse(response.text) as ExecutionStep[]
27}
28
29// Example plan for "Extract direct flights BUC→LHR next week":
30// [
31//   { tool: "searchFlights", params: { origin: "OTP", destination: "LHR", date: "2025-06-15" }, reason: "Search available flights" },
32//   { tool: "setFilters", params: { stops: 0 }, reason: "Filter to direct only" },
33//   { tool: "listFlights", params: {}, reason: "Get final filtered results" }
34// ]

Schema Validation with Retry

typescript
1import Ajv from 'ajv'
2const ajv = new Ajv()
3
4async function executeWithValidation(
5  client: CDP.Client,
6  plan: ExecutionStep[],
7  outputSchema: object,
8  maxRetries = 2
9): Promise<unknown> {
10  const validate = ajv.compile(outputSchema)
11  let lastResult: unknown
12
13  for (const step of plan) {
14    lastResult = await callTool(client, step.tool, step.params)
15  }
16
17  // Validate final output against expected schema
18  if (!validate(lastResult)) {
19    if (maxRetries > 0) {
20      // Ask Gemini to fix the plan based on the validation errors
21      const fixedPlan = await repairPlan(plan, validate.errors, outputSchema)
22      return executeWithValidation(client, fixedPlan, outputSchema, maxRetries - 1)
23    }
24    throw new Error(`Output validation failed: ${JSON.stringify(validate.errors)}`)
25  }
26
27  return lastResult
28}

WebMCP is currently a Chrome flag (chrome://flags/#enable-webmcp-testing) in Chrome 146+. The protocol is experimental. The value is in the model: websites that opt in provide a stable, typed API surface that won't break when they redesign their UI.

CSS selectors — no DOM scraping
0
Tool discovery via navigator.modelContext.getTools()
Auto
Plans tool execution sequence from goal
Gemini
JSON Schema validation + auto-retry on mismatch
Schema

Request a discovery call

30 minutesGoogle MeetFree