Open Source · A2UI 0.9 · Full Stack

Why Freesail?

A batteries-included A2UI 0.9 implementation that gives you the full stack — renderer, gateway, agent runtime, catalogs, and CLI.

A2UI defines how agents describe UI. Freesail handles everything around it — the gateway that validates and coordinates, the renderer that enforces and themes, the runtime that manages sessions, and the CLI that builds catalogs. These are the problems that sit between the protocol spec and a production deployment, and they're where Freesail focuses.

Token-efficient progressive disclosure

Beyond A2UI — on their roadmap

Most A2UI implementations inject the full catalog schema into the system prompt — every component, every property, every function — whether the agent needs them or not. With real-world applications running 30–50+ components across composed catalogs, this burns tokens and dilutes the LLM's attention.

Freesail takes a different approach. The gateway exposes three MCP tools that let the agent fetch catalog information progressively: get_catalogs returns a slim index — names, descriptions, and type definitions only. When the agent decides to use a specific component, it calls get_component_details for the full property reference. Same for get_function_details. The agent only ever loads what it actually needs.

Progressive disclosure is on the A2UI roadmap as a future item. Freesail ships it today.

💬

Chat over A2UI — no agent API exposed

Freesail-only

In Freesail, chat is just another A2UI surface. The React app bootstraps a chat surface at startup, and all conversation state — user messages, agent replies, streaming tokens, typing indicators — flows through standard updateDataModel and action messages on the same SSE connection as every other surface.

This means the agent process never exposes an HTTP endpoint, a WebSocket, or any ingress route. It sits entirely behind the gateway, making only outbound MCP calls. You can run it on a private network with no inbound rules at all. The browser never has a route to the agent.

It also means chat is composable with the rest of the surface model. The agent can stream a reply to the chat panel and build a chart in the workspace area in the same turn — one gateway connection, no additional plumbing.

🛡️

Gateway-level schema validation with agent feedback

Freesail-only

Every update_components call passes through the gateway before it reaches the frontend. The gateway validates the component tree against the catalog JSON schema — wrong property types, missing required fields, unknown component names — and rejects invalid payloads with a structured error returned over MCP.

The LLM sees the error as tool output and self-corrects on the next turn. In practice, this closes the debugging loop in real time: the agent learns from gateway feedback rather than silently producing a broken UI that the developer has to diagnose after the fact.

This is architecturally distinct from renderer-side validation. The gateway catches errors before they reach the browser, protecting the frontend from invalid state entirely.

🔍

On-demand state inspection — data and UI

A2UI extension

The A2UI spec defines a one-way flow for UI state: the server pushes updates to the client. Freesail adds the reverse path with two protocol extensions that let the agent retrieve the current client-side state at any time.

getDataModel retrieves the current data model for a surface — form values, list contents, toggle states — without requiring sendDataModel to be enabled globally. getComponentTree retrieves the current component tree in JSON format — which components are rendered, their resolved properties, and their hierarchy. Together, these give the agent a complete snapshot of what the user is looking at and what they've entered, on demand rather than on every action.

This is particularly useful for agents that need to make decisions based on the current UI state — inspecting a form as the user interacts with it, checking what components are already displayed before creating another, or understanding the current layout before updating it.

🚦

Renderer-side interceptors

Freesail-only

The gateway validates the shape of what the agent sends. But the React app often has context the agent does not readily have — device constraints, viewport size, geo-location, performance considerations and application specific rules that change at runtime.

Freesail's FreesailProvider exposes four interceptor hooks: onBeforeCreateSurface, onBeforeUpdateComponents, onBeforeUpdateDataModel, and onBeforeDeleteSurface. Each can inspect the incoming operation, allow it, or block it with a message. When blocked, the message travels back to the agent as an MCP error — so the LLM can see why its operation was rejected and adjust its approach.

This gives the frontend hard enforcement authority over agent actions, independent of gateway validation.

🎛️

Dynamic functions for visibility and behaviour

A2UI extension

