feat(mcp-apps): experimental inline view for Alexandria results (off by default)
Spike, draft. This server returns no UI today. It adds one MCP Apps view behind a flag so we can try server-provided inline UI for Alexandria in ChatGPT and Claude.
What the clients need (Oct 2026)
There are two separate things called "inline UI".
- Model-generated: ChatGPT's GPT-6 "Intelligent UI" (launched 10-07) and Claude's in-chat visuals. The model composes charts, forms and widgets from its own component library, using whatever data it has. That data includes our tool results, so the "one prompt, something visual" demo works today with no server change.
- Server-provided, MCP Apps (
io.modelcontextprotocol/ui, stable 2026-01-26): ChatGPT and Claude both render it. A tool declares_meta.ui.resourceUripointing at aui://resource served astext/html;profile=mcp-app. The host renders it in a sandboxed iframe and sends it the tool result over theui/*postMessage bridge. ChatGPT also acceptsopenai/outputTemplateandwindow.openaias aliases, but recommends the standard keys. Claude's help center says it uses a connector's interactive interface when one fits, and generates its own visuals only otherwise.
What this PR adds
When FIRECRAWL_MCP_APPS=true is set (or mcpApps: true for createFirecrawlMcpServer embedders):
firecrawl_scrapegets_meta.ui.resourceUri = "ui://firecrawl/alexandria-results.html", added next to the existinganthropic/alwaysLoad.- The server serves that resource: a self-contained HTML view with no external scripts, styles or network access, so it needs no CSP domains. The view runs the
ui/initializehandshake and readsstructuredContentfromui/notifications/tool-result. It renders each Alexandria call as a table (up to 50 rows and 6 scalar columns). When one column is numeric it adds a bar chart, and when a call fails it shows the error. It follows the host's theme variables and reports its size. - Tool inputs, outputs and text content are unchanged, and the flag is off by default, so the contract snapshots don't change. Hosts without MCP Apps ignore
_meta.ui.
The screenshot comes from a local host harness that runs the real ui/initialize handshake and tool-result flow with a FRED result. It is not a ChatGPT or Claude session: testing in those needs this build deployed behind a connector.
Known limits before this could leave the flag
- The view is bound to every
firecrawl_scrapecall, not just Alexandria ones.resourceUriis static per tool, so URL scrapes also get the frame, which renders empty. In Claude, that may also take the place of its own generated visual. A clean fix means splitting Alexandria execution into its own tool or switching the binding per call, which changes the public tool contract. That is a separate proposal, not part of this PR. - No per-session gating yet. The spec says servers SHOULD register UI tools only when the client advertises
io.modelcontextprotocol/ui. This prototype uses a process flag instead. - No "Connect Firecrawl" view. An MCP App renders only after a connected server's tool is called, so it can't help a user install the server in the first place. One-click install stays with ChatGPT and Claude directory links.
Tests
tests/mcp-apps.test.mjs:
- With the flag off, the scrape tool has no
uimetadata and no resources are listed. - With the flag on, the tool metadata, resource list and
resources/read(mime type,_meta.ui.prefersBorder, no external assets) are checked, and an Alexandria call still returns the same result. - The view's table and chart extraction skips
find-toolscalls, flattens nested records, charts numeric columns and shows errors.
tsc, lint and pnpm test (170 tests) pass locally.
Summary by cubic
Adds an experimental MCP Apps view that renders Alexandria results from firecrawl_scrape in hosts like ChatGPT and Claude. It's off by default, and tool inputs, outputs, and text content are unchanged.
New Features
- With
FIRECRAWL_MCP_APPS=true(ormcpApps: truefor embedders), the scrape tool declares_meta.ui.resourceUriand the server servesui://firecrawl/alexandria-results.htmlastext/html;profile=mcp-app. - The view runs the
ui/initializehandshake, renders each Alexandria call as a table, adds a bar chart when one column is numeric, shows call errors, and follows host theme variables. - Hosts without MCP Apps ignore the metadata; when the flag is off, tool metadata and the resource list are unchanged.
Known Limits
- The view attaches to every
firecrawl_scrapecall, not just Alexandria ones, so URL scrapes render an empty frame. - There's no per-session gating yet; the spec recommends registering UI tools only when a client advertises
io.modelcontextprotocol/ui, whereas this uses a process flag.