@assistant-ui/mcp-docs-server 0.1.30 → 0.1.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/.docs/organized/code-examples/waterfall.md +1 -1
  2. package/.docs/organized/code-examples/with-a2a.md +2 -2
  3. package/.docs/organized/code-examples/with-ag-ui.md +3 -3
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +5 -5
  5. package/.docs/organized/code-examples/with-artifacts.md +5 -5
  6. package/.docs/organized/code-examples/with-assistant-transport.md +3 -3
  7. package/.docs/organized/code-examples/with-chain-of-thought.md +79 -50
  8. package/.docs/organized/code-examples/with-cloud-standalone.md +4 -4
  9. package/.docs/organized/code-examples/with-cloud.md +4 -4
  10. package/.docs/organized/code-examples/with-custom-thread-list.md +56 -11
  11. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +7 -7
  12. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +7 -7
  13. package/.docs/organized/code-examples/with-expo.md +16 -16
  14. package/.docs/organized/code-examples/with-external-store.md +2 -2
  15. package/.docs/organized/code-examples/with-ffmpeg.md +5 -5
  16. package/.docs/organized/code-examples/with-generative-ui.md +5 -5
  17. package/.docs/organized/code-examples/with-google-adk.md +4 -4
  18. package/.docs/organized/code-examples/with-heat-graph.md +1 -1
  19. package/.docs/organized/code-examples/with-interactables.md +5 -5
  20. package/.docs/organized/code-examples/with-langchain.md +3 -3
  21. package/.docs/organized/code-examples/with-langgraph.md +3 -3
  22. package/.docs/organized/code-examples/with-livekit.md +8 -8
  23. package/.docs/organized/code-examples/with-opencode.md +99 -54
  24. package/.docs/organized/code-examples/with-parent-id-grouping.md +4 -4
  25. package/.docs/organized/code-examples/with-react-hook-form.md +5 -5
  26. package/.docs/organized/code-examples/with-react-ink.md +1 -1
  27. package/.docs/organized/code-examples/with-react-router.md +8 -8
  28. package/.docs/organized/code-examples/with-store.md +1 -1
  29. package/.docs/organized/code-examples/with-tanstack.md +5 -5
  30. package/.docs/organized/code-examples/with-tap-runtime.md +2 -2
  31. package/.docs/raw/docs/(docs)/cli.mdx +2 -1
  32. package/.docs/raw/docs/(docs)/copilots/assistant-frame.mdx +1 -0
  33. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +10 -3
  34. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +8 -3
  35. package/.docs/raw/docs/(docs)/copilots/make-assistant-visible.mdx +1 -0
  36. package/.docs/raw/docs/(docs)/copilots/model-context.mdx +1 -0
  37. package/.docs/raw/docs/(docs)/copilots/motivation.mdx +1 -0
  38. package/.docs/raw/docs/(docs)/copilots/use-assistant-instructions.mdx +1 -0
  39. package/.docs/raw/docs/(docs)/devtools.mdx +1 -0
  40. package/.docs/raw/docs/(docs)/index.mdx +1 -0
  41. package/.docs/raw/docs/(docs)/installation.mdx +1 -0
  42. package/.docs/raw/docs/(docs)/rtl.mdx +1 -0
  43. package/.docs/raw/docs/(reference)/api-reference/adapters/attachments.mdx +34 -0
  44. package/.docs/raw/docs/(reference)/api-reference/adapters/feedback-speech.mdx +41 -0
  45. package/.docs/raw/docs/(reference)/api-reference/adapters/index.mdx +26 -0
  46. package/.docs/raw/docs/(reference)/api-reference/adapters/persistence.mdx +34 -0
  47. package/.docs/raw/docs/(reference)/api-reference/adapters/runtime.mdx +31 -0
  48. package/.docs/raw/docs/(reference)/api-reference/context-providers/index.mdx +20 -0
  49. package/.docs/raw/docs/(reference)/api-reference/hooks/index.mdx +26 -0
  50. package/.docs/raw/docs/(reference)/api-reference/hooks/model-context.mdx +72 -0
  51. package/.docs/raw/docs/(reference)/api-reference/hooks/runtimes.mdx +41 -0
  52. package/.docs/raw/docs/(reference)/api-reference/hooks/state.mdx +48 -0
  53. package/.docs/raw/docs/(reference)/api-reference/hooks/utilities.mdx +30 -0
  54. package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +23 -0
  55. package/.docs/raw/docs/(reference)/api-reference/overview.mdx +21 -0
  56. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +7 -0
  57. package/.docs/raw/docs/(reference)/api-reference/primitives/index.mdx +65 -0
  58. package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +50 -6
  59. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list.mdx +15 -0
  60. package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +36 -1
  61. package/.docs/raw/docs/(reference)/api-reference/runtimes/index.mdx +38 -0
  62. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +5 -0
  63. package/.docs/raw/docs/(reference)/migrations/v0-14.mdx +144 -6
  64. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +230 -2
  65. package/.docs/raw/docs/cloud/ai-sdk.mdx +221 -3
  66. package/.docs/raw/docs/cloud/langgraph.mdx +274 -2
  67. package/.docs/raw/docs/{(docs)/guides → guides}/attachments.mdx +41 -36
  68. package/.docs/raw/docs/guides/branching.mdx +76 -0
  69. package/.docs/raw/docs/guides/chain-of-thought.mdx +166 -0
  70. package/.docs/raw/docs/{(docs)/guides → guides}/context-api.mdx +50 -22
  71. package/.docs/raw/docs/{(docs)/guides → guides}/dictation.mdx +2 -0
  72. package/.docs/raw/docs/guides/editing.mdx +102 -0
  73. package/.docs/raw/docs/guides/index.mdx +103 -0
  74. package/.docs/raw/docs/{(docs)/guides → guides}/interactables.mdx +49 -0
  75. package/.docs/raw/docs/{(docs)/guides → guides}/latex.mdx +51 -8
  76. package/.docs/raw/docs/{(docs)/guides → guides}/mentions.mdx +61 -86
  77. package/.docs/raw/docs/{(docs)/guides → guides}/message-timing.mdx +8 -2
  78. package/.docs/raw/docs/{(docs)/guides → guides}/multi-agent.mdx +64 -4
  79. package/.docs/raw/docs/{(docs)/guides → guides}/quoting.mdx +10 -17
  80. package/.docs/raw/docs/{(docs)/guides → guides}/slash-commands.mdx +103 -37
  81. package/.docs/raw/docs/guides/speech.mdx +156 -0
  82. package/.docs/raw/docs/{(docs)/guides → guides}/suggestions.mdx +21 -83
  83. package/.docs/raw/docs/{(docs)/guides → guides}/tool-ui.mdx +108 -36
  84. package/.docs/raw/docs/{(docs)/guides → guides}/tools.mdx +131 -35
  85. package/.docs/raw/docs/{(docs)/guides → guides}/voice.mdx +39 -0
  86. package/.docs/raw/docs/ink/index.mdx +1 -3
  87. package/.docs/raw/docs/ink/migration.mdx +1 -3
  88. package/.docs/raw/docs/ink/primitives.mdx +37 -1
  89. package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +520 -0
  90. package/.docs/raw/docs/integrations/auth/better-auth.mdx +191 -0
  91. package/.docs/raw/docs/integrations/auth/clerk.mdx +172 -0
  92. package/.docs/raw/docs/integrations/auth/next-auth.mdx +196 -0
  93. package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +79 -0
  94. package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +188 -0
  95. package/.docs/raw/docs/integrations/frameworks/mastra/overview.mdx +57 -0
  96. package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +201 -0
  97. package/.docs/raw/docs/integrations/gateways/index.mdx +157 -0
  98. package/.docs/raw/docs/integrations/index.mdx +173 -0
  99. package/.docs/raw/docs/integrations/observability/helicone.mdx +130 -0
  100. package/.docs/raw/docs/integrations/observability/langfuse.mdx +156 -0
  101. package/.docs/raw/docs/integrations/observability/langsmith.mdx +146 -0
  102. package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +712 -0
  103. package/.docs/raw/docs/integrations/tools/mcp.mdx +267 -0
  104. package/.docs/raw/docs/primitives/action-bar.mdx +1 -0
  105. package/.docs/raw/docs/primitives/assistant-modal.mdx +1 -0
  106. package/.docs/raw/docs/primitives/attachment.mdx +1 -0
  107. package/.docs/raw/docs/primitives/branch-picker.mdx +1 -0
  108. package/.docs/raw/docs/primitives/chain-of-thought.mdx +90 -85
  109. package/.docs/raw/docs/primitives/composer.mdx +2 -1
  110. package/.docs/raw/docs/primitives/error.mdx +1 -0
  111. package/.docs/raw/docs/primitives/index.mdx +2 -1
  112. package/.docs/raw/docs/primitives/message.mdx +68 -5
  113. package/.docs/raw/docs/primitives/selection-toolbar.mdx +1 -0
  114. package/.docs/raw/docs/primitives/suggestion.mdx +1 -0
  115. package/.docs/raw/docs/primitives/thread-list.mdx +39 -0
  116. package/.docs/raw/docs/primitives/thread.mdx +16 -13
  117. package/.docs/raw/docs/react-native/index.mdx +1 -3
  118. package/.docs/raw/docs/react-native/migration.mdx +1 -3
  119. package/.docs/raw/docs/runtimes/a2a/client-and-hooks.mdx +396 -0
  120. package/.docs/raw/docs/runtimes/a2a/overview.mdx +60 -0
  121. package/.docs/raw/docs/runtimes/a2a/quickstart.mdx +216 -0
  122. package/.docs/raw/docs/runtimes/ag-ui/overview.mdx +70 -0
  123. package/.docs/raw/docs/runtimes/ag-ui/quickstart.mdx +243 -0
  124. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +123 -0
  125. package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +52 -0
  126. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +71 -131
  127. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +69 -63
  128. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +330 -123
  129. package/.docs/raw/docs/runtimes/concepts/adapters.mdx +265 -0
  130. package/.docs/raw/docs/runtimes/concepts/architecture.mdx +125 -0
  131. package/.docs/raw/docs/runtimes/concepts/stability.mdx +67 -0
  132. package/.docs/raw/docs/runtimes/concepts/threads.mdx +428 -0
  133. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +703 -0
  134. package/.docs/raw/docs/runtimes/custom/data-stream.mdx +323 -0
  135. package/.docs/raw/docs/runtimes/custom/external-store.mdx +253 -1236
  136. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +746 -0
  137. package/.docs/raw/docs/runtimes/custom/overview.mdx +71 -0
  138. package/.docs/raw/docs/runtimes/google-adk/api.mdx +256 -0
  139. package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +717 -0
  140. package/.docs/raw/docs/runtimes/google-adk/overview.mdx +69 -0
  141. package/.docs/raw/docs/runtimes/google-adk/quickstart.mdx +229 -0
  142. package/.docs/raw/docs/runtimes/langchain.mdx +533 -0
  143. package/.docs/raw/docs/runtimes/langgraph/generative-ui.mdx +305 -0
  144. package/.docs/raw/docs/runtimes/langgraph/interrupts.mdx +104 -0
  145. package/.docs/raw/docs/runtimes/langgraph/overview.mdx +84 -0
  146. package/.docs/raw/docs/runtimes/langgraph/quickstart.mdx +496 -0
  147. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +127 -0
  148. package/.docs/raw/docs/runtimes/langgraph/threads.mdx +113 -0
  149. package/.docs/raw/docs/runtimes/langgraph/tutorial/introduction.mdx +3 -3
  150. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-1.mdx +0 -23
  151. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +1 -1
  152. package/.docs/raw/docs/runtimes/opencode/hooks.mdx +191 -0
  153. package/.docs/raw/docs/runtimes/opencode/overview.mdx +48 -0
  154. package/.docs/raw/docs/runtimes/opencode/quickstart.mdx +119 -0
  155. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +71 -203
  156. package/.docs/raw/docs/ui/accordion.mdx +1 -0
  157. package/.docs/raw/docs/ui/assistant-modal.mdx +1 -0
  158. package/.docs/raw/docs/ui/assistant-sidebar.mdx +1 -0
  159. package/.docs/raw/docs/ui/attachment.mdx +1 -0
  160. package/.docs/raw/docs/ui/badge.mdx +1 -0
  161. package/.docs/raw/docs/ui/composer-trigger-popover.mdx +1 -0
  162. package/.docs/raw/docs/ui/context-display.mdx +1 -0
  163. package/.docs/raw/docs/ui/diff-viewer.mdx +1 -0
  164. package/.docs/raw/docs/ui/directive-text.mdx +1 -0
  165. package/.docs/raw/docs/ui/file.mdx +1 -0
  166. package/.docs/raw/docs/ui/image.mdx +1 -0
  167. package/.docs/raw/docs/ui/markdown.mdx +2 -14
  168. package/.docs/raw/docs/ui/mermaid.mdx +1 -0
  169. package/.docs/raw/docs/ui/message-timing.mdx +3 -2
  170. package/.docs/raw/docs/ui/model-selector.mdx +1 -0
  171. package/.docs/raw/docs/ui/part-grouping.mdx +325 -313
  172. package/.docs/raw/docs/ui/quote.mdx +1 -0
  173. package/.docs/raw/docs/ui/reasoning.mdx +66 -33
  174. package/.docs/raw/docs/ui/scrollbar.mdx +1 -0
  175. package/.docs/raw/docs/ui/select.mdx +1 -0
  176. package/.docs/raw/docs/ui/sources.mdx +1 -0
  177. package/.docs/raw/docs/ui/streamdown.mdx +1 -0
  178. package/.docs/raw/docs/ui/syntax-highlighting.mdx +1 -0
  179. package/.docs/raw/docs/ui/tabs.mdx +1 -0
  180. package/.docs/raw/docs/ui/thread-list.mdx +17 -0
  181. package/.docs/raw/docs/ui/thread.mdx +56 -1
  182. package/.docs/raw/docs/ui/tool-fallback.mdx +1 -0
  183. package/.docs/raw/docs/ui/tool-group.mdx +39 -11
  184. package/.docs/raw/docs/ui/voice.mdx +1 -0
  185. package/.docs/raw/docs/utilities/heat-graph.mdx +1 -0
  186. package/.docs/raw/docs/utilities/react-o11y.mdx +278 -0
  187. package/.docs/raw/docs/utilities/tw-shimmer.mdx +1 -0
  188. package/package.json +3 -3
  189. package/src/tools/tests/path-traversal.test.ts +1 -1
  190. package/.docs/raw/docs/(docs)/guides/branching.mdx +0 -65
  191. package/.docs/raw/docs/(docs)/guides/chain-of-thought.mdx +0 -164
  192. package/.docs/raw/docs/(docs)/guides/editing.mdx +0 -66
  193. package/.docs/raw/docs/(docs)/guides/speech.mdx +0 -38
  194. package/.docs/raw/docs/runtimes/a2a/index.mdx +0 -298
  195. package/.docs/raw/docs/runtimes/assistant-transport.mdx +0 -1033
  196. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +0 -314
  197. package/.docs/raw/docs/runtimes/custom/local.mdx +0 -1464
  198. package/.docs/raw/docs/runtimes/data-stream.mdx +0 -422
  199. package/.docs/raw/docs/runtimes/google-adk/index.mdx +0 -686
  200. package/.docs/raw/docs/runtimes/helicone.mdx +0 -61
  201. package/.docs/raw/docs/runtimes/langchain/comparison.mdx +0 -60
  202. package/.docs/raw/docs/runtimes/langchain/index.mdx +0 -210
  203. package/.docs/raw/docs/runtimes/langgraph/index.mdx +0 -699
  204. package/.docs/raw/docs/runtimes/langgraph/tutorial/index.mdx +0 -12
  205. package/.docs/raw/docs/runtimes/langserve.mdx +0 -116
  206. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +0 -218
  207. package/.docs/raw/docs/runtimes/mastra/overview.mdx +0 -18
  208. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +0 -217
