# WebMCP Embeds (Beta)

:::caution[Beta]
WebMCP embeds are an early-stage feature. The underlying [WebMCP proposal](https://github.com/webmachinelearning/webmcp) shipped in Chrome 146 in February 2026 and the consumer ecosystem (Chrome's built-in AI agent, browser extensions like the [Model Context Tool Inspector](https://github.com/beaufortfrancois/model-context-tool-inspector)) is still maturing. Expect rough edges and some manual flag-flipping in the browser. The producer-side API (what Wire ships) is stable; the consumer-side experience varies by browser and agent.
:::

WebMCP embeds let you drop a single `<script>` tag onto any website and expose a public Wire container's read-only tools to in-browser AI agents.

When a visitor lands on your page in a [WebMCP-capable browser](https://chromestatus.com/feature/), the shim registers `wire_explore`, `wire_search`, and `wire_navigate` with `navigator.modelContext`. The browser AI agent can then call those tools directly, with no extension or proxy.

## Requirements

- A **public** Wire container. Private and org-only containers cannot be embedded.
- Owner or admin role on the container's organization.
- Visitor browser: Chrome 146+ or Edge 147+ for native support.

## Step 1: Make the container public

In [app.usewire.io](https://app.usewire.io), open your container's **Settings** tab and set visibility to **Public**.

While the container is public, anyone can read its content via the embed surface and the public REST endpoints. Don't put secrets in a public container.

## Step 2: Mint a publishable key

1. With the container set to public, the **Embed** row appears in **Settings** below Public Access. Click **Manage Embed**.
2. Click **Create Embed Key**.
3. Optionally name the key (e.g. "Marketing site").
4. Add at least one allowed origin. The shim will only work when loaded from a page on one of these origins. Match is exact: `scheme + host + port`. No wildcards. No subdomain matching.
5. Click **Create Key**.

The publishable `pk_…` key is shown immediately along with a ready-to-paste `<script>` snippet. Both stay accessible from the Embed page indefinitely. Use the kebab menu on any key to copy the key, copy the snippet, or revoke the key.

## Step 3: Drop the snippet

Paste this into the `<head>` of any page on an allowlisted origin:

```html
<script
  src="https://embed.usewire.io/v1.js"
  data-key="pk_live_…"
  async
></script>
```

That's the whole integration. On page load the shim:

1. Reads `data-key` and validates the format.
2. Feature-detects via `'modelContext' in navigator`. If absent, logs a hint and exits.
3. Fetches `/v1/embed/tools` from the Wire API to load the schemas your key is scoped to.
4. Calls `navigator.modelContext.registerTool` for each one, with a single `AbortController` signal so all tools can be unregistered together.

## Verify it works

The fastest way to confirm your embed is registering tools is the [Model Context Tool Inspector](https://chromewebstore.google.com/detail/model-context-tool-inspec/gbpdfapgefenggkahomfgkhfehlcenpd) Chrome extension by François Beaufort. It opens a side panel that lists every tool the current page has registered, lets you inspect schemas, and lets you invoke any tool with a JSON args payload.

Setup:

1. Install the extension from the Chrome Web Store.
2. In `chrome://flags`, enable both **WebMCP** and **WebMCP for testing** (the Inspector reads via `navigator.modelContextTesting`, gated behind that flag).
3. Restart Chrome.
4. Open the page where you embedded Wire's `<script>` and click the Inspector's toolbar icon.
5. The side panel lists `wire_explore__pk_<prefix>`, `wire_search__pk_<prefix>`, `wire_navigate__pk_<prefix>`. The `__pk_<prefix>` suffix is intentional. Wire namespaces tool names so multiple embeds on one page coexist.

Pick a tool, drop in a JSON args payload (e.g. `{"query":"hello"}` for `wire_search`), click **Execute Tool**. A successful round-trip returns the canonical MCP `{ content: [{ type: "text", text: "..." }] }` shape.

You can also drive the registered tools directly from DevTools without the inspector:

```js
const tools = await navigator.modelContext.listTools();
const search = tools.find(t => t.name.startsWith('wire_search'));
const result = await navigator.modelContext.invokeTool(search.name, { query: 'hello' });
console.log(result);
```

The shim also exposes `window.wireEmbed = { registered, unregister }` so you can list registered tool names or tear them all down.

## Tool surface

Embed keys are read-only by design. The public endpoints expose three tools:

| Tool | Purpose |
|------|---------|
| `wire_explore` | Structured access to canonical data: schema, list, get by id, filter by field, keyword match. |
| `wire_search` | Fuzzy retrieval over raw content (file chunks, agent writes). Use for natural-language questions. |
| `wire_navigate` | Traverse from a `wire_search` match: adjacent chunks, the full source, or related entries. |

Other container tools (writes, deletes, analysis re-runs, plus any custom or per-entity tools that exist on the container's MCP surface) are not surfaced via embeds.

## Multiple containers on one page

Add multiple `<script>` tags, each with its own key. The shim namespaces tool names by appending the key prefix, so two embeds on the same page won't collide:

```
wire_explore__pk_live_abc1
wire_explore__pk_live_def2
```

## Origin allowlist

The allowlist is enforced server-side via the request's `Origin` header. The Wire API rejects requests where the origin doesn't appear in the key's allowlist verbatim.

If you need to support `https://example.com` and `https://www.example.com`, add both. There is no wildcard syntax.

## Rate limits and credit ceiling

Each key has a built-in per-minute rate limit and a per-day credit ceiling that protect the container owner from a viral page running up the bill. Defaults are conservative for the beta and not user-configurable yet. Each tool call costs 1 credit, charged to the container's owning organization. If the org's credit balance is exhausted, the embed gets a 402-style error and stops registering tools on subsequent loads.

## Revoking a key

The Embed page lists every active key. Open the kebab menu on a key and pick **Revoke Key**. Sites still loading the key get an authentication error on the next tool call and the shim stops registering tools on subsequent page loads.

## Security model

Embed keys are publishable on purpose: they live in client HTML on third-party sites. The defense in depth is:

1. The key only works when the parent container is `public`.
2. The origin allowlist enforces the key only runs on pages you authorize.
3. The read-only tool surface means a leaked key cannot mutate your container.
4. Per-key rate limits and credit ceilings cap blast radius if the key is misused.

## Troubleshooting

**The shim does nothing in my browser.**
WebMCP shipped in Chrome 146 (Feb 2026, Edge 147 followed). Older browsers see a single console hint and exit.

**`Origin not allowed`.**
Your page's origin isn't in the key's allowlist. Add it from the Embed page. Trailing slashes and paths are normalized; only `scheme + host + port` matter.

**`Invalid or revoked embed key`.**
The container is no longer public, or the key was revoked, or the key string is malformed. Mint a new one or flip the container back to public.

**`Container owner has run out of credits`.**
The org's credit balance is exhausted. Top up credits in billing.

## Limits

- One container per `<script>` tag. Multiple tags equals multiple keys.
- Read-only tools only.
- Public containers only.
- Chrome 146+ / Edge 147+ for native support.
- Exact origin match only.