Open-source interactive UI widget registry for Docsbook documentation chats.
Widgets render inline in the chat as interactive UI panels — powered by the MCP Apps standard (io.modelcontextprotocol/ui, spec 2026-01-26). Built-in widgets are active by default; users can connect their own widget repositories and enable/disable any widget from the Docsbook admin panel.
Works in Docsbook chat, ChatGPT (confirmed), and Claude.ai (officially supported).
- Each widget is a MCP tool with a
_meta.ui.resourceUripointing to a self-contained HTML file. - The LLM reads the widget's
triggersanddescription, then decides when to call the tool. - The host (Docsbook / ChatGPT / Claude.ai) renders the HTML in a sandboxed iframe inline in the conversation.
- The widget communicates with the host via the
@modelcontextprotocol/ext-appsAppclass overpostMessage.
docs-widgets/
widgets/
<widget-name>/
WIDGET.md # Frontmatter (metadata) + prose description the LLM reads
widget.html # Self-contained HTML widget
schema/
widget.schema.json # JSON Schema for WIDGET.md frontmatter
index.json # Generated widget catalog (consumed by Docsbook)
---
name: my-widget # Unique kebab-case identifier (required)
description: "..." # Human-readable description + when to use (required, used as MCP tool description)
version: 1.0.0 # Semver (required)
category: developer # visualization|forms|navigation|ai|developer|analytics|media (required)
modes:
- user # admin|user — which chat context shows this widget (required, at least one)
triggers:
- show my widget # Keyword phrases the LLM matches to activate (required, at least one)
mcp_resource_uri: "ui://my-widget/render" # The ui:// URI stem (required)
keywords: # Discovery keywords for scoring (optional)
- keyword1
discriminators: # Exact high-precision phrases like proper nouns (optional)
- ExactName
requires_plan: pro # pro|pro_plus — gate on Docsbook plan (optional)
built_in: false # true = official Docsbook widget (default: false)
author: Your Name # Optional
repository: https://github.com/you/your-widgets # Optional
homepage: https://yoursite.com # Optional
---| Value | When shown |
|---|---|
user |
End-user documentation chat (public visitors reading your docs) |
admin |
Docsbook admin chat (only you / your team) |
triggers become part of the MCP tool description that the LLM reads. The LLM matches the user's message against these phrases (and description, keywords, discriminators) to decide when to render the widget. Write them as short action phrases:
triggers:
- show API reference
- view Swagger documentation
- explore REST endpointsmy-widgets/
widgets/
openapi-renderer/
WIDGET.md
widget.html
index.json
package.json
Start with the YAML frontmatter (see reference above), followed by a prose description the LLM will read to understand what the widget does and when to use it. Be specific about input format and output.
The HTML file must be fully self-contained (all JS/CSS inlined or from CDN). Use the @modelcontextprotocol/ext-apps App class to receive data from the host:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>My Widget</title>
</head>
<body>
<div id="output"></div>
<script type="module">
import { App } from 'https://cdn.jsdelivr.net/npm/@modelcontextprotocol/ext-apps@latest/dist/app.js';
const app = new App();
// Receive data pushed from the LLM tool result
app.on('toolResult', (result) => {
document.getElementById('output').textContent = JSON.stringify(result, null, 2);
});
// Receive initial params on first render
app.on('initialize', (params) => {
if (params?.data) {
document.getElementById('output').textContent = JSON.stringify(params.data, null, 2);
}
});
// Call a tool back on the MCP server from user interaction
document.getElementById('refresh-btn')?.addEventListener('click', async () => {
const data = await app.callTool('my_tool', { param: 'value' });
document.getElementById('output').textContent = JSON.stringify(data, null, 2);
});
</script>
</body>
</html>MCP Apps postMessage flow:
- Host renders your HTML in a sandboxed
<iframe srcDoc="..."> - Host sends
ui/initializeviapostMessage→Appclass receives it - LLM tool result is pushed to iframe as
toolResultevent - Widget can call
app.callTool(name, args)to invoke MCP tools - Widget can call
app.sendMessage(text)to add context to the LLM conversation
Generate (or hand-write) a catalog file listing all widgets. Each entry mirrors the WIDGET.md frontmatter plus raw_url and widget_url:
[
{
"name": "my-widget",
"description": "...",
"version": "1.0.0",
"category": "developer",
"modes": ["user"],
"triggers": ["show my widget"],
"keywords": ["my", "widget"],
"discriminators": [],
"mcp_resource_uri": "ui://my-widget/render",
"requires_plan": null,
"built_in": false,
"author": "Your Name",
"repository": "https://github.com/you/my-widgets",
"raw_url": "https://raw.githubusercontent.com/you/my-widgets/main/widgets/my-widget/WIDGET.md",
"widget_url": "https://raw.githubusercontent.com/you/my-widgets/main/widgets/my-widget/widget.html"
}
]Host your index.json at a public URL (GitHub raw, npm, or any CDN), then connect it in Docsbook Admin → Widgets → Add widget repository. Docsbook will fetch your index and surface your widgets in the chat.
To publish to npm for easy versioning:
// package.json
{
"name": "@yourscope/widgets",
"version": "1.0.0",
"main": "index.json",
"files": ["widgets/", "index.json"]
}| Host | Status |
|---|---|
| Docsbook chat | Native — full integration |
| ChatGPT | Confirmed — renders via MCP Apps standard |
| Claude.ai | Officially supported (minor rendering bugs in some environments as of 2026) |
| VS Code (Continue, Copilot) | Partial — depends on client MCP Apps support |
The MCP Apps spec (io.modelcontextprotocol/ui) is ratified as of 2026-01-26 and is the single recommended authoring format for widgets that need to work across multiple LLM hosts.
| Widget | Category | Modes | Description |
|---|---|---|---|
openapi-renderer |
developer | user | Interactive Swagger UI for OpenAPI specs |
PRs welcome. Each widget lives in widgets/<name>/ with a WIDGET.md and widget.html. Open an issue first for new categories or schema changes.
MIT