Documents saga a2ui 0.11.0-rc.1 (prerelease). This is the SDK guideReact and its MCP stack are a supported application path for both custom A2UI catalogs and FaithfulCard applications. The stack owns MCP connection ordering and the React view lifecycle; application code supplies its data interpretation, actions and presentation.docs/react-consumers.mdat source revisionfb4126c. It is published as a snapshot: later source changes do not alter it. Licensed under Apache-2.0.
One stack, plus React
An application installs one stack package,@saga-a2ui/react-mcp-stack, plus its framework peers (react and react-dom, singletons at the version SAGA supports), and imports from the stack only:
z for catalog schemas; an application that also defines MCP tools declares Zod 4 separately for those tool schemas. An application that installs the stack writes it against the stack’s exports alone (Catalog, CommonSchemas, createComponentImplementation, ReactCatalogComponent, defineSurface, z, the action names and the failures it catches), without a direct @a2ui/* dependency, a separate Zod dependency for the catalog, or another SAGA package; the /faithful-card entry starts a card application from one validated definition. A server composes the same application from the stack’s /content-policy entry (createContentPolicy, embedContentPolicy) and its Node-only /server entry (createServerBudgets, registerPublicOperation, registerProtectedOperation, operationViewFromHtml, viewUriForHtml, attachRequestLifetime), so one install serves both sides without a browser bundle ever importing a server module. The definition’s one result reader receives the whole delivered result (error flag, content blocks and structured content) and supplies its own rejection wording; readingProfile tells its hostContext callback whether the host is read on a device, and the application maps that onto its own tokens (details). The stack connects the React renderer to MCP Apps delivery, so the application writes no A2UI message, imports no bridge and orders no handshake: it states its layout, its catalog, what a result means and what ending the view takes with it. @saga-a2ui/core, @saga-a2ui/components, @saga-a2ui/mcp, @saga-a2ui/react and the MCP Apps bridge are regular dependencies of the stack, so one install suffices under a strict (pnpm) layout.
Two footprints follow, and they are different things. An own-catalog application’s installed tree includes @saga-a2ui/components and markdown-it as declared dependencies of the stack; that is the correct install result. Its emitted browser bundle and runtime import graph exclude them, because the main entry imports nothing from components. The stack’s exports.test.ts bundles each entry’s module graph from workspace source to show this, and scripts/pack1.test.ts builds the own-catalog and card pages from the packed copies; npm run test:pnpm-react and npm run candidate:verify check the install, not the bundle.
The stack belongs to the public release set. See public installation for its exact-version recipe and registry availability, and release preparation for exact-artifact checks. Both React and Lit implement the typed-surface and FaithfulCard application contracts. Their framework APIs and teardown mechanics differ. The host compatibility guide records the observed flows and remaining host limits; the generated React starter covers one server (react-mcp on Hono), and an application on another server or an existing React application integrates the stack directly, which is a working application path.
Add a message to the conversation
A view can reach the conversation around it. Theactions callback of startA2uiView receives host.conversation beside host.server, and appendMessage adds a message to the conversation as the person, which gives the assistant its turn:
appendMessage(text, {role?, context?}) resolves, and never rejects, with an AppendResult:
context is information for the assistant’s next turn that the conversation does not show: text becomes a content block and data becomes structuredContent of a ui/update-model-context request, which the stack sends before the ui/message request, never in parallel, and only for a host that advertises the matching capability (updateModelContext, message). Context blocks the message by default; set bestEffort: true only when the assistant has another way to get the information, such as a tool the message names. Nothing is retried: a request that fails or times out may still have been carried out, and a repeated message would ask the assistant twice. When the view ends, an outstanding or later call resolves unavailable and sends nothing further.
The application owns everything about the conversation: whether to append, what the text says, what context goes with it, and what the person sees when nothing was appended (compare the application’s own identity for the state on screen first, because the append settles after the action’s current() has gone false). The server stays authoritative; an appended message authorizes nothing. The Lit stack offers the same host.conversation. Whether Claude, ChatGPT or Alexa+ show the message, or how their assistants respond, is not established by the local checks; see the host compatibility guide.
A server can also declare an operation with no view. Omit view from the declaration given to registerPublicOperation or registerProtectedOperation (both from the stack’s /server entry); the operation keeps its input and output contracts, annotations, budgets, cancellation and safe failures, and carries no UI association: no view resource and no _meta.ui on the tool definition or on any result.
For a local Node server, the optional local MCP listener handles HTTP transport and request lifetimes while your app supplies its operations.
Removable credit
An application with its own catalog can credit the framework without a FaithfulCard.SagaAttribution is a small line, “Built with saga a2ui”, whose name asks the host to open https://saga-ai.dev/. Render it where you want it and pass the host’s link seam from the actions factory:
prefix, name and href (an absolute https: URL) change the wording and destination, and only your own code supplies them. Without links, or with a destination that is not acceptable, nothing is clickable and nothing navigates. A host that does not open the link leaves the credit in place with a short message and the destination as text. The component makes no network request of its own and carries no card, Markdown parser or components package. Style it with .saga-attribution or the --saga-attribution-* tokens documented at the top of the stylesheet. Removing it removes no licence notice or answer-source attribution; those are not part of it. The design is own-catalog attribution.
React in Ansari Faithful Answers
Ansari Faithful Answers uses the React MCP stack for its answer-card application. The public destination integration PR records a candidate using@saga-a2ui/react-mcp-stack@0.7.6-rc.1 and React/React DOM 19.3.1, with clean public-package installation, application checks and browser validation. Its view source uses the stack’s FaithfulCard application entry. This is a concrete application consumer alongside the SDK examples; it does not depend on a generated React starter.
A prior React integration was also exercised in ChatGPT web, Claude web and the Alexa+ development simulator; the recorded SDK integration identifies that earlier candidate. Those observations are separate from the current destination PR, which remains a candidate, and from its version-specific installation and browser checks. The host compatibility guide separately identifies demonstrated assistant-host flows and their limits. Neither adoption by Ansari nor a local browser pass establishes every host or all renderer features.
Follow a complete React application
The notes React view is a complete stack composition. It uses the notes layout and React catalog, with the same controller and backend as the Lit application:- The page supplies a
#rootcontainer and callsstartNotesReactView(). That function creates the application controller and starts the stack with the notes layout and catalog. - Its
actionscallback receives the host transport. The controller builds the Archive and Mark read definitions, andcreateReactActionsbinds them to each rendered row. Archive reads the row’s current note ID as itssubject, so a reused row cannot receive an old note’s completion message. onToolResultgives the delivered result to the controller. The controller validates the application data and updates the typed surface; rejected input is handled by the application instead of being cast into the view.- When the view ends, the stack retires the surface, calls
onTeardownto retire the application controller and its resources, and unmounts the React root. The action provider disposes its bindings during unmount; application cleanup must not depend on those bindings already being disposed insideonTeardown.
saga init --a2ui-stack react-mcp --server hono as Getting started describes; the starter holds the same public status, protected account and local Clear action as the Lit starters. Use public installation for the stack and exact peers.
The React integration surface
Use SAGA’s React A2UI entry point for surfaces and catalogs when you compose below the stack:SurfaceModel, CommonSchemas, createComponentImplementation, ReactComponentImplementation and NodeViewProps for custom catalogs.
Open a typed surface
For a lower-level integration without MCP delivery, define the surface once withdefineSurface, open it with your catalog and mount it. The following is the lifecycle outline: layout, catalog and data are your application’s validated definition, component catalog and next value. The complete notes composition above uses startA2uiView to own this lifecycle for MCP delivery.
openSurface owns the message session, the decoder and the order of the opening messages, so application code imports none of them. update is typed from the layout, validates against its schema and applies the data in place: the mounted controls keep their DOM nodes, focus, local state and action bindings. A rejected update returns the failure and changes nothing. After dispose, update fails and <Surface> renders nothing. Open the surface once per surface lifetime, outside render (for example in state or a store), and dispose it where you created it: <Surface> never disposes the handle, so Strict Mode’s replay of effects cannot end it.
A2uiSurface and SurfaceModel stay exported for custom use over a session of your own.
Let SAGA own its A2UI installation
Applications using this integration should not add direct dependencies on@a2ui/react or @a2ui/web_core, or import their catalog/surface APIs directly. Migrate those imports to @saga-a2ui/react/a2ui and remove the direct dependencies. Keep React and React DOM as the application’s declared peers at the versions supported by SAGA.
SAGA React owns A2UI 0.13.0 and explicitly depends on Zod 3.25.76. MCP Apps and MCP tool schemas use Zod 4. Both versions are intentionally installed. A direct application-level A2UI React dependency can receive the application’s Zod 4 peer under pnpm, making its catalog types incompatible with A2UI’s Zod 3 types. A re-export alone does not solve this: the explicit Zod 3 dependency and removal of the application’s direct A2UI dependencies are both necessary.
For custom A2UI schemas use z from @saga-a2ui/core/protocols/a2ui/zod3. For MCP tool schemas use the application’s Zod 4. Do not pass schemas between these interfaces, force a single Zod version globally, cast away the mismatch, or suppress it with skipLibCheck.
An application that independently uses A2UI outside this SAGA integration owns that additional dependency combination and must qualify it separately; it is not covered by SAGA’s installed-consumer tests.
Pin the compatible upstream Markdown package
A2UI 0.13.0 allows@a2ui/markdown-it ^0.2.1. Version 0.2.2 requires @a2ui/web_core ^0.14.0; this is a different upstream combination. Pin 0.2.1 at the consumer workspace root, where package-manager overrides take effect.
For pnpm, merge this into the destination root pnpm-workspace.yaml (preserve its existing workspace globs and other overrides):
package.json:
pnpm install --strict-peer-dependencies and the application typecheck. The SDK’s npm run test:pnpm-react performs that check using packed SDK packages, a real MCP/A2UI consumer and no source aliases or direct A2UI dependencies. Public SDK release availability is a separate prerequisite; this test does not publish packages.