@frontmcp/plugin-webmcp

中文文档
1.12.2发布于 2026/11/15Apache-2.0TypeScriptESM + CJS构建来源已验证近 30 天下载 5152

WebMCP plugin for FrontMCP - expose an in-browser FrontMCP server's tools to browser agents through document.modelContext

原始使用文档
文档目录11 节
  1. Install
  2. Usage
  3. Options
  4. Choosing which tools agents see
  5. How tools are translated
  6. Registering tools before the server loads
  7. Browser support
  8. Docs
  9. Related packages
  10. Contributing
  11. License

@frontmcp/plugin-webmcp

Offers the tools of a FrontMCP server that runs in the page to the agents in the user's browser, through WebMCP (document.modelContext): the browser's own assistant, an extension, or the developer tools. Use it on a website that creates a FrontMCP server in the page, often under @frontmcp/react. The plugin registers the server's tools, keeps them in step as tools come and go, and runs each agent's call through the server's own flow.

npm License

Install

npm install @frontmcp/sdk @frontmcp/plugin-webmcp

Use the same version for both packages. The plugin's peer dependency @frontmcp/lazy-zod comes with the SDK. The build-time helper listWebMcpTools() runs in Node.js 24 or later.

Usage

import { WebMcpPlugin } from '@frontmcp/plugin-webmcp';
import { create } from '@frontmcp/sdk';

const server = await create({
  info: { name: 'shop', version: '1.0.0' },
  tools: [SearchProducts, AddToCart],
  plugins: [WebMcpPlugin.init({ prefix: 'shop.' })],
});

Register the plugin with WebMcpPlugin.init(), with or without options; plugins: [WebMcpPlugin] uses the defaults. Install it once: it offers every tool the server lists, whichever app it's registered on.

Once the server is ready, every tool it lists on the 'webmcp' surface is registered with document.modelContext.registerTool():

  • Kept in sync. Tools added or removed later, including tools added with server.registerTool() and React's useDynamicTool(), are registered or unregistered as they change.
  • Cleaned up. Every tool is unregistered when server.dispose() runs.

An agent's call runs the server's tools:call-tool flow, so hooks, authorities, quota and availableWhen apply to it as they do to any MCP client.

Options

All optional.

