WebMCP Integration
- internal/wiki/webmcp/webmcp.js
- internal/wiki/webmcp/astrolabe-mcp.js
Relevant source files · 2
The internal/wiki/webmcp directory ships the JavaScript that turns a generated wiki site into something an AI agent can use, not just read. Two files cooperate: webmcp.js is a vendored copy of the webmcp.dev widget — a small floating button that opens a WebSocket bridge so a local MCP client (Claude Desktop, Cursor, …) can discover and call tools on the page Label. astrolabe-mcp.js is Astrolabe's own script: it defines a set of read-only tools over the wiki bundle and exposes them two ways — through the W3C WebMCP document.modelContext API for browser-native agents, and through the widget when it is present Label. The result is that a reader's own agent can answer questions about the codebase using exactly the same bundle the human sees.
How it works
Everything starts from the static bundle. All tool data comes from plain files — index.json, graph.json, version.json, pages/<slug>.md and the src/ snapshot — fetched once and memoized in a small cache keyed by path and format, so repeated tool calls don't re-download the bundle Label. Because fetching requires http(s), a wiki opened from file:// can serve prose but not tools.
The tools
The script builds a tools array of seven read-only functions over that data Label:
| Tool | What it does |
|---|---|
astrolabe_overview |
Lists the repo, commit/branch/tag and every page with type, scope, symbols and verification status Label |
astrolabe_read_page |
Returns one page as Markdown (flagged untrusted — it's remote page content) Label |
astrolabe_search_wiki |
Paragraph-level full-text search with term-frequency scoring, page slug and nearest heading in the results Label |
astrolabe_search_symbols |
Substring search over graph.json nodes, ranked with exact matches first Label |
astrolabe_symbol_details |
One symbol: location, commit-pinned source link, documenting page, and all in/out graph edges Label |
astrolabe_read_source |
Numbered source lines from the src/ snapshot; rejects .. traversal and falls back to a forge URL when the bundle links out instead of shipping source Label |
Source links are resolved by sourceLink, which either fills the {path}/{line}/{endline} template recorded in index.json (a commit-pinned code-host URL) or points into the bundle's own src/ snapshot with an #L fragment Label. This mirrors the citation model of the Wiki Bundle & Pages generator: whatever the wiki cites, the agent can read.
Two registration surfaces
At load time init() tries both surfaces and records the outcome on window.AstrolabeMCP Label:
- W3C WebMCP.
registerSpecregisters each tool ondocument.modelContext, falling back to the legacynavigator.modelContext(the MCP-B polyfill surface), and annotates each tool withreadOnlyHint: trueanduntrustedContentHintfor tools returning wiki content Label. - webmcp.dev widget. If
webmcp.jswas loaded first,registerWidgetinstantiatesWebMCPtinted with the theme's--accentcolor and registers the same tools with aregisterTool(name, description, schema, fn)signature Label.
In both cases the execute wrapper normalizes results to MCP text content blocks.
The widget bridge
webmcp.js is a self-contained class. On construction it injects a small trigger button plus a connection panel into the page (guarding against double initialization) Label, and immediately checks sessionStorage for a token from a previous page visit so navigation doesn't drop the connection Label.
Connecting is a two-step handshake. The reader pastes a base64 token from npx @jason.today/webmcp; _processConnectionToken decodes it into {server, token} and derives the channel name from the page's hostname Label. The script then registers with a relay over a /register WebSocket, sending the token plus the formatted host and receiving a session token back Label, before opening the real channel socket at server/channel?token=… Label. Registration is skipped entirely when the stored connection info matches the current server and host Label.
Once connected, the widget becomes a thin MCP relay. Server-initiated messages are dispatched to _handleToolCall, which looks the tool up in the local registry, executes it (resolving Promises asynchronously), and replies with a toolResponse — or an error payload if the tool is missing or throws Label. Prompt and resource requests follow the same pattern. Tools reach that registry via the public registerTool, which stores name/description/schema/execute locally, announces the tool to the server if already connected, and persists it to sessionStorage so reconnection restores it Label. On reconnect, _registerItemsWithServer replays every registered tool, prompt and resource Label. A safety valve disconnects after five minutes of inactivity (configurable via inactivityTimeout) Label. Disconnection clears the socket, the stored token and the saved registrations Label.
sequenceDiagram
participant Agent as MCP client / browser agent
participant Page as wiki page
participant MCP as astrolabe-mcp.js
participant Widget as WebMCP widget
participant Relay as webmcp relay
Page->>MCP: load with data-root
MCP->>MCP: fetch + cache index.json, graph.json, pages/
MCP->>Page: document.modelContext.registerTool ×7
MCP->>Widget: registerTool ×7 (if webmcp.js loaded)
Agent->>Relay: token handshake (/register)
Relay-->>Widget: session token
Widget->>Relay: open channel WebSocket
Agent->>Relay: tool call
Relay->>Widget: _handleToolCall
Widget->>MCP: execute cached tool
Widget->>Relay: toolResponseTrust and safety notes
Two properties are deliberate. First, all tools are read-only: the annotations sent to the W3C surface declare readOnlyHint for every tool Label, and none of the runners mutate anything. Second, tools that return wiki-authored prose (astrolabe_read_page, astrolabe_search_wiki, astrolabe_read_source) are marked untrusted so consuming agents treat the content as data, not instructions Label. Input handling also defends the bundle itself: page slugs are validated against a strict pattern Label and source paths are stripped of leading slashes and rejected if they contain .. Label.
Reference
| Symbol | Kind | What it does | Location |
|---|---|---|---|
WebMCP (class) |
class | Widget: token handshake, WebSocket relay to MCP clients, tool/prompt/resource registry, session persistence | internal/wiki/webmcp/webmcp.js#L8 |
WebMCP#connect |
method | Decodes token, registers with relay, opens channel WebSocket | internal/wiki/webmcp/webmcp.js#L895 |
WebMCP#_registerWithServer |
method | /register handshake exchanging the pasted token for a session token |
internal/wiki/webmcp/webmcp.js#L1097 |
WebMCP#_handleToolCall |
method | Dispatches server tool calls to the local registry and replies toolResponse |
internal/wiki/webmcp/webmcp.js#L1268 |
WebMCP#registerTool |
method | Public tool registration; persists to sessionStorage and announces to the server |
internal/wiki/webmcp/webmcp.js#L1632 |
tools |
array | The seven read-only Astrolabe tools over the wiki bundle | internal/wiki/webmcp/astrolabe-mcp.js#L87 |
sourceLink |
function | Builds commit-pinned source URLs from index.json template or the src/ snapshot |
internal/wiki/webmcp/astrolabe-mcp.js#L70 |
registerSpec |
function | Registers tools on document.modelContext (or legacy navigator.modelContext) |
internal/wiki/webmcp/astrolabe-mcp.js#L219 |
registerWidget |
function | Registers the same tools on the webmcp.dev widget when present | internal/wiki/webmcp/astrolabe-mcp.js#L239 |
init |
function | Entry point; wires both surfaces and exposes window.AstrolabeMCP |
internal/wiki/webmcp/astrolabe-mcp.js#L253 |
Generated by astrolabe from commit ecc180b3 (tag 0.4.0).