WebMCP Marketplace Plugins
A marketplace plugin is a GitHub repo (or local folder) containing a webmcp-bridge.json manifest and JavaScript bridge scripts. Scripts are IIFEs that call navigator.modelContext.registerTool() to add MCP tools to websites.
Quick Start
Minimal plugin structure:
my-plugin/
├── webmcp-bridge.json
└── scripts/
└── main.js
webmcp-bridge.json:
{
"name": "my-plugin",
"version": "1.0.0",
"description": "What this plugin does",
"author": "your-name",
"license": "MIT",
"scripts": [
{
"id": "main",
"name": "Main Script",
"description": "Brief description",
"file": "scripts/main.js",
"matches": ["*://example.com/*"],
"runAt": "document_idle"
}
]
}
scripts/main.js:
(function () {
"use strict";
if (!navigator.modelContext) return;
navigator.modelContext.registerTool({
name: "my_tool",
description: "What this tool does",
inputSchema: {
type: "object",
properties: {
query: { type: "string", description: "Parameter description" },
},
},
execute: async (args) => {
// DOM access, fetch, localStorage — all standard browser APIs available
return { result: "Tool output" };
},
});
})();
Manifest Reference
Top-level fields
| Field | Required | Type | Description |
|---|---|---|---|
name |
Yes | string | Package identifier (non-empty) |
version |
Yes | string | Semver (e.g. "1.0.0") |
description |
No | string | Shown in plugin management UI |
author |
No | string | Author name |
license |
No | string | SPDX identifier |
scripts |
Yes | array | Non-empty array of script entries |
Script entry fields
| Field | Required | Type | Description |
|---|---|---|---|
id |
Yes | string | Unique within the package |
name |
No | string | Human-readable label for UI |
description |
No | string | Brief description |
file |
Yes | string | Path relative to manifest |
matches |
Yes | string[] | URL match patterns (non-empty) |
runAt |
No | string | "document_idle" (default) or "document_start" |
Match Patterns
Chrome extension match pattern syntax: scheme://host/path
| Pattern | Matches |
|---|---|
<all_urls> |
All HTTP/HTTPS URLs |
*://mail.google.com/* |
Gmail (HTTP or HTTPS) |
https://github.com/* |
GitHub HTTPS only |
*://*.google.com/* |
All Google subdomains |
https://example.com/app/* |
Specific path prefix |
- Scheme:
http,https, or*(both) - Host: exact (
example.com), wildcard subdomains (*.example.com), or*(any) - Path:
/*(any), or a specific prefix
Script Authoring
Requirements
- Wrap in an IIFE:
(function () { "use strict"; ... })(); - Guard with
if (!navigator.modelContext) return; - Use
inputSchema(notschema) in the tool descriptor - The
executecallback receives a plain args object, return the result
WebMCP API
navigator.modelContext.registerTool(descriptor); // Register one tool
navigator.modelContext.unregisterTool(name); // Remove by name
navigator.modelContext.provideContext({ tools: [] }); // Batch register (array)
navigator.modelContext.clearContext(); // Remove all tools
Execution context
- Scripts run in the MAIN world (full page JS/DOM access)
- Injected via
chrome.scripting.executeScript({ world: 'MAIN' }) - The extension's
content-main.jswrapsModelContext.prototype.registerToolatdocument_start, intercepting all registrations automatically - Scripts re-inject on navigation; no cleanup needed
runAt timing
document_idle(default): Injected after page load complete. Use for most plugins.document_start: Injected during loading. Use only if intercepting early page behavior.
Common patterns
DOM scraping tool:
execute: async () => {
const items = [...document.querySelectorAll(".item")];
return { items: items.map((el) => ({ text: el.textContent, href: el.href })) };
};
Form interaction tool:
execute: async (args) => {
document.querySelector("#search-input").value = args.query;
document.querySelector("#search-form").submit();
return { status: "submitted" };
};
Multiple tools in one script:
(function () {
"use strict";
if (!navigator.modelContext) return;
navigator.modelContext.registerTool({ name: "tool_a" /* ... */ });
navigator.modelContext.registerTool({ name: "tool_b" /* ... */ });
})();
Validation
Scripts are scanned for dangerous patterns on install. See references/validation-rules.md for the complete list of blocked patterns and obfuscation thresholds.
Key constraints:
- No
eval(),new Function(),document.write() - No dynamic
<script>creation - No WebSocket connections to non-localhost hosts
- No
setAttribute("on...")for inline event handlers - Obfuscated code (heavy hex/unicode escapes, minified lines) triggers warnings
- Syntax errors are NOT caught at install time (extension CSP blocks
new Function); they surface at injection time
Testing
Local folder install
- Create folder with
webmcp-bridge.json+ scripts - Open extension popup > Manage Plugins
- Click Load Folder, select the folder
- Navigate to a URL matching your patterns
- Tools appear in the extension badge and your MCP client
Dev browser
bun run chrome # Launch Chrome with extension pinned
bun run chrome:dev # Chrome + CLI together
GitHub install
Push to GitHub, then install via user/repo@ref in the plugins page:
user/repo@v1.0.0 # tag (recommended)
user/repo@main # branch
user/repo # latest release or default branch
Working Example
See examples/marketplace/ in the webmcp-bridge repo for a complete working plugin that registers a hello_world tool on all URLs.
Converted and distributed by TomeVault — claim your Tome and manage your conversions.