Skip to main content
Use this when your backend owns the whole contract — sessions, chat streaming, and the agent builder. You implement AgentUIServer and pass the object straight to the component, with no type wrapper: every call the UI makes, including the createTurn event stream, hits your endpoints. Because server accepts an AgentUIServer directly, you can front any backend — including your own proxy that keeps API keys server-side. The object is used immediately, so there is no loading indicator on mount.

Minimal implementation

The shape below is the smallest thing that mounts. Optional methods are omitted; required ones must exist even if they only throw in paths your UI never reaches. List methods return a ListResult<T> — a plain object of { data, nextPageToken? }. There are no methods on it, so you can return a parsed JSON body directly as long as it carries those two fields.
server.ts
Declaring sandbox and skill as disabled is the honest starting point — the UI then hides those affordances instead of offering controls your backend cannot serve. Flip them on as you implement each.
App.tsx

Streaming

createTurn yields envelopes of the form { sequenceNumber, event } — not bare events. The number must increase monotonically within the turn, and it is what lets a reconnecting client resume via subscribeToTurn({ afterSequenceNumber }). The minimum viable stream for one assistant reply is three events:
Two things are easy to get wrong. Deltas carry the increment rather than the accumulated text, and every delta for one message shares a single id. And a stream that ends without a turn.done leaves the UI showing the turn as still running. Streaming events documents the full protocol — tool calls, approval prompts, sub-agent threads, and sandbox artifacts.

Pagination

ListResult<T> is { data: T[]; nextPageToken?: string }. Return the next cursor in nextPageToken; omit it (or leave it undefined) on the last page. Tokens travel the other way too — list requests accept a pageToken, which the SDK feeds back to fetch the following page.
Spreading nextPageToken in only when present keeps the result valid under exactOptionalPropertyTypes; assigning undefined to the optional field directly is rejected there. listEvents additionally accepts lastTurnId, used when hydrating history for an existing session.

Optional capabilities

In a hand-written AgentUIServer like the one above, omitting these leaves the corresponding affordances unavailable rather than broken. With createTrueForgeServer, an omitted deleteAgent throws when called.
Return a Blob. Required only if your agents produce sandbox artifacts users should be able to download.
Resumes a live stream from afterSequenceNumber, so a reconnecting client picks up where it left off instead of replaying the turn. Skip it if your backend cannot resume.
Attach a CatalogServer to enable the settings UI — models, connectors, skills, and sandboxes. Without it those surfaces do not render.

Widening the types

The interfaces are generic so you can carry backend-specific fields through the UI without casting. AgentUIServer itself is a plain alias, so compose the widened chat and builder ports directly:
See Widening the types for the full set of parameters.

Attachments

Attachment handling defaults to trueForgeAttachmentAdapter. Override it through the adapters prop on TrueForgeUI if your backend stores uploads differently.