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

use-webmcp-tool

v0.2.0

Published

React hook for registering WebMCP tools (document.modelContext) with lifecycle-managed registration and MCP result normalization.

Readme

Use-Webmcp-Tool

A hook that registers a WebMCP tool with the browser and ties its lifecycle to a React component.

This is maintained by Chrome, and will be updated with any spec changes. The spec is 🧪 experimental, so the hook feature-detects and degrades to a no-op everywhere the API is absent.

Status / accuracy note (2026-06-05): Built against the current WebMCP spec, which exposes the imperative API on document.modelContext (registerTool + an AbortSignal for unregistration).


Install

npm install use-webmcp-tool

Requires React 18+ as a peer dependency. Ships as ESM with TypeScript types included — no runtime dependencies.


What it does

WebMCP lets a page expose JavaScript functions as "tools" that an AI agent (browser-built-in, iframe-hosted, or extension) can discover and call. The site author can expose functionality, and the agent uses this instead of scraping the DOM, a11y tree, or using screenshots.

The raw imperative API looks like this:

const controller = new AbortController();

document.modelContext.registerTool({
  name: "add-todo",
  description: "Add a new item to the user's active todo list",
  inputSchema: {
    type: "object",
    properties: {
      text: { type: "string", description: "The text content of the todo item" },
    },
    required: ["text"],
  },
  async execute({ text }) {
    await addTodoItemToCollection(text);
    return { content: [{ type: "text", text: `Added todo item: "${text}" successfully.` }] };
  },
}, { signal: controller.signal });

// Unregister later:
controller.abort();

useWebMCP wraps that imperative, lifecycle-bound API in the declarative, lifecycle-managed model React developers already use for everything else:

import { useWebMCP } from "use-webmcp-tool";

function TodoTools({ addTodo }) {
  const { supported, registered } = useWebMCP({
    name: "add-todo",
    description: "Add a new item to the user's active todo list",
    inputSchema: {
      type: "object",
      properties: {
        text: { type: "string", description: "The text content of the todo item" },
      },
      required: ["text"],
    },
    async execute({ text }) {
      addTodo(text);
      return `Added todo item: "${text}" successfully.`;
    },
  });

  if (!supported) return null;
  return <p>{registered ? "🤖 Agent tools ready" : "…"}</p>;
}

The tool is registered when the component mounts and unregistered automatically when it unmounts. This is designed so that the set of tools an agent sees stays in lockstep with what is actually on screen.


API

const { supported, registered, error } = useWebMCP({
  name,           // string — tool identifier (required)
  description,    // string — natural-language description for the agent (required)
  inputSchema,    // JSON Schema object describing args (optional)
  annotations,    // ToolAnnotations object with readOnlyHint/untrustedContentHint (optional)
  execute,        // (args) => result | Promise<result> (required)
  enabled = true, // boolean — register only while true
  formatOutput,   // (result, args) => any — optional shaper before MCP normalization
  onError,        // (error) => void — optional side-effect when execute throws
});

Returns

| field | type | meaning | | ------------ | ---------------- | -------------------------------------------------------------------- | | supported | boolean | document.modelContext exists in this environment. | | registered | boolean | The tool is currently registered with the browser. | | error | Error \| null | Registration error, e.g. NotAllowedError from a tools permissions policy. |

execute return values are normalized:

  • a string{ content: [{ type: "text", text }] }
  • undefined/null (no return) → { content: [] } (success, no payload)
  • a value that is already { content: [...] } → passed through untouched
  • a thrown value — Error or not (throw "not signed in", throw { code: 403 } both count) → { content: [{ type: "text", text }], isError: true }, after onError. A failure must never read as success to the agent.
  • a returned Error → treated exactly like a throw: onError fires, then an isError result
  • anything else (object/array/number) → JSON-serialized into a text block

Tests

useWebMCP.test.jsx (vitest + jsdom + @testing-library/react, 21 tests) covers the registration lifecycle (mount/unmount, StrictMode, enabled, late injection, registration errors), re-registration identity (execute changes don't churn, content-equal schemas don't churn, name changes do), and the full result/error normalization matrix including thrown non-Errors and returned Errors.

Run with npm install && npm test.