@frontmcp/plugin-webmcp 1.9.6 → 1.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,16 +1,22 @@
1
1
  # @frontmcp/plugin-webmcp
2
2
 
3
- Expose the tools of a FrontMCP server that runs **in the page** to in-browser agents through
4
- [WebMCP](https://webmachinelearning.github.io/webmcp/) (`document.modelContext`). Agents such as
5
- Gemini in Chrome, the Model Context Tool Inspector extension, and DevTools' WebMCP panel can then
6
- discover and call them.
3
+ Offers the tools of a [FrontMCP](https://frontmcp.dev) server that runs **in the page** to the agents in the user's
4
+ browser, through [WebMCP](https://webmachinelearning.github.io/webmcp/) (`document.modelContext`): the browser's own
5
+ assistant, an extension, or the developer tools. Use it on a website that creates a FrontMCP server in the page, often
6
+ under `@frontmcp/react`. The plugin registers the server's tools, keeps them in step as tools come and go, and runs each
7
+ agent's call through the server's own flow.
7
8
 
8
- ## Installation
9
+ [![npm](https://img.shields.io/npm/v/@frontmcp/plugin-webmcp.svg)](https://www.npmjs.com/package/@frontmcp/plugin-webmcp) [![License](https://img.shields.io/npm/l/@frontmcp/plugin-webmcp.svg)](https://github.com/agentfront/frontmcp/blob/main/LICENSE)
10
+
11
+ ## Install
9
12
 
10
13
  ```bash
11
- npm install @frontmcp/plugin-webmcp
14
+ npm install @frontmcp/sdk @frontmcp/plugin-webmcp
12
15
  ```
13
16
 
17
+ Use the same version for both packages. The plugin's peer dependency `@frontmcp/lazy-zod` comes with the SDK. The
18
+ build-time helper `listWebMcpTools()` runs in Node.js 24 or later.
19
+
14
20
  ## Usage
15
21
 
16
22
  ```typescript
@@ -24,44 +30,66 @@ const server = await create({
24
30
  });
25
31
  ```
26
32
 
27
- Install the plugin with `WebMcpPlugin.init()` (with or without options).
33
+ Register the plugin with `WebMcpPlugin.init()`, with or without options; `plugins: [WebMcpPlugin]` uses the defaults.
34
+ Install it once: it offers every tool the server lists, whichever app it's registered on.
28
35
 
29
36
  Once the server is ready, every tool it lists on the `'webmcp'` surface is registered with
30
37
  `document.modelContext.registerTool()`:
31
38
 
32
- - **Kept in sync.** Tools added or removed later, including tools added with `server.registerTool()`
33
- and React's `useDynamicTool`, are registered or unregistered as they change.
39
+ - **Kept in sync.** Tools added or removed later, including tools added with `server.registerTool()` and React's
40
+ `useDynamicTool()`, are registered or unregistered as they change.
34
41
  - **Cleaned up.** Every tool is unregistered when `server.dispose()` runs.
35
42
 
36
- An agent's call runs the server's `tools:call-tool` flow, so hooks, authorities, quota and
37
- `availableWhen` apply to it as they do to any MCP client.
43
+ An agent's call runs the server's `tools:call-tool` flow, so hooks, authorities, quota and `availableWhen` apply to it
44
+ as they do to any MCP client.
38
45
 
39
46
  ## Options
40
47
 
41
- | Option | Type | Default | Description |
42
- | -------------- | ---------------------------------------------- | ------------------------- | ------------------------------------------------------------------- |
43
- | `prefix` | `string` | `''` | Prepended to every exposed tool name, e.g. `'shop.'`. |
44
- | `include` | `(tool) => boolean` | all | Decides which listed tools are exposed. |
45
- | `exposedTo` | `string[]` | — | Other origins (e.g. an iframe's parent) the tools are offered to. |
46
- | `authContext` | `DirectAuthContext \| () => DirectAuthContext` | anonymous `webmcp` caller | Who the server sees calling. A function is resolved on every call. |
47
- | `modelContext` | `ModelContext` | `document.modelContext` | The context to register with: a polyfill, or a test double. |
48
- | `result` | `'structured' \| 'content' \| 'both'` | `'structured'` | What a call resolves to (see [Results](#how-tools-are-translated)). |
48
+ All optional.
49
+
50
+ | Option | Type | Default | Description |
51
+ | -------------- | ---------------------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------- |
52
+ | `prefix` | `string` | `''` | Prepended to every exposed tool name, e.g. `'shop.'`. |
53
+ | `include` | `(tool) => boolean` | every tool | Decides which listed tools are exposed. Runs after `availableWhen` and authorities. |
54
+ | `exposedTo` | `string[]` | | Other origins (e.g. an iframe's parent) the tools are offered to. |
55
+ | `authContext` | `DirectAuthContext \| () => DirectAuthContext` | an anonymous caller, `anon:webmcp` | Who the server sees calling. A function is called again for every listing and every call. |
56
+ | `modelContext` | `ModelContext` | `document.modelContext` | The context to register with: a polyfill, or a test double. |
57
+ | `result` | `'structured' \| 'content' \| 'both'` | `'structured'` | What a call resolves to (see [Results](#how-tools-are-translated)). |
58
+
59
+ Without `authContext`, every call comes from an anonymous caller, so a tool that refuses anonymous callers refuses
60
+ agents too. To let agents act as the person using the page, pass that user, for example
61
+ `authContext: () => ({ user: { sub: userId, roles: ['agent'] } })`.
49
62
 
50
63
  ## Choosing which tools agents see
51
64
 
52
65
  Tools are listed through the `'webmcp'` call surface, so `availableWhen` decides per tool:
53
66
 
54
67
  ```typescript
55
- @Tool({ name: 'checkout', availableWhen: { surface: ['webmcp'] } }) // in-browser agents only
56
- @Tool({ name: 'admin_reset', availableWhen: { surface: ['mcp'] } }) // never exposed through WebMCP
68
+ import { Tool, ToolContext } from '@frontmcp/sdk';
69
+
70
+ @Tool({ name: 'checkout', description: 'Check out the cart', inputSchema: {}, availableWhen: { surface: ['webmcp'] } })
71
+ export class Checkout extends ToolContext {
72
+ async execute() {
73
+ return { ordered: true };
74
+ }
75
+ }
76
+
77
+ @Tool({ name: 'admin_reset', description: 'Reset the store', inputSchema: {}, availableWhen: { surface: ['mcp'] } })
78
+ export class AdminReset extends ToolContext {
79
+ async execute() {
80
+ return { reset: true };
81
+ }
82
+ }
57
83
  ```
58
84
 
85
+ `checkout` is offered only to in-browser agents, and `admin_reset` never through WebMCP.
86
+
59
87
  ## How tools are translated
60
88
 
61
- - **Names.** Names become WebMCP-valid. Characters outside `[A-Za-z0-9_.-]` become `_` (so
62
- `app:tool` becomes `app_tool`), names are cut to 128 characters, and collisions get `_2`, `_3`, …
63
- - **Annotations.** MCP's `readOnlyHint` maps to `readOnlyHint`. An explicit `destructiveHint: true`
64
- maps to `consequentialHint`, and an explicit `openWorldHint: true` to `untrustedContentHint`.
89
+ - **Names.** Names become WebMCP-valid. Characters outside `[A-Za-z0-9_.-]` become `_` (so `app:tool` becomes
90
+ `app_tool`), names are cut to 128 characters, and collisions get `_2`, `_3`, …
91
+ - **Annotations.** MCP's `readOnlyHint` maps to `readOnlyHint`. An explicit `destructiveHint: true` maps to
92
+ `consequentialHint`, and an explicit `openWorldHint: true` to `untrustedContentHint`.
65
93
  - **Results.** An agent reads the whole result as text, so by default it gets each result once. With
66
94
  `result: 'structured'` (the default) a call resolves to the tool's `structuredContent` alone when its content only
67
95
  repeats it: one text block with the same JSON, or the primitive a `{ content }` wrapper holds, as the server writes
@@ -73,12 +101,15 @@ Tools are listed through the `'webmcp'` call surface, so `availableWhen` decides
73
101
 
74
102
  ## Registering tools before the server loads
75
103
 
76
- A server in the page is a large bundle, and starting it is one long task. To keep it off page load, compute the
77
- tool list at build time and register it in the page; the server loads on an agent's first call:
104
+ 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
105
+ list at build time and register it in the page; the server loads on an agent's first call.
78
106
 
79
107
  `server.ts`, one factory for the build script and the page:
80
108
 
81
109
  ```typescript
110
+ import { WebMcpPlugin, type ModelContext } from '@frontmcp/plugin-webmcp';
111
+ import { create } from '@frontmcp/sdk';
112
+
82
113
  export function createShopServer({ modelContext }: { modelContext?: ModelContext } = {}) {
83
114
  return create({
84
115
  info: { name: 'shop', version: '1.0.0' },
@@ -88,11 +119,15 @@ export function createShopServer({ modelContext }: { modelContext?: ModelContext
88
119
  }
89
120
  ```
90
121
 
91
- The build script (Node):
122
+ The build script (Node.js):
92
123
 
93
124
  ```typescript
125
+ import { writeFile } from 'node:fs/promises';
126
+
94
127
  import { listWebMcpTools } from '@frontmcp/plugin-webmcp';
95
128
 
129
+ import { createShopServer } from './server';
130
+
96
131
  const tools = await listWebMcpTools((modelContext) => createShopServer({ modelContext }));
97
132
  await writeFile('src/webmcp-tools.json', JSON.stringify(tools));
98
133
  ```
@@ -109,24 +144,50 @@ void registerWebMcpTools(resolveDocumentModelContext(), tools, (modelContext) =>
109
144
  );
110
145
  ```
111
146
 
112
- - `listWebMcpTools(factory)` builds the server with a `modelContext` that records what the plugin registers,
113
- returns those descriptors (name, title, description, input schema, hints) and disposes the server. The factory
114
- must pass the `modelContext` it is given to `WebMcpPlugin.init()`.
147
+ - `listWebMcpTools(factory)` builds the server with a `modelContext` that records what the plugin registers, returns
148
+ those descriptors (name, title, description, input schema, hints) and disposes the server. The factory must pass the
149
+ `modelContext` it is given to `WebMcpPlugin.init()`.
115
150
  - `registerWebMcpTools(modelContext, tools, loadServer, { exposedTo? })` registers the list at once. The first call
116
151
  loads the server with `loadServer` (once, however many calls arrive together) and runs through the plugin; a failed
117
- 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
118
- the server's tools change, and `server.dispose()` unregisters them. A listed tool the loaded server lacks is
119
- unregistered. Without a `modelContext` (no WebMCP) it does nothing.
120
- - `@frontmcp/plugin-webmcp/register` does not import `@frontmcp/sdk`; it is a few kilobytes.
152
+ load rejects that call (a server it built is disposed, and the listed tools stay), and the next call tries again.
153
+ From then on the plugin registers and unregisters tools as the server's tools change, and `server.dispose()`
154
+ unregisters them. A listed tool the loaded server lacks is unregistered. Without a `modelContext` (no WebMCP) it does
155
+ nothing.
156
+ - `@frontmcp/plugin-webmcp/register` does not import `@frontmcp/sdk`; it is a few kilobytes. It exports
157
+ `registerWebMcpTools`, `resolveDocumentModelContext` and `isWebMcpSupported`.
121
158
 
122
159
  ## Browser support
123
160
 
124
- WebMCP is in origin trial in Chrome and Edge (Chrome 149+). For local development, enable
125
- `chrome://flags/#enable-webmcp-testing`.
161
+ WebMCP is a draft. Chrome and Edge have it behind a flag or an origin trial; for local development, turn on
162
+ `chrome://flags/#enable-webmcp-testing`. The plugin follows the draft of 2026-09-30. See
163
+ [Browsers](https://frontmcp.dev/reference/plugins/webmcp#browsers) for where `document.modelContext` is available.
126
164
 
127
- Where `document.modelContext` is missing, the plugin does nothing; `isWebMcpSupported()` tells you
128
- up front. To support other browsers, load a polyfill that installs `document.modelContext` before
129
- creating the server, such as [`@mcp-b/global`](https://github.com/WebMCP-org/npm-packages) (which
130
- also bridges the page's tools to the MCP-B browser extension), or pass one as `modelContext`.
165
+ Where `document.modelContext` is missing, the plugin does nothing and the server works as before;
166
+ `isWebMcpSupported()` tells you up front. To support other browsers, load a polyfill that installs
167
+ `document.modelContext` before creating the server, such as
168
+ [`@mcp-b/global`](https://www.npmjs.com/package/@mcp-b/global), or pass one as `modelContext`.
131
169
 
132
170
  WebMCP exposes tools only: resources and prompts stay MCP-only.
171
+
172
+ ## Docs
173
+
174
+ - [WebMCP plugin reference](https://frontmcp.dev/reference/plugins/webmcp): options, translation, who the server sees
175
+ calling, and troubleshooting.
176
+ - [Running in the browser](https://frontmcp.dev/reference/deployment/browser): what a FrontMCP server can do in a web
177
+ page.
178
+
179
+ ## Related packages
180
+
181
+ - [`@frontmcp/sdk`](https://www.npmjs.com/package/@frontmcp/sdk): the FrontMCP framework, including `create()` for a
182
+ server in the page.
183
+ - [`@frontmcp/react`](https://www.npmjs.com/package/@frontmcp/react): React hooks and components for a server in the
184
+ page; tools a component adds with `useDynamicTool()` are offered to agents while it's mounted.
185
+
186
+ ## Contributing
187
+
188
+ Source: [`plugins/plugin-webmcp`](https://github.com/agentfront/frontmcp/tree/main/plugins/plugin-webmcp). Issues and
189
+ pull requests are welcome; see [CONTRIBUTING.md](https://github.com/agentfront/frontmcp/blob/main/CONTRIBUTING.md).
190
+
191
+ ## License
192
+
193
+ Apache-2.0. See [LICENSE](https://github.com/agentfront/frontmcp/blob/main/LICENSE).
package/esm/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/plugin-webmcp",
3
- "version": "1.9.6",
3
+ "version": "1.9.7",
4
4
  "description": "WebMCP plugin for FrontMCP - expose an in-browser FrontMCP server's tools to browser agents through document.modelContext",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "license": "Apache-2.0",
@@ -8,13 +8,14 @@
8
8
  "node": ">=24.0.0"
9
9
  },
10
10
  "keywords": [
11
+ "frontmcp",
11
12
  "mcp",
13
+ "model-context-protocol",
12
14
  "webmcp",
13
15
  "modelcontext",
14
16
  "browser",
15
17
  "agents",
16
18
  "plugin",
17
- "frontmcp",
18
19
  "agentfront"
19
20
  ],
20
21
  "repository": {
@@ -59,13 +60,14 @@
59
60
  }
60
61
  },
61
62
  "dependencies": {
62
- "@frontmcp/sdk": "1.9.6",
63
- "@frontmcp/utils": "1.9.6"
63
+ "@frontmcp/utils": "1.9.7"
64
64
  },
65
65
  "peerDependencies": {
66
- "@frontmcp/lazy-zod": "1.9.6"
66
+ "@frontmcp/lazy-zod": "1.9.7",
67
+ "@frontmcp/sdk": "1.9.7"
67
68
  },
68
69
  "devDependencies": {
70
+ "@frontmcp/sdk": "1.9.7",
69
71
  "reflect-metadata": "^0.2.2"
70
72
  }
71
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/plugin-webmcp",
3
- "version": "1.9.6",
3
+ "version": "1.9.7",
4
4
  "description": "WebMCP plugin for FrontMCP - expose an in-browser FrontMCP server's tools to browser agents through document.modelContext",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "license": "Apache-2.0",
@@ -8,13 +8,14 @@
8
8
  "node": ">=24.0.0"
9
9
  },
10
10
  "keywords": [
11
+ "frontmcp",
11
12
  "mcp",
13
+ "model-context-protocol",
12
14
  "webmcp",
13
15
  "modelcontext",
14
16
  "browser",
15
17
  "agents",
16
18
  "plugin",
17
- "frontmcp",
18
19
  "agentfront"
19
20
  ],
20
21
  "repository": {
@@ -59,13 +60,14 @@
59
60
  }
60
61
  },
61
62
  "dependencies": {
62
- "@frontmcp/sdk": "1.9.6",
63
- "@frontmcp/utils": "1.9.6"
63
+ "@frontmcp/utils": "1.9.7"
64
64
  },
65
65
  "peerDependencies": {
66
- "@frontmcp/lazy-zod": "1.9.6"
66
+ "@frontmcp/lazy-zod": "1.9.7",
67
+ "@frontmcp/sdk": "1.9.7"
67
68
  },
68
69
  "devDependencies": {
70
+ "@frontmcp/sdk": "1.9.7",
69
71
  "reflect-metadata": "^0.2.2"
70
72
  }
71
73
  }