{"slug": "three-questions-to-answer-before-you-ship-an-agent-ui", "title": "Three Questions to Answer Before You Ship an Agent UI", "summary": "A developer building agent UIs argues that teams should decide where the agent loop runs before designing the chat interface, sorting five libraries into three architectural shapes: a vendor runtime in the request path, vendor code inside an owned API route, or the agent framework's own loop. The developer, who maintains the Angular project Threadplane, recommends the third shape for most teams since the agent server already holds the model key, handles authentication, and executes tool calls. The post also distinguishes three mechanisms commonly lumped under \"generative UI\": tool-call rendering, declarative specs like A2UI and json-render, and sandboxed MCP Apps mounted in iframes.", "body_md": "Most teams building an agent UI start with the chat box and work backwards.\n\nI think that is the wrong order.\n\nThe chat box is the easy part.\n\nThe decisions that actually shape the product are made earlier, usually by default, and usually without anyone noticing they were decisions.\n\nIn this post, I want to give the three I keep coming back to:\n\nI build in Angular and I maintain [Threadplane](https://threadplane.ai), so my examples lean that way.\n\nThe questions do not.\n\nThey apply to React, Vue, Svelte, and whatever you are using, and I have tried to keep the answers framework-neutral and to say where my own project makes a trade.\n\nSomebody's code has to call the model, look at what came back, run or forward any tool call, and call the model again.\n\nWhere that code lives decides almost everything else about your architecture: who holds the API key, where approvals pause, where threads persist, and whose release schedule you are on.\n\nI compared five libraries on this question in September 2026, and the answers sort into three shapes.\n\n**A vendor server in the request path.**\n\nThe UI library ships a runtime.\n\nYou deploy it, your browser talks to it, and it talks to your agent.\n\nYou get key custody, auth, agent routing, and trusted middleware in one place, and the door to a hosted thread store and inspector.\n\nYou trade a second server on the hot path and, in at least one popular library, a direct path that is labelled either dev-only or enterprise.\n\n**Vendor code in your route.**\n\nNo box to deploy, but the loop is the library's, running inside an API route you own.\n\nServer tools, approvals, and persistence can be first-class because the library owns both ends of the wire.\n\nThe trade is that the loop is separate from the one your agent framework already has, and it moves when the library moves.\n\n**Your agent.**\n\nThe loop is wherever your agent framework runs it, and the UI library talks to that server directly from the browser.\n\nOne loop, owned by the framework you already chose.\n\nThe trade is that the UI can only show what the agent server actually sends.\n\nIf a framework's bridge never emits a state delta, no client can invent one.\n\nFor me, the third shape is right for most teams, and it is the one Threadplane is built on.\n\nThe reasoning is simple: if you are running an agent at all, you already have a server.\n\nIt holds the model key, because the agent is the thing calling the model.\n\nIt already has to authenticate, because it is on a network.\n\nIt already sees every tool call, because it executes them.\n\nA vendor's box in front of it is a second place to solve a problem you already had one place to solve.\n\nThat does not mean no server at all.\n\nYou should still put an endpoint you own in front of your agent for auth, credentials, and rate limits.\n\nThe difference from a vendor runtime is not the hop.\n\nIt is who owns the code running in it.\n\nI wrote up the full comparison, twenty-four rows across five libraries with every cell source-checked, in [Why Do Agent UI Libraries Require a Runtime?](https://threadplane.ai/blog/why-do-agent-ui-libraries-require-a-runtime)\n\nThe honest version includes what my own choice costs: no hosted inspector, no multi-agent routing, and no server-side stream middleware from us.\n\n\"Generative UI\" hides several different mechanisms behind one phrase, and the libraries that support it rarely say which one they mean.\n\nSeparate them before you compare anything.\n\n**Tool-call rendering.**\n\nThe model calls a tool, and you map the tool name to a component.\n\nEvery library does this, and it is where most teams should start.\n\n**Declarative specs.**\n\nThe model authors a UI tree in a standard format, and the client renders it from a catalog of components you registered.\n\nThe two open specs I see gaining ground are [A2UI](https://a2ui.org) and [json-render](https://github.com/vercel-labs/json-render).\n\n**Sandboxed apps.**\n\nAn MCP server returns a `ui://` resource, and the client mounts it in an iframe.\n\nThis is MCP Apps, and it is the one my project does not render yet.\n\nThe choice that matters is inside the second bucket, and it is about *contract shape*, not renderers.\n\nWith a fixed spec like json-render, the contract is application-owned.\n\nYou define the schema, you validate the whole spec before anything mounts, and your handlers decide what every click means.\n\nIt is a document you can lint, snapshot in a test, and reject before it renders.\n\nWith a live protocol like A2UI, the surface is agent-owned.\n\nThe agent creates it, keeps editing it over the life of the conversation, and receives structured actions back when the user interacts.\n\nStructure arrives in one message and data in another, so a card can mount as a skeleton and fill in as values land.\n\nIt is a conversation you subscribe to, not a document you validate.\n\nMy heuristic is short.\n\nIf you can validate the entire UI before it renders, start with the fixed spec.\n\nIf the surface has to live past its first render, with data trickling in, actions coming back, or edits across turns, step up to the protocol.\n\nTwo examples make the line concrete.\n\nAn order summary card that the agent produces once and never touches again is a fixed spec.\n\nA three-day itinerary that the agent proposes, fills in with prices over the next few seconds, and then rewrites on day two when the user objects is a live surface.\n\nOne thing both shapes share, and it is worth knowing: the component registry is doing allowlist duty.\n\nA component name the model emits that you did not register renders nothing.\n\nThat posture is the same in both, so it is not a reason to pick either.\n\nThe Angular-specific walkthrough, with the same order card in both formats, is in [json-render vs A2UI: Choosing a Generative UI Contract](https://threadplane.ai/blog/json-render-vs-a2ui-choosing).\n\nAgents are hard to test end to end for one boring reason: the model does not return the same thing twice.\n\nWrite an assertion against that and you have written a coin flip.\n\nThe usual fix is to stop calling the model.\n\nRecord its responses once as fixtures, replay them on every run, and the tests go deterministic while CI stops spending tokens.\n\nWe test our whole demo fleet that way, and I recommend it.\n\nTwo things about that setup are worth more thought than they usually get.\n\n**Where you put the mock decides what is under test.**\n\nMock the agent at the app boundary and you have proven that your component renders what you handed it.\n\nNothing more.\n\nPush the seam out to the model provider instead, so the real agent server runs against a fake model endpoint, and your graph's routing, your streaming transport, and your tool round-trips are all under test, because none of them were replaced.\n\nThe only thing that is not real is the model.\n\n**Every deterministic harness buys its determinism by deleting a dimension.**\n\nOurs deletes *time*.\n\nReplayed responses arrive in one or two chunks by default instead of token by token, so assertions on the final DOM stay stable.\n\nThat is a deliberate choice with the reason written in the source, and a handful of fixtures opt back in with tiny chunk sizes and real latencies where the progressive render is the thing under test.\n\nHere is the class of bug that disappears when you delete time.\n\nA child agent streams tokens under a namespace.\n\nA client that merges them into the transcript shows an extra chat bubble mid-run.\n\nThen the run settles, the parent publishes its authoritative state, the transcript is rebuilt, and the extra bubble vanishes.\n\nAssert on the finished DOM and the test passes, and not by luck.\n\nThe end state is correct.\n\nAny assertion that runs after an `await` sees a settled system, and a self-correcting bug is precisely one that settles.\n\nCatching that class means driving a live model and sampling the DOM on a tight interval for the length of a run.\n\nNo final-state suite can report it, and no final-state suite could.\n\nSo the interesting question about your harness is not whether it is green.\n\nIt is which dimension you deleted, and which tests opted back in.\n\nThe full accounting, including a fixture-ordering mistake that makes a replay run never terminate, is in [What Fixture Replay Can't Catch](https://threadplane.ai/blog/what-fixture-replay-cant-catch).\n\nEach question has a default answer that arrives with whatever library you install first.\n\nThe library ships a runtime, so the loop runs there.\n\nThe library has a tool-rendering hook, so that is your generative UI.\n\nThe library has a mock, so that is your test seam.\n\nNone of those defaults is wrong.\n\nAll of them are decisions, and I think you should make them as decisions.\n\nFor me, the answers are: run the loop in the agent and put an endpoint you own in front of it, start with a fixed UI spec and step up to a live one per surface, and mock at the model provider while knowing exactly which dimension you gave up.\n\nThose are the answers [Threadplane](https://threadplane.ai) is built around, and the [docs](https://threadplane.ai/docs) show what each one looks like in Angular with a LangGraph or AG-UI backend.\n\nThe questions are the part I am confident about.", "url": "https://wpnews.pro/news/three-questions-to-answer-before-you-ship-an-agent-ui", "canonical_source": "https://dev.to/blove/three-questions-to-answer-before-you-ship-an-agent-ui-2ief", "published_at": "2026-09-16 05:29:13+00:00", "updated_at": "2026-09-16 05:37:14.780487+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-products"], "entities": ["Threadplane", "Angular", "React", "Vue", "Svelte", "A2UI", "json-render", "MCP Apps"], "alternates": {"html": "https://wpnews.pro/news/three-questions-to-answer-before-you-ship-an-agent-ui", "markdown": "https://wpnews.pro/news/three-questions-to-answer-before-you-ship-an-agent-ui.md", "text": "https://wpnews.pro/news/three-questions-to-answer-before-you-ship-an-agent-ui.txt", "jsonld": "https://wpnews.pro/news/three-questions-to-answer-before-you-ship-an-agent-ui.jsonld"}}