npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mcp-b/webmcp-extension

v5.1.0

Published

Inject WebMCP from a Chromium extension and call page tools from an isolated content script

Readme

@mcp-b/webmcp-extension

Install WebMCP from a Chromium extension, then discover and call the page's imperative and declarative tools from an isolated content script.

The page API stays native-shaped: websites register JavaScript tools with document.modelContext or declare form tools in HTML. The extension-side helper returns the official MCP Client, with its normal listTools(), callTool(), and close() methods.

Install

pnpm add @mcp-b/global @mcp-b/webmcp-extension

Set up the extension

Declare the runtime and client as static document_start content scripts: install WebMCP in the page's MAIN world, then connect from the default isolated world. world: "MAIN" requires Chrome 111 or newer. See Chrome's content-script guide and content_scripts manifest reference.

{
  "manifest_version": 3,
  "name": "My WebMCP Extension",
  "version": "1.0.0",
  "minimum_chrome_version": "111",
  "content_scripts": [
    {
      "matches": ["https://*/*", "http://localhost/*", "http://127.0.0.1/*"],
      "js": ["main-world.iife.js"],
      "run_at": "document_start",
      "world": "MAIN"
    },
    {
      "matches": ["https://*/*", "http://localhost/*", "http://127.0.0.1/*"],
      "js": ["content-script.iife.js"],
      "run_at": "document_start"
    }
  ]
}

The MAIN-world entry only installs the runtime:

// main-world.ts
import '@mcp-b/global';

Page code continues to use document.modelContext directly:

await document.modelContext.registerTool({
  name: 'get_cart',
  description: 'Read the current shopping cart.',
  execute: () => ({
    content: [{ type: 'text', text: JSON.stringify(readCart()) }],
  }),
});

Annotated forms use the same extension connection:

<form
  toolname="extension_declarative"
  tooldescription="Submit a value through an annotated form."
  toolautosubmit
>
  <input name="value" toolparamdescription="Value to submit" required />
  <button type="submit">Submit</button>
</form>

@mcp-b/global uses native declarative support when available and installs the polyfilled form runtime otherwise. Imperative registrations and annotated forms both appear in client.listTools() and run through client.callTool(). The extension adds no separate DOM scanner or declarative client API. See the declarative API reference for the evolving browser behavior and polyfill compatibility boundary.

Native Chrome also includes imperative and declarative tools owned by same-origin child documents in the top-level client's tool list. This uses the browser's frame-tree discovery; the extension does not inject into those child frames. The polyfilled path remains scoped to the top document.

Connect from the isolated content script and use the standard client API:

// content-script.ts
import { connectWebMCPClient } from '@mcp-b/webmcp-extension/content-script';

const client = await connectWebMCPClient({
  name: 'my-extension',
  version: '1.0.0',
});

const { tools } = await client.listTools();
const result = await client.callTool({
  name: 'get_cart',
  arguments: {},
});

await client.close();

Pass the official MCP ClientOptions as the second argument when you need list-change handlers, validation, caching, or input-required behavior. The template uses listChanged.tools so tools registered after page hydration appear automatically.

Bundle each entry as a self-contained classic script. Extension content scripts cannot load bare npm imports at runtime.

Start from the template

The published package includes a minimal buildable extension in template:

cp -R node_modules/@mcp-b/webmcp-extension/template my-webmcp-extension
cd my-webmcp-extension
pnpm install
pnpm build

Load dist/ as an unpacked extension from chrome://extensions.

Security and scope

  • MAIN-world code shares the website's JavaScript environment. Keep extension secrets, credentials, and privileged Chrome API calls out of main-world.ts.
  • The client transport pins messages to window.location.origin; it does not use a wildcard origin. This is routing, not authentication: same-page code can observe or forge the channel. Treat page tool metadata, arguments, and results as untrusted, and never authorize privileged extension actions from them alone.
  • The template needs no extension API permissions, background worker, or web_accessible_resources declaration. Its match patterns declare the page access it needs.
  • Narrow the manifest match patterns to the sites your extension actually supports before publishing it.
  • Add icons and the remaining Chrome Web Store listing metadata before publishing; the included manifest is a runnable development baseline.
  • The template injects into top-level pages only. Native Chrome still discovers same-origin child-document tools through getTools().
  • Cross-origin imperative tools require allow="tools", exposedTo, and an explicit fromOrigins request. The extension client does not make that request. Declarative tools currently have no cross-origin exposure attribute.
  • Native Chrome resolves executeTool() with null when a tool navigates. The extension client reports that as an interrupted MCP call and does not carry calls across navigation or read JSON-LD from the destination document. Use SubmitEvent.respondWith() when a declarative tool must return a result without navigation.

Test

pnpm --filter @mcp-b/webmcp-extension test:e2e

The test smoke-builds the copyable template with its own config, then loads its manifest and runtime as a real unpacked MV3 extension in Playwright's bundled Chromium. It covers document-start injection, strict page CSP, imperative and declarative discovery and calls, toolautosubmit, respondWith(), list-change handling, tool errors, dynamic registration and removal, top-frame scoping, navigation, and BFCache restoration. A native Chromium lane also covers same-origin child-document imperative and declarative tools. See Playwright's Chrome extension testing guide.