Fetching latest headlines…
Enabling WebMCP Tools on my SvelteKit Migration Reference
NORTH AMERICA
πŸ‡ΊπŸ‡Έ United Statesβ€’March 22, 2026

Enabling WebMCP Tools on my SvelteKit Migration Reference

2 views0 likes0 comments
Originally published byDev.to

An AI agent asks: "What's the Svelte 5 equivalent of useEffect?" Today it has two options β€” scrape the page HTML and parse it, or fetch api/data.json and filter 100+ items in context. Both work ok, but they both waste tokens.

WebMCP gives our AI agent a third option: call get_svelte_equivalent({ framework: 'react', concept: 'Side Effects' }) and get a structured answer directly. The data is the same. The interface is purpose-built for agents.

What WebMCP Is

WebMCP is a W3C Community Group Draft that adds navigator.modelContext to browsers. It lets websites register MCP tools β€” the same protocol that powers Claude's tool use, Cursor's context, and other agent frameworks β€” directly in a webpage. An AI agent browsing the site can discover and call those tools without scraping.

Chrome Canary (146+) has WebMCP behind a flag, and Microsoft Edge appears to support it out of the box β€” no flags or settings needed. For other browsers, @mcp-b/global from the MCP-B project fills in, until native support ships. Don't confuse this with Anthropic's MCP server protocol β€” WebMCP is a client-side browser API that happens to speak the same message format.

The App

My site svelte.cogley.jp is a migration reference showing how concepts from React, Vue, Angular, and now Svelte 4 map to Svelte 5. There are 120+ structured mappings across four categories: overview, syntax, architecture, and ecosystem. Each mapping has a concept name, source framework code, Svelte equivalent code, notes, doc links, and bilingual support (English/Japanese, well, because I can).

The data already has machine interfaces: a JSON API at /api/data.json, content negotiation via Accept: text/markdown, and an llms.txt describing the site for crawlers. WebMCP adds another layer β€” typed tools that agents can discover and call in-page without fetching or parsing anything.

Loading Strategy

The polyfill weighs ~285KB. Most visitors are humans browsing migration cards, not AI agents calling tools β€” so shipping it in the initial bundle would be wasteful. Dynamic import:

onMount(async () => {
  theme.init();
  language.init(data.lang);

  // Force polyfill mode β€” Chrome Canary's native modelContext is
  // incomplete (missing listTools/registerTool), which crashes the
  // native adapter. Disable auto-init, wipe, then reinitialize.
  const win = window as unknown as Record;
  win.__webModelContextOptions = { autoInitialize: false };
  const { cleanupWebModelContext, initializeWebModelContext } =
    await import('@mcp-b/global');
  cleanupWebModelContext();
  try {
    Object.defineProperty(navigator, 'modelContext', {
      value: undefined, configurable: true, writable: true,
    });
  } catch { /* non-configurable β€” already cleaned */ }
  initializeWebModelContext();

  const { registerMigrationTools } = await import('$lib/webmcp');
  registerMigrationTools();
});

Vite code-splits the polyfill into its own chunk. The __webModelContextOptions flag prevents the module's auto-initialization from crashing on browsers (like Chrome Canary) where the native navigator.modelContext exists but is incomplete β€” missing listTools() and registerTool(). After wiping the incomplete native API, initializeWebModelContext() installs the full polyfill. On Edge and browsers without a native API, cleanupWebModelContext() is a no-op and the polyfill installs normally.

The registerMigrationTools() import is dynamic too, keeping tool definitions out of both the server bundle and the initial page load.

Designing the Tools

Six read-only tools answer questions that AI agents might want to do, like "what's the Svelte equivalent of X?"

search_mappings

Full-text search across concepts, code, and notes. Same filter logic as the UI's search box:

mc.registerTool({
  name: 'search_mappings',
  description: 'Search migration mappings by keyword...',
  inputSchema: {
    type: 'object',
    properties: {
      query: { type: 'string', description: 'Search keyword' },
      framework: { type: 'string', enum: ['react', 'vue', 'angular', 'svelte4'] },
      category: { type: 'string', enum: ['overview', 'syntax', 'architecture', 'ecosystem'] },
      lang: { type: 'string', enum: ['en', 'ja'] },
    },
    required: ['query'],
  },
  annotations: { readOnlyHint: true },
  execute: async (args) => {
    // Filter pool by framework/category, then substring-match on query
    // Return structured JSON with framework/category context per match
  },
});

An agent searching for "useState" gets React entries; searching for "routing" gets results across all four frameworks. The optional framework and category filters let agents narrow results when they already know what they're looking for.

get_svelte_equivalent

Direct lookup: give it a framework and concept name, get the best match. Uses exact match first, then substring fallback. If the agent asks for { framework: 'react', concept: 'Reactive State' }, it gets the $state mapping with full code examples and notes.

list_ecosystem_gaps

Items where the to field contains "No equivalent" or "⚠️". This is the kind of query that's awkward to express via JSON filtering but natural as a tool call: "What can't I do in Svelte that I can do in React?"