@@ -0,0 +1,703 @@
1
+ ---
2
+ title: Assistant Transport
3
+ description: Stream agent state to the frontend and handle user commands for custom agents.
4
+ platforms: ["react"]
5
+ ---
6
+
7
+ `AssistantTransport` is a state-streaming protocol layered on `ExternalStoreRuntime` (see [architecture](/docs/runtimes/concepts/architecture)). Instead of streaming message parts, your backend streams snapshots of its full agent state and the runtime converts them into UI messages.
8
+
9
+ Three things make this useful:
10
+
11
+ - **State streaming** — efficient updates to your agent state (any JSON object).
12
+ - **UI integration** — your agent's native state becomes assistant-ui messages.
13
+ - **Command handling** — user actions (messages, tool results, custom commands) flow back to the agent.
14
+
15
+ ## When to use it
16
+
17
+ Pick `AssistantTransport` when:
18
+
19
+ - Your backend does not have a streaming protocol yet and you want one.
20
+ - Your agent has internal state worth surfacing in the UI directly.
21
+ - You are building a custom agent framework or one without a streaming protocol (e.g. open-source LangGraph).
22
+ - You need bidirectional commands beyond simple message turns.
23
+
24
+ If you only need message streaming, [DataStream](/docs/runtimes/custom/data-stream) is simpler.
25
+
26
+ ## Mental model
27
+
28
+ ```mermaid
29
+ graph LR
30
+ Frontend -->|Commands| Agent[Agent server]
31
+ Agent -->|State snapshots| Frontend
32
+ ```
33
+
34
+ The frontend receives state snapshots and converts them to React components. The UI is a stateless view on top of the agent state.
35
+
36
+ The agent server receives commands from the frontend. When a user interacts with the UI (sends a message, clicks a button), the frontend queues a command and sends it. `AssistantTransport` defines `add-message` and `add-tool-result`; you can define more.
37
+
38
+ ### Command lifecycle
39
+
40
+ ```mermaid
41
+ graph LR
42
+ queued -->|sent to backend| in_transit
43
+ in_transit -->|backend processes| applied
44
+ ```
45
+
46
+ The runtime alternates between **idle** (no active backend request) and **sending** (request in flight). When a new command is created while idle, it is sent immediately; otherwise it is queued until the current request completes.
47
+
48
+ ```mermaid
49
+ graph LR
50
+ idle -->|new command| sending
51
+ sending -->|request completes| check{check queue}
52
+ check -->|queue has commands| sending
53
+ check -->|queue empty| idle
54
+ ```
55
+
56
+ To implement this you build two pieces:
57
+
58
+ 1. **Backend endpoint** that accepts commands and returns a stream of state snapshots.
59
+ 2. **Frontend state converter** that maps state snapshots to assistant-ui's data format.
60
+
61
+ ## Building a backend endpoint
62
+
63
+ The endpoint receives POST requests with this payload:
64
+
65
+ ```ts
66
+ {
67
+ state: T, // previous state the frontend has
68
+ commands: AssistantTransportCommand[],
69
+ system?: string,
70
+ tools?: Record<string, ToolJSONSchema>, // tool definitions keyed by name
71
+ threadId: string | null, // null for new threads
72
+ parentId?: string | null, // present when editing or branching
73
+ callSettings?: { maxTokens, temperature, topP, presencePenalty, frequencyPenalty, seed },
74
+ config?: { apiKey, baseUrl, modelName },
75
+ }
76
+ ```
77
+
78
+ <Callout type="warn">
79
+ The previous wire shape spread `callSettings` and `config` fields at the top level (e.g. `body.modelName`). Both formats are sent for compatibility, but the top-level fields are deprecated. Read from the nested objects.
80
+ </Callout>
81
+
82
+ The endpoint returns a stream of state snapshots using the [`assistant-stream`](https://www.npmjs.com/package/assistant-stream) library ([PyPI](https://pypi.org/project/assistant-stream/)).
83
+
84
+ ### Handling commands
85
+
86
+ ```python
87
+ for command in request.commands:
88
+ if command.type == "add-message":
89
+ # Handle adding a user message
90
+ elif command.type == "add-tool-result":
91
+ # Handle tool execution result
92
+ elif command.type == "my-custom-command":
93
+ # Handle your custom command
94
+ ```
95
+
96
+ ### Streaming updates
97
+
98
+ Mutate `controller.state` inside your run callback:
99
+
100
+ ```python
101
+ from assistant_stream import RunController, create_run
102
+ from assistant_stream.serialization import DataStreamResponse
103
+
104
+ @app.post("/assistant")
105
+ async def chat_endpoint(request: ChatRequest):
106
+ async def run_callback(controller: RunController):
107
+ controller.state["message"] = "Hello" # emits "set" at ["message"]
108
+ controller.state["message"] += " World" # emits "append-text"
109
+
110
+ stream = create_run(run_callback, state=request.state)
111
+ return DataStreamResponse(stream)
112
+ ```
113
+
114
+ State changes are automatically streamed using the operations described in [streaming protocol](#streaming-protocol).
115
+
116
+ ### Cancellation
117
+
118
+ `create_run` exposes `controller.is_cancelled` and `controller.cancelled_event`. If the response stream closes early (user cancel, client disconnect), these are set so your loop can exit cleanly. `create_run` gives callbacks a ~50ms cooperative shutdown window before forced cancellation. Put critical cleanup in `finally` blocks.
119
+
120
+ ```python
121
+ async def run_callback(controller: RunController):
122
+ while not controller.is_cancelled:
123
+ await asyncio.sleep(0.05)
124
+ ```
125
+
126
+ ```python
127
+ async def run_callback(controller: RunController):
128
+ await controller.cancelled_event.wait()
129
+ # cancellation-aware shutdown
130
+ ```
131
+
132
+ ### Backend reference implementation
133
+
134
+ <Tabs items={["Custom agent", "LangGraph"]}>
135
+ <Tab value="Custom agent">
136
+
137
+ ```python
138
+ from assistant_stream.serialization import DataStreamResponse
139
+ from assistant_stream import RunController, create_run
140
+
141
+ @app.post("/assistant")
142
+ async def chat_endpoint(request: ChatRequest):
143
+ async def run_callback(controller: RunController):
144
+ if controller.state is None:
145
+ controller.state = {"messages": []}
146
+
147
+ for command in request.commands:
148
+ if command.type == "add-message":
149
+ controller.state["messages"].append(command.message)
150
+
151
+ async for message in your_agent.stream():
152
+ controller.state["messages"].append(message)
153
+
154
+ stream = create_run(run_callback, state=request.state)
155
+ return DataStreamResponse(stream)
156
+ ```
157
+
158
+ </Tab>
159
+ <Tab value="LangGraph">
160
+
161
+ ```python
162
+ from assistant_stream.serialization import DataStreamResponse
163
+ from assistant_stream import RunController, create_run
164
+ from assistant_stream.modules.langgraph import append_langgraph_event
165
+
166
+ @app.post("/assistant")
167
+ async def chat_endpoint(request: ChatRequest):
168
+ async def run_callback(controller: RunController):
169
+ if controller.state is None:
170
+ controller.state = {"messages": []}
171
+
172
+ input_messages = []
173
+ for command in request.commands:
174
+ if command.type == "add-message":
175
+ text_parts = [
176
+ p.text for p in command.message.parts
177
+ if p.type == "text" and p.text
178
+ ]
179
+ if text_parts:
180
+ input_messages.append(HumanMessage(content=" ".join(text_parts)))
181
+
182
+ async for namespace, event_type, chunk in graph.astream(
183
+ {"messages": input_messages},
184
+ stream_mode=["messages", "updates"],
185
+ subgraphs=True,
186
+ ):
187
+ append_langgraph_event(controller.state, namespace, event_type, chunk)
188
+
189
+ stream = create_run(run_callback, state=request.state)
190
+ return DataStreamResponse(stream)
191
+ ```
192
+
193
+ </Tab>
194
+ </Tabs>
195
+
196
+ Full LangGraph example: [`python/assistant-transport-backend-langgraph`](https://github.com/assistant-ui/assistant-ui/tree/main/python/assistant-transport-backend-langgraph).
197
+
198
+ ## Streaming protocol
199
+
200
+ assistant-stream replicates an arbitrary JSON object via two operations.
201
+
202
+ ### Operations
203
+
204
+ These two operations cover all complex state mutations: `set` for value updates and structure, `append-text` for efficient streaming of text content.
205
+
206
+ #### `set`
207
+
208
+ ```json
209
+ // Operation
210
+ { "type": "set", "path": ["status"], "value": "completed" }
211
+
212
+ // Before
213
+ { "status": "pending" }
214
+
215
+ // After
216
+ { "status": "completed" }
217
+ ```
218
+
219
+ #### `append-text`
220
+
221
+ ```json
222
+ // Operation
223
+ { "type": "append-text", "path": ["message"], "value": " World" }
224
+
225
+ // Before
226
+ { "message": "Hello" }
227
+
228
+ // After
229
+ { "message": "Hello World" }
230
+ ```
231
+
232
+ ### Wire format
233
+
234
+ <Callout type="warn">
235
+ The wire format will migrate to Server-Sent Events (SSE) in a future release.
236
+ </Callout>
237
+
238
+ Inspired by [AI SDK's data stream protocol](https://sdk.vercel.ai/docs/ai-sdk-ui/stream-protocol).
239
+
240
+ **state update:**
241
+
242
+ ```
243
+ aui-state:[{"type":"set","path":["status"],"value":"completed"}]
244
+ ```
245
+
246
+ **error:**
247
+
248
+ ```
249
+ 3:"error message"
250
+ ```
251
+
252
+ ## Building a frontend
253
+
254
+ `useAssistantTransportRuntime` accepts:
255
+
256
+ ```ts
257
+ {
258
+ initialState: T,
259
+ api: string,
260
+ resumeApi?: string,
261
+ protocol?: "data-stream" | "assistant-transport",
262
+ converter: (state: T, connectionMetadata: ConnectionMetadata) => AssistantTransportState,
263
+ headers?: Record<string, string> | Headers | (() => Promise<Record<string, string> | Headers>),
264
+ body?: object | (() => Promise<object | undefined>),
265
+ prepareSendCommandsRequest?: (body: SendCommandsRequestBody) => Record<string, unknown> | Promise<Record<string, unknown>>,
266
+ capabilities?: { edit?: boolean },
267
+ adapters?: { attachments?: AttachmentAdapter; history?: ThreadHistoryAdapter },
268
+ onResponse?: (response: Response) => void,
269
+ onFinish?: () => void,
270
+ onError?: (error: Error, params: { commands: AssistantTransportCommand[]; updateState: (updater: (state: T) => T) => void }) => void | Promise<void>,
271
+ onCancel?: (params: { commands: AssistantTransportCommand[]; updateState: (updater: (state: T) => T) => void; error?: Error }) => void
272
+ }
273
+ ```
274
+
275
+ ### State converter
276
+
277
+ The state converter transforms your agent state into assistant-ui's message format:
278
+
279
+ ```ts
280
+ (
281
+ state: T,
282
+ connectionMetadata: {
283
+ pendingCommands: Command[], // commands not yet sent
284
+ isSending: boolean, // a request is in flight
285
+ toolStatuses: Record<string, ToolExecutionStatus>,
286
+ },
287
+ ) => {
288
+ messages: ThreadMessage[],
289
+ isRunning: boolean,
290
+ state?: ReadonlyJSONValue,
291
+ };
292
+ ```
293
+
294
+ ### Converting messages
295
+
296
+ `unstable_createMessageConverter` transforms agent messages to assistant-ui's format:
297
+
298
+ <Tabs items={["Example", "LangChain"]}>
299
+ <Tab value="Example">
300
+
301
+ ```ts
302
+ import { unstable_createMessageConverter as createMessageConverter } from "@assistant-ui/react";
303
+
304
+ type YourMessage = {
305
+ id: string;
306
+ role: "user" | "assistant";
307
+ content: string;
308
+ timestamp: number;
309
+ };
310
+
311
+ const messageConverter = createMessageConverter((message: YourMessage) => ({
312
+ role: message.role,
313
+ content: [{ type: "text", text: message.content }],
314
+ }));
315
+
316
+ const converter = (state: YourAgentState) => ({
317
+ messages: messageConverter.toThreadMessages(state.messages),
318
+ isRunning: false,
319
+ });
320
+ ```
321
+
322
+ </Tab>
323
+ <Tab value="LangChain">
324
+
325
+ ```ts
326
+ import { unstable_createMessageConverter as createMessageConverter } from "@assistant-ui/react";
327
+ import { convertLangChainMessages } from "@assistant-ui/react-langgraph";
328
+
329
+ const messageConverter = createMessageConverter(convertLangChainMessages);
330
+
331
+ const converter = (state: YourAgentState) => ({
332
+ messages: messageConverter.toThreadMessages(state.messages),
333
+ isRunning: false,
334
+ });
335
+ ```
336
+
337
+ </Tab>
338
+ </Tabs>
339
+
340
+ Retrieve the original message format anywhere via `messageConverter.toOriginalMessage(threadMessage)` or `toOriginalMessages(threadMessage)`.
341
+
342
+ ### Optimistic updates from commands
343
+
344
+ The converter also receives `connectionMetadata.pendingCommands`. Use it to show optimistic UI before the backend responds:
345
+
346
+ ```ts
347
+ const converter = (state: State, connectionMetadata: ConnectionMetadata) => {
348
+ const optimisticMessages = connectionMetadata.pendingCommands
349
+ .filter((c) => c.type === "add-message")
350
+ .map((c) => c.message);
351
+
352
+ return {
353
+ messages: [...state.messages, ...optimisticMessages],
354
+ isRunning: connectionMetadata.isSending || false,
355
+ };
356
+ };
357
+ ```
358
+
359
+ ## Errors and cancellations
360
+
361
+ `onError` and `onCancel` receive `updateState` so you can mutate state on the client without making a server request:
362
+
363
+ ```ts
364
+ const runtime = useAssistantTransportRuntime({
365
+ // ... other options
366
+ onError: (error, { commands, updateState }) => {
367
+ updateState((s) => ({ ...s, lastError: error.message }));
368
+ },
369
+ onCancel: ({ commands, updateState }) => {
370
+ updateState((s) => ({ ...s, status: "cancelled" }));
371
+ },
372
+ });
373
+ ```
374
+
375
+ `onError` receives commands that were in transit. `onCancel` receives both in-transit and queued commands when the user cancels directly; when called after an error, it only receives queued commands (in-transit commands are passed to `onError`).
376
+
377
+ ## Custom headers and body
378
+
379
+ ```ts
380
+ const runtime = useAssistantTransportRuntime({
381
+ // ...
382
+ headers: { Authorization: "Bearer token", "X-Custom-Header": "value" },
383
+ body: { customField: "value" },
384
+ });
385
+ ```
386
+
387
+ Evaluate per-request:
388
+
389
+ ```ts
390
+ const runtime = useAssistantTransportRuntime({
391
+ // ...
392
+ headers: async () => ({
393
+ Authorization: `Bearer ${await getAccessToken()}`,
394
+ "X-Request-ID": crypto.randomUUID(),
395
+ }),
396
+ body: async () => ({
397
+ customField: "value",
398
+ requestId: crypto.randomUUID(),
399
+ timestamp: Date.now(),
400
+ }),
401
+ });
402
+ ```
403
+
404
+ ### Transforming the request body
405
+
406
+ `prepareSendCommandsRequest` lets you transform the entire body before send:
407
+
408
+ ```ts
409
+ const runtime = useAssistantTransportRuntime({
410
+ // ...
411
+ prepareSendCommandsRequest: (body) => ({
412
+ ...body,
413
+ trackingId: crypto.randomUUID(),
414
+ commands: body.commands.map((cmd) =>
415
+ cmd.type === "add-message"
416
+ ? { ...cmd, trackingId: crypto.randomUUID() }
417
+ : cmd,
418
+ ),
419
+ }),
420
+ });
421
+ ```
422
+
423
+ ## Editing messages
424
+
425
+ Editing is disabled by default. Enable it:
426
+
427
+ ```ts
428
+ const runtime = useAssistantTransportRuntime({
429
+ // ...
430
+ capabilities: { edit: true },
431
+ });
432
+ ```
433
+
434
+ `add-message` commands always include `parentId` and `sourceId`:
435
+
436
+ ```ts
437
+ {
438
+ type: "add-message",
439
+ message: { role: "user", parts: [...] },
440
+ parentId: "msg-3", // insert after this message
441
+ sourceId: "msg-4", // ID of the message being replaced (null for new)
442
+ }
443
+ ```
444
+
445
+ ### Backend handling
446
+
447
+ When the backend receives `add-message` with a `parentId`:
448
+
449
+ 1. Truncate all messages after the parent.
450
+ 2. Append the new message.
451
+ 3. Stream the updated state back.
452
+
453
+ ```python
454
+ for command in request.commands:
455
+ if command.type == "add-message":
456
+ if hasattr(command, "parentId") and command.parentId is not None:
457
+ parent_idx = next(
458
+ i for i, m in enumerate(messages) if m.id == command.parentId
459
+ )
460
+ messages = messages[:parent_idx + 1]
461
+ messages.append(command.message)
462
+ ```
463
+
464
+ ## Resuming from a sync server
465
+
466
+ <Callout type="info">
467
+ The sync server is currently part of the enterprise plan; contact us for details.
468
+ </Callout>
469
+
470
+ When a user refreshes the page or reconnects, the backend may still be generating. `resumeRun` reconnects to an active stream.
471
+
472
+ ```ts
473
+ const runtime = useAssistantTransportRuntime({
474
+ // ...
475
+ api: "http://localhost:8010/assistant",
476
+ resumeApi: "http://localhost:8010/resume",
477
+ });
478
+ ```
479
+
480
+ ```tsx
481
+ import { useAui } from "@assistant-ui/react";
482
+ import { useEffect, useRef } from "react";
483
+
484
+ function useResumeOnMount(threadId: string) {
485
+ const aui = useAui();
486
+ const checkedRef = useRef(false);
487
+
488
+ useEffect(() => {
489
+ if (checkedRef.current) return;
490
+ checkedRef.current = true;
491
+
492
+ (async () => {
493
+ const status = await fetch(`/api/sync-server/status/${threadId}`).then((r) =>
494
+ r.json(),
495
+ );
496
+ if (status.isRunning) {
497
+ const parentId = aui.thread().getState().messages.at(-1)?.id ?? null;
498
+ aui.thread().resumeRun({ parentId });
499
+ }
500
+ })();
501
+ }, [aui, threadId]);
502
+ }
503
+ ```
504
+
505
+ For `AssistantTransport`, do not pass a `stream` parameter; the runtime uses the configured `resumeApi`.
506
+
507
+ ## Accessing runtime state
508
+
509
+ `useAssistantTransportState` reads the current agent state from any component:
510
+
511
+ ```ts
512
+ import { useAssistantTransportState } from "@assistant-ui/react";
513
+
514
+ function MyComponent() {
515
+ const state = useAssistantTransportState();
516
+ return <div>{JSON.stringify(state)}</div>;
517
+ }
518
+
519
+ function MessageCount() {
520
+ const messages = useAssistantTransportState((state) => state.messages);
521
+ return <div>Message count: {messages.length}</div>;
522
+ }
523
+ ```
524
+
525
+ ### Type safety
526
+
527
+ Augment the module to type your agent state:
528
+
529
+ ```ts title="assistant.config.ts"
530
+ import "@assistant-ui/react";
531
+
532
+ declare module "@assistant-ui/react" {
533
+ namespace Assistant {
534
+ interface ExternalState {
535
+ myState: {
536
+ messages: Message[];
537
+ customField: string;
538
+ };
539
+ }
540
+ }
541
+ }
542
+ ```
543
+
544
+ Place this file anywhere in your project; TypeScript picks it up via module resolution. `useAssistantTransportState` becomes fully typed.
545
+
546
+ ### Accessing the original message
547
+
548
+ If you used `createMessageConverter`, retrieve the original message from any assistant-ui state:
549
+
550
+ ```ts
551
+ import { useAuiState } from "@assistant-ui/react";
552
+
553
+ function MyMessageComponent() {
554
+ const message = useAuiState((s) => s.message);
555
+ const original = messageConverter.toOriginalMessage(message);
556
+ return <div>{original.yourCustomField}</div>;
557
+ }
558
+ ```
559
+
560
+ `toOriginalMessages` returns all source messages when a `ThreadMessage` was created from multiple sources.
561
+
562
+ ## Frontend reference implementation
563
+
564
+ ```tsx
565
+ "use client";
566
+
567
+ import {
568
+ AssistantRuntimeProvider,
569
+ AssistantTransportConnectionMetadata,
570
+ useAssistantTransportRuntime,
571
+ } from "@assistant-ui/react";
572
+
573
+ type State = { messages: Message[] };
574
+
575
+ const converter = (
576
+ state: State,
577
+ connectionMetadata: AssistantTransportConnectionMetadata,
578
+ ) => {
579
+ const optimistic = connectionMetadata.pendingCommands
580
+ .filter((c) => c.type === "add-message")
581
+ .map((c) => c.message);
582
+
583
+ return {
584
+ messages: [...state.messages, ...optimistic],
585
+ isRunning: connectionMetadata.isSending || false,
586
+ };
587
+ };
588
+
589
+ export function MyRuntimeProvider({ children }) {
590
+ const runtime = useAssistantTransportRuntime({
591
+ initialState: { messages: [] },
592
+ api: "http://localhost:8010/assistant",
593
+ converter,
594
+ headers: async () => ({ Authorization: "Bearer token" }),
595
+ body: { "custom-field": "custom-value" },
596
+ onError: (error, { commands, updateState }) => {
597
+ updateState((s) => ({ ...s, lastError: error.message }));
598
+ },
599
+ onCancel: ({ commands, updateState }) => {
600
+ updateState((s) => ({ ...s, status: "cancelled" }));
601
+ },
602
+ });
603
+
604
+ return (
605
+ <AssistantRuntimeProvider runtime={runtime}>
606
+ {children}
607
+ </AssistantRuntimeProvider>
608
+ );
609
+ }
610
+ ```
611
+
612
+ Full example: [`examples/with-assistant-transport`](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-assistant-transport). A LangChain variant lives in the same examples directory.
613
+
614
+ ## Custom commands
615
+
616
+ ### Define
617
+
618
+ ```ts title="assistant.config.ts"
619
+ import "@assistant-ui/react";
620
+
621
+ declare module "@assistant-ui/react" {
622
+ namespace Assistant {
623
+ interface Commands {
624
+ myCustomCommand: {
625
+ type: "my-custom-command";
626
+ data: string;
627
+ };
628
+ }
629
+ }
630
+ }
631
+ ```
632
+
633
+ ### Issue
634
+
635
+ ```ts
636
+ import { useAssistantTransportSendCommand } from "@assistant-ui/react";
637
+
638
+ function MyComponent() {
639
+ const sendCommand = useAssistantTransportSendCommand();
640
+ return (
641
+ <button
642
+ onClick={() => sendCommand({ type: "my-custom-command", data: "hello" })}
643
+ >
644
+ Send
645
+ </button>
646
+ );
647
+ }
648
+ ```
649
+
650
+ ### Handle on the backend
651
+
652
+ ```python
653
+ for command in request.commands:
654
+ if command.type == "my-custom-command":
655
+ data = command.data
656
+ ```
657
+
658
+ ### Optimistic updates
659
+
660
+ ```ts
661
+ const converter = (state: State, connectionMetadata: ConnectionMetadata) => {
662
+ const customCommands = connectionMetadata.pendingCommands.filter(
663
+ (c) => c.type === "my-custom-command",
664
+ );
665
+ return {
666
+ messages: state.messages,
667
+ state: { ...state, customData: customCommands.map((c) => c.data) },
668
+ isRunning: connectionMetadata.isSending || false,
669
+ };
670
+ };
671
+ ```
672
+
673
+ Custom commands follow the same lifecycle as built-in ones; check for them in `onError` and `onCancel` if needed.
674
+
675
+ ## Adapter support
676
+
677
+ | Adapter | Supported via |
678
+ | --- | --- |
679
+ | Attachments | `adapters.attachments` |
680
+ | History | `adapters.history` |
681
+ | threadList | Via [thread list adapter](/docs/runtimes/concepts/threads) |
682
+
683
+ Speech, dictation, feedback, and suggestions are not currently exposed by `AssistantTransport`. Drop down to `ExternalStoreRuntime` if you need them.
684
+
685
+ ## Related
686
+
687
+ <Cards>
688
+ <Card
689
+ title="ExternalStoreRuntime"
690
+ description="The core runtime AssistantTransport is built on."
691
+ href="/docs/runtimes/custom/external-store"
692
+ />
693
+ <Card
694
+ title="Data Stream"
695
+ description="Message-streaming protocol on top of LocalRuntime."
696
+ href="/docs/runtimes/custom/data-stream"
697
+ />
698
+ <Card
699
+ title="Adapters"
700
+ description="Attachments, history, and the rest of the shared adapter contracts."
701
+ href="/docs/runtimes/concepts/adapters"
702
+ />
703
+ </Cards>