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

@josharsh/webmcp-bridge

v0.1.0

Published

Expose webmcp-tools tools as a real MCP server — lets browser extensions, iframe agents, and devtools list and call the page's WebMCP tools over postMessage.

Readme

@josharsh/webmcp-bridge

Expose the tools your page registered with webmcp-tools as a real MCP server, so external agents — browser extensions, iframe agents, devtools — can tools/list and tools/call them over window.postMessage.

The bridge mirrors the kit registry live: tool registrations and unregistrations are pushed to connected agents as notifications/tools/list_changed, and every call runs the tool's full pipeline (schema validation, confirm gate, result normalization).

Install

npm install webmcp-tools @josharsh/webmcp-bridge

In the page (server side)

import { tool } from "webmcp-tools";
import {
  createWebMCPServer,
  PostMessageServerTransport,
} from "@josharsh/webmcp-bridge";

tool("add-to-cart", {
  description: "Add a product to the shopping cart",
  input: {
    type: "object",
    properties: { sku: { type: "string" } },
    required: ["sku"],
  },
  run: ({ sku }) => cart.add(String(sku)),
});

const bridge = createWebMCPServer({ name: "my-shop", version: "1.0.0" });
await bridge.connect(
  new PostMessageServerTransport({
    // Only these origins may list/call your tools. Pass ["*"] explicitly
    // (and knowingly) to accept any origin.
    allowedOrigins: ["https://agent.example"],
  }),
);

In the agent (client side — extension content script, iframe, devtools)

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { PostMessageClientTransport } from "@josharsh/webmcp-bridge";

const client = new Client({ name: "my-agent", version: "1.0.0" });
await client.connect(
  new PostMessageClientTransport({
    target: pageWindow, // e.g. an iframe's contentWindow, or window itself
    targetOrigin: "https://shop.example",
  }),
);

const { tools } = await client.listTools();
const result = await client.callTool({
  name: "add-to-cart",
  arguments: { sku: "SKU-1" },
});

API

| Export | Description | | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | createWebMCPServer(opts?) | Build an MCP server backed by the kit registry. opts: { name?, version? }. Returns { server, connect(transport), close() }. | | WebMCPBridgeServer | Return type of createWebMCPServer. server is the raw SDK Server for custom handlers. | | PostMessageServerTransport | new (opts: { window?, allowedOrigins, channel? }). Listens for client messages; rejects any origin not explicitly in allowedOrigins, binds exactly one peer (the {source, origin} of the first valid JSON-RPC message — later messages from other sources/origins are ignored), exposes the bound origin as peerOrigin, and replies to the bound event.source targeting its origin (never a wildcard when a concrete origin is known). | | PostMessageClientTransport | new (opts: { target, targetOrigin, channel? }). Connects an SDK Client to a page's bridge; only consumes replies from targetOrigin. | | DEFAULT_CHANNEL | "webmcp-tools-mcp" — the default envelope channel. Both peers must use the same channel. |

Semantics

  • exposedTo filtering. Tools registered with exposedTo: [origins] are only served (in tools/list and tools/call) when the transport's bound peerOrigin is in the list. Tools without exposedTo are served to every connected peer. Hidden tools are indistinguishable from unknown ones.
  • Unknown tools are JSON-RPC errors. tools/call with an unknown (or hidden) name responds with an InvalidParams JSON-RPC error — SDK clients see client.callTool(...) reject. Tool execution failures (validation, declined confirm, thrown run) still come back as isError results.
  • untrustedContentHint rides in _meta. The MCP SDK's annotations schema strips the WebMCP-specific untrustedContentHint; the bridge preserves it as tool._meta["webmcp/untrustedContentHint"] (readOnlyHint stays in annotations).

Wire format

Messages are wrapped as { channel, side: "client" | "server", message: <JSON-RPC> } and everything else on the window is ignored. The side tag lets both peers share a single window (devtools, tests) without consuming their own traffic; the channel lets multiple bridges coexist.

License

MIT