A2UI v0.9 introduced client-side functions for validation — required, regex, email. Freesail extends this with dynamic functions that control component behaviour at runtime. show and hide bind a component's visibility to a data model path, so sections of the UI appear or disappear based on user input without requiring a round trip to the agent.

An agent can build a multi-step form in a single update_components call, with conditional sections that reveal themselves as the user fills in earlier fields. The agent doesn't need to re-render the surface on every interaction — the client handles the visibility logic locally, keeping the experience fast and the agent's token budget low.

🎨

Dynamic theming at the container level

Beyond A2UI spec

A2UI supports per-surface themes — you set a theme when you call createSurface. But that theme is static: changing it means deleting and recreating the entire surface, losing all component and data model state in the process.

Freesail extends theming to container components. Card, Row, and Column all accept theme token overrides. When one of these is the root component of a surface, the agent can change the surface's entire visual treatment dynamically — by updating the root component's theme prop via update_components — without touching the surface itself. No delete, no recreate, no state loss.

At the React level, FreesailSurface also accepts a theme prop, so a chat panel can stay light while the rest of the app switches to dark mode. CSS custom properties (--freesail-*) propagate from the surface wrapper through every container and leaf component. Font size tokens use fluid clamp() values that scale with the container, and the exported defaultLightTokens / defaultDarkTokens provide complete baselines you can spread and override.

🧩

Catalog composition via CLI

Freesail-only

Creating a custom catalog is a single command: npx freesail new catalog. But the real power is composition. freesail include catalog pulls components and functions from any installed catalog package into yours. You choose which to keep, add your own alongside, and freesail prepare catalog merges everything into a single resolved schema that the agent sees as one unified catalog.

freesail validate catalog catches drift between the schema and the implementation before it reaches the agent — if a component is declared in JSON but missing from React, or vice versa, the build fails with a clear message. This makes it practical to build domain-specific catalogs that inherit standard components, add domain-specific ones, and ship as a single npm package.

📦

Batteries included — full stack, fully open source

Freesail ships every layer of the A2UI stack as a free, MIT-licensed package: the React renderer (@freesail/react), the gateway with MCP server (@freesail/gateway), the agent runtime (@freesail/agent-runtime), the standard and chat catalogs, and the CLI.

The gateway is a standalone process with session management, schema validation, a reconnect grace period for seamless recovery from disconnections, CSRF protection, and user context propagation for deployment behind a reverse proxy. The agent runtime handles session lifecycle, push-based action routing via MCP resource subscriptions, and shared prompt/tool caching with deduplication across concurrent sessions. The example app ships with working configurations for Gemini, OpenAI, and Anthropic Claude.

Protocol Extensions

What Freesail adds to A2UI

Freesail is a faithful A2UI 0.9 implementation. It also extends the protocol in areas the spec doesn't yet cover.

Gateway validation

Schema validation at the gateway level with structured MCP error feedback. Invalid payloads never reach the frontend. The agent self-corrects on the next turn.

Progressive disclosure

Slim catalog index first, full component/function details on demand. Agents only load what they use — listed on the A2UI roadmap, shipped in Freesail today.

Chat as A2UI

Conversation state flows through standard A2UI messages on a chat surface. The agent securely sits behind the gateway with no ingress.

Interceptors

Four client-side hooks that can inspect, allow, or block any A2UI message before it's applied. Rejection messages flow back to the agent over MCP.

getComponentTree

On-demand read of the current component tree as the frontend sees it — rendered components, resolved properties, and hierarchy — so the agent knows the current UI state.

getDataModel

On-demand read of the client-side data model for any surface — form values, list state, toggle positions — without enabling global data model forwarding.

Dynamic container theming

Container components (Card, Row, Column) accept theme overrides. Change a surface's entire visual treatment dynamically via the root component — no delete/recreate, no state loss.

show / hide functions

Conditional component visibility bound to data model paths. Sections appear and disappear based on user input with no round trip to the agent.

Get started in five minutes

Clone the quickstart, run the stack, and start building.

Quickstart → Read the docs