WebMCP
Every tool the workbench has registered, handed to the browser's own AI agent as MCP tools.
WebMCP is a page telling the browser what it can do:
document.modelContext.registerTool(...), so an agent living in the browser — a sidebar, an
extension, whatever the reader already has — can call it. webmcp is the bridge from codelet's own
tool registry to that: whatever anything in the workbench registered with vscode.lm.registerTool
is published, and nothing else.
import { Workbench, FileSystem } from "codelet/workbench";
import { webmcp } from "codelet/extensions/webmcp";
import { workspaceTools } from "codelet/extensions/workspace-tools";
import { typescript } from "codelet/extensions/lsp/typescript";
new Workbench({
parent: document.getElementById("app")!,
fs: new FileSystem({ "/index.ts": "export const hello = 1;\n" }),
extensions: [workspaceTools(), typescript(), webmcp()],
});Note
It publishes a registry, so mount something that fills one. On its own it publishes nothing — that's
an empty registry, not a bug. workspace-tools is the workspace's
files, lsp is what the language servers know, and any extension of your own
that registers a tool is published the same way.
#Options
webmcp({
// Install a `document.modelContext` where the browser has none. On by default, and behind a
// dynamic import — a browser with the real thing never fetches it.
polyfill: true,
// Which registered tools to publish. Everything, by default.
tools: (tool) => !tool.tags.includes("workspace_write"),
});tools is the one decision codelet can't make for you: a page agent isn't the reader, and a
workbench published on the open web may want the reading half of its tools offered and not the
writing half.
#The reader is still asked
A call from the browser goes through the same path an extension's would: the input is checked against the tool's declared schema, and a tool that asks for confirmation raises it over the workbench before it runs. So an agent driving the page reaches exactly what an extension in the page reaches, under the same questions — it doesn't get a way around them.
Tools are exposed to this window only. A workbench framed by someone else's page hands that page nothing.
#The polyfill
No shipping browser has document.modelContext without a flag, so the extension installs one unless
the browser beat it to it. It's the shape everybody's page agents were written against — the same
window.__webmcp_registered_tools registry and the same getTools() / executeTool() pair — with
results serialized to JSON the way the spec says, so a client can't tell it from native.
#Seeing what's published
WebMCP: Republish Tools in the command palette writes the list to an output channel and opens it. Useful when a tool arrived late — a language server's tools land when the server answers, not when the page loads — though the extension republishes on its own whenever the registry moves.