OptionTypeDefaultDescription
prefixstring''Prepended to every exposed tool name, e.g. 'shop.'.
include(tool) => booleanevery toolDecides which listed tools are exposed. Runs after availableWhen and authorities.
exposedTostring[]Other origins (e.g. an iframe's parent) the tools are offered to.
authContextDirectAuthContext | () => DirectAuthContextan anonymous caller, anon:webmcpWho the server sees calling. A function is called again for every listing and every call.
modelContextModelContextdocument.modelContextThe context to register with: a polyfill, or a test double.
result'structured' | 'content' | 'both''structured'What a call resolves to (see Results).

Without authContext, every call comes from an anonymous caller, so a tool that refuses anonymous callers refuses agents too. To let agents act as the person using the page, pass that user, for example authContext: () => ({ user: { sub: userId, roles: ['agent'] } }).

Choosing which tools agents see

Tools are listed through the 'webmcp' call surface, so availableWhen decides per tool:

import { Tool, ToolContext } from '@frontmcp/sdk';

@Tool({ name: 'checkout', description: 'Check out the cart', inputSchema: {}, availableWhen: { surface: ['webmcp'] } })
export class Checkout extends ToolContext {
  async execute() {
    return { ordered: true };
  }
}

@Tool({ name: 'admin_reset', description: 'Reset the store', inputSchema: {}, availableWhen: { surface: ['mcp'] } })
export class AdminReset extends ToolContext {
  async execute() {
    return { reset: true };
  }
}

checkout is offered only to in-browser agents, and admin_reset never through WebMCP.

How tools are translated

  • Names. Names become WebMCP-valid. Characters outside [A-Za-z0-9_.-] become _ (so app:tool becomes app_tool), names are cut to 128 characters, and collisions get _2, _3, …
  • Annotations. MCP's readOnlyHint maps to readOnlyHint. An explicit destructiveHint: true maps to consequentialHint, and an explicit openWorldHint: true to untrustedContentHint.
  • Results. An agent reads the whole result as text, so by default it gets each result once. With result: 'structured' (the default) a call resolves to the tool's structuredContent alone when its content only repeats it: one text block with the same JSON, or the primitive a { content } wrapper holds, as the server writes it for clients that don't read structured output. Otherwise it resolves to { content }, plus structuredContent when there is one, so text that says more and images are kept. 'content' always resolves to { content }, and 'both' to { content, structuredContent } as an MCP client gets them (the shape up to 1.9.4). _meta is never included.
  • Errors. An error result (isError), or an error from the server, rejects with its message.

Registering tools before the server loads

A server in the page is a large bundle, and starting it is one long task. To keep it off page load, compute the tool list at build time and register it in the page; the server loads on an agent's first call.

server.ts, one factory for the build script and the page:

import { WebMcpPlugin, type ModelContext } from '@frontmcp/plugin-webmcp';
import { create } from '@frontmcp/sdk';

export function createShopServer({ modelContext }: { modelContext?: ModelContext } = {}) {
  return create({
    info: { name: 'shop', version: '1.0.0' },
    tools: [SearchProducts, AddToCart],
    plugins: [WebMcpPlugin.init({ prefix: 'shop.', modelContext })],
  });
}

The build script (Node.js):

import { writeFile } from 'node:fs/promises';

import { listWebMcpTools } from '@frontmcp/plugin-webmcp';

import { createShopServer } from './server';

const tools = await listWebMcpTools((modelContext) => createShopServer({ modelContext }));
await writeFile('src/webmcp-tools.json', JSON.stringify(tools));

The page entry, a chunk without the SDK:

import { registerWebMcpTools, resolveDocumentModelContext } from '@frontmcp/plugin-webmcp/register';

import tools from './webmcp-tools.json';

void registerWebMcpTools(resolveDocumentModelContext(), tools, (modelContext) =>
  import('./server').then(({ createShopServer }) => createShopServer({ modelContext })),
);
  • listWebMcpTools(factory) builds the server with a modelContext that records what the plugin registers, returns those descriptors (name, title, description, input schema, hints) and disposes the server. The factory must pass the modelContext it is given to WebMcpPlugin.init().
  • registerWebMcpTools(modelContext, tools, loadServer, { exposedTo? }) registers the list at once. The first call loads the server with loadServer (once, however many calls arrive together) and runs through the plugin; a failed load rejects that call (a server it built is disposed, and the listed tools stay), and the next call tries again. From then on the plugin registers and unregisters tools as the server's tools change, and server.dispose() unregisters them. A listed tool the loaded server lacks is unregistered. Without a modelContext (no WebMCP) it does nothing.
  • @frontmcp/plugin-webmcp/register does not import @frontmcp/sdk; it is a few kilobytes. It exports registerWebMcpTools, resolveDocumentModelContext and isWebMcpSupported.

Browser support

WebMCP is a draft. Chrome and Edge have it behind a flag or an origin trial; for local development, turn on chrome://flags/#enable-webmcp-testing. The plugin follows the draft of 2026-09-30. See Browsers for where document.modelContext is available.

Where document.modelContext is missing, the plugin does nothing and the server works as before; isWebMcpSupported() tells you up front. To support other browsers, load a polyfill that installs document.modelContext before creating the server, such as @mcp-b/global, or pass one as modelContext.

WebMCP exposes tools only: resources and prompts stay MCP-only.

Docs

  • @frontmcp/sdk: the FrontMCP framework, including create() for a server in the page.
  • @frontmcp/react: React hooks and components for a server in the page; tools a component adds with useDynamicTool() are offered to agents while it's mounted.

Contributing

Source: plugins/plugin-webmcp. Issues and pull requests are welcome; see CONTRIBUTING.md.

License

Apache-2.0. See LICENSE.

版本与发布记录

@frontmcp/plugin-webmcp 共发布 47 个版本,首次发布于 2026/10/04;过去 12 个月发布了 47 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.12.2latest2026/11/151 天前
1.12.12026/11/132 天前
1.12.02026/11/114 天前
1.12.0-rc.1rc2026/11/114 天前
1.11.32026/11/106 天前
1.11.22026/11/096 天前

关于 @frontmcp/plugin-webmcp 的常见问题

@frontmcp/plugin-webmcp 是什么?

WebMCP plugin for FrontMCP - expose an in-browser FrontMCP server's tools to browser agents through document.modelContext

如何安装 @frontmcp/plugin-webmcp?

在项目目录运行 npm install @frontmcp/plugin-webmcp,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@frontmcp/sdk(1.12.2)、@frontmcp/lazy-zod(1.12.2)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@frontmcp/plugin-webmcp 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@frontmcp/plugin-webmcp 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@frontmcp/plugin-webmcp 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=24.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@frontmcp/plugin-webmcp 可以商用吗?

@frontmcp/plugin-webmcp 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@frontmcp/plugin-webmcp 的下载量有多少?

根据 npm 官方下载统计,@frontmcp/plugin-webmcp 在 2026/10/16 – 2026/11/14 这 30 天内被下载 5,152 次,日均约 172 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@frontmcp/plugin-webmcp 还在维护吗?

最新版本 1.12.2 发布于 2026/11/15(1 天前);至今共发布 47 个版本,其中过去 12 个月发布了 47 个;首次发布于 2026/10/04。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。