The Other Three

  • get_site_info β€” overview: frameworks, categories, total count, API URLs. The "hello world" tool.
  • get_changelog β€” recent updates, bilingual, with configurable limit.
  • compare_frameworks β€” side-by-side: how React, Vue, Angular, and Svelte 4 each handle the same concept. An agent comparing "Reactive State" gets useState vs ref() vs signal() vs let count = 0 β†’ $state in one call.

The Registration Module

The entire module is ~180 lines of TypeScript. Shared helpers handle filtering and matching:

function applyFilter(pool: MappingItem[], query: string): MappingItem[] {
  if (!query.trim()) return pool;
  const q = query.toLowerCase();
  return pool.filter(
    (item) =>
      item.concept.toLowerCase().includes(q) ||
      item.concept_ja?.toLowerCase().includes(q) ||
      item.from?.toLowerCase().includes(q) ||
      item.to?.toLowerCase().includes(q) ||
      item.notes?.toLowerCase().includes(q) ||
      item.notes_ja?.toLowerCase().includes(q) ||
      item.checklist?.some((s) => s.toLowerCase().includes(q)) ||
      item.checklist_ja?.some((s) => s.toLowerCase().includes(q))
  );
}

Same logic that drives the $derived block in +page.svelte. The UI and the tools search identically β€” one filter function, two consumers.

Every tool returns MCP's standard content format ({ content: [{ type: 'text', text: JSON.stringify(...) }] }) and carries annotations: { readOnlyHint: true }. Query only, no mutations.

Testing

Microsoft Edge works out of the box. Chrome Canary needs the WebMCP flag (chrome://flags β†’ "WebMCP for testing"). Alternatively, install the MCP-B extension. Open the console:

// List tools
navigator.modelContext.listTools()
// β†’ Array of 6 tools with names, descriptions, input schemas

// Search for useState
await navigator.modelContext.callTool({
  name: 'search_mappings',
  arguments: { query: 'useState' }
})

// Direct lookup
await navigator.modelContext.callTool({
  name: 'get_svelte_equivalent',
  arguments: { framework: 'react', concept: 'Reactive State' }
})

// Compare across frameworks
await navigator.modelContext.callTool({
  name: 'compare_frameworks',
  arguments: { concept: 'Reactive State' }
})

In browsers without native support, the polyfill loads silently with no visible UI change and no console errors. Here it is in Chrome Canary, but I tested in Edge too and it worked without any special settings:

Computer screen displaying Svelte 5 migration guide with green and blue code and text.

Updating llms.txt

I added a WebMCP section to llms.txt listing all six tools with their parameters. An agent that discovers the site via llms.txt now knows it can call tools directly β€” it doesn't have to guess whether the site supports WebMCP or fall back to fetching JSON.

Update: Svelte 4 β†’ 5 Upgrade Data

22 Feb 2026 β€” I added Svelte 4 as a fourth source framework. This one's different from the other three β€” it's not a cross-framework migration, it's an upgrade guide for developers already on Svelte who need to move from v4 to v5 (runes, snippets, new event syntax, stores β†’ reactive classes).

The data includes 25 new mappings: 4 overview items, 12 syntax mappings, 5 architecture patterns, and 4 ecosystem entries. The overview category has a new card type β€” a ChecklistCard β€” that renders a step-by-step migration checklist with numbered steps instead of the usual two-column code comparison. The checklist covers everything from npm install svelte@5 through running sv migrate to testing $$props edge cases.

A new checklist field on MappingItem holds the step arrays, with checklist_ja for Japanese translations. All four machine-readable interfaces were updated:

  • WebMCP tools β€” search_mappings and get_svelte_equivalent now accept framework: 'svelte4'; search covers checklist text; localizeItem returns the checklist array
  • llms-full.txt β€” Svelte 4 section renders checklist steps as numbered lists
  • llms.txt β€” framework list and tool descriptions updated
  • data.json β€” includes the full svelte4 data structure automatically

An agent can now do things like get_svelte_equivalent({ framework: 'svelte4', concept: 'Props' }) to learn that export let prop β†’ let { prop } = $props(), or search_mappings({ query: 'stores', framework: 'svelte4' }) to find the stores β†’ reactive classes migration path.

What It Cost

The registration module is ~200 lines of TypeScript, with six tools. Zero bytes added to the initial page bundle of https://svelte.cogley.jp because everything loads dynamically β€” the polyfill chunk (429KB) only fires if the browser doesn't have native support yet.

The migration data was already structured and queryable. Now β€” with four frameworks and 120+ mappings including the Svelte 4 β†’ 5 upgrade path β€” the site has an interface that AI agents can use without scraping.

Originally published at cogley.jp

Rick Cogley is CEO of eSolia Inc., providing bilingual IT outsourcing and infrastructure services in Tokyo, Japan.

Comments (0)

Sign in to join the discussion

Be the first to comment!