@assistant-ui/mcp-docs-server 0.1.34 → 0.1.36

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 (127) hide show
  1. package/.docs/organized/code-examples/waterfall.md +4 -4
  2. package/.docs/organized/code-examples/with-a2a.md +5 -5
  3. package/.docs/organized/code-examples/with-ag-ui.md +6 -6
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +7 -7
  5. package/.docs/organized/code-examples/with-artifacts.md +7 -7
  6. package/.docs/organized/code-examples/with-assistant-transport.md +73 -57
  7. package/.docs/organized/code-examples/with-browser-extension.md +7 -7
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +44 -93
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +7 -7
  10. package/.docs/organized/code-examples/with-cloud.md +7 -7
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +7 -7
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +9 -9
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +9 -9
  14. package/.docs/organized/code-examples/with-eve.md +343 -0
  15. package/.docs/organized/code-examples/with-expo.md +943 -940
  16. package/.docs/organized/code-examples/with-external-store.md +5 -5
  17. package/.docs/organized/code-examples/with-ffmpeg.md +8 -11
  18. package/.docs/organized/code-examples/with-generative-ui.md +33 -309
  19. package/.docs/organized/code-examples/with-google-adk.md +5 -5
  20. package/.docs/organized/code-examples/with-heat-graph.md +4 -4
  21. package/.docs/organized/code-examples/with-image-generation.md +7 -7
  22. package/.docs/organized/code-examples/with-interactables.md +169 -341
  23. package/.docs/organized/code-examples/with-langchain.md +7 -7
  24. package/.docs/organized/code-examples/with-langgraph.md +23 -160
  25. package/.docs/organized/code-examples/with-livekit.md +10 -10
  26. package/.docs/organized/code-examples/with-mcp.md +7 -7
  27. package/.docs/organized/code-examples/with-opencode.md +106 -580
  28. package/.docs/organized/code-examples/with-pi.md +2046 -0
  29. package/.docs/organized/code-examples/with-react-hook-form.md +16 -9
  30. package/.docs/organized/code-examples/with-react-ink-web.md +691 -0
  31. package/.docs/organized/code-examples/with-react-ink.md +29 -17
  32. package/.docs/organized/code-examples/with-react-router.md +12 -12
  33. package/.docs/organized/code-examples/with-resumable-stream.md +8 -8
  34. package/.docs/organized/code-examples/with-store.md +14 -10
  35. package/.docs/organized/code-examples/with-tanstack.md +21 -7
  36. package/.docs/organized/code-examples/with-tap-runtime.md +5 -5
  37. package/.docs/organized/code-examples/with-virtualized-thread.md +676 -0
  38. package/.docs/raw/docs/(docs)/architecture.mdx +13 -12
  39. package/.docs/raw/docs/(docs)/cli.mdx +4 -2
  40. package/.docs/raw/docs/(docs)/devtools.mdx +25 -2
  41. package/.docs/raw/docs/(docs)/index.mdx +5 -2
  42. package/.docs/raw/docs/(docs)/installation.mdx +5 -2
  43. package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +16 -16
  44. package/.docs/raw/docs/(reference)/api-reference/hooks/composer-triggers.mdx +73 -42
  45. package/.docs/raw/docs/(reference)/api-reference/hooks/model-context.mdx +3 -20
  46. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +234 -123
  47. package/.docs/raw/docs/(reference)/api-reference/integrations/eve.mdx +51 -0
  48. package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +4 -1
  49. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +70 -38
  50. package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -1
  51. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +30 -0
  52. package/.docs/raw/docs/(reference)/api-reference/primitives/attachment.mdx +7 -0
  53. package/.docs/raw/docs/(reference)/api-reference/primitives/branch-picker.mdx +26 -0
  54. package/.docs/raw/docs/(reference)/api-reference/primitives/chain-of-thought.mdx +15 -0
  55. package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +103 -0
  56. package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +16 -0
  57. package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +56 -0
  58. package/.docs/raw/docs/(reference)/api-reference/primitives/queue-item.mdx +12 -0
  59. package/.docs/raw/docs/(reference)/api-reference/primitives/selection-toolbar.mdx +12 -0
  60. package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +14 -0
  61. package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +57 -1
  62. package/.docs/raw/docs/(reference)/api-reference/tools/index.mdx +6 -0
  63. package/.docs/raw/docs/(reference)/api-reference/tools/interactables-legacy.mdx +55 -0
  64. package/.docs/raw/docs/(reference)/api-reference/tools/interactables.mdx +151 -0
  65. package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +17 -38
  66. package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +22 -0
  67. package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +20 -27
  68. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +40 -25
  69. package/.docs/raw/docs/guides/chain-of-thought.mdx +1 -1
  70. package/.docs/raw/docs/guides/headless-composer-input.mdx +113 -0
  71. package/.docs/raw/docs/guides/index.mdx +3 -0
  72. package/.docs/raw/docs/guides/input-history.mdx +55 -0
  73. package/.docs/raw/docs/guides/latex.mdx +28 -22
  74. package/.docs/raw/docs/guides/mentions.mdx +32 -7
  75. package/.docs/raw/docs/guides/slash-commands.mdx +3 -6
  76. package/.docs/raw/docs/guides/speech.mdx +5 -7
  77. package/.docs/raw/docs/guides/virtualization.mdx +133 -0
  78. package/.docs/raw/docs/guides/voice.mdx +3 -2
  79. package/.docs/raw/docs/ink/hooks.mdx +2 -2
  80. package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +4 -12
  81. package/.docs/raw/docs/integrations/auth/better-auth.mdx +7 -10
  82. package/.docs/raw/docs/integrations/auth/clerk.mdx +3 -9
  83. package/.docs/raw/docs/integrations/auth/next-auth.mdx +3 -8
  84. package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +8 -5
  85. package/.docs/raw/docs/integrations/index.mdx +5 -12
  86. package/.docs/raw/docs/integrations/observability/helicone.mdx +3 -4
  87. package/.docs/raw/docs/integrations/observability/langfuse.mdx +3 -5
  88. package/.docs/raw/docs/integrations/observability/langsmith.mdx +3 -2
  89. package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +4 -3
  90. package/.docs/raw/docs/primitives/composer.mdx +8 -0
  91. package/.docs/raw/docs/primitives/thread.mdx +24 -0
  92. package/.docs/raw/docs/react-native/hooks.mdx +1 -1
  93. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +22 -3
  94. package/.docs/raw/docs/runtimes/concepts/stability.mdx +1 -1
  95. package/.docs/raw/docs/runtimes/custom/data-stream.mdx +1 -1
  96. package/.docs/raw/docs/runtimes/custom/external-store.mdx +54 -0
  97. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +108 -4
  98. package/.docs/raw/docs/runtimes/eve/overview.mdx +100 -0
  99. package/.docs/raw/docs/runtimes/eve/quickstart.mdx +143 -0
  100. package/.docs/raw/docs/runtimes/langchain.mdx +386 -18
  101. package/.docs/raw/docs/runtimes/langgraph/quickstart.mdx +1 -1
  102. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-1.mdx +1 -1
  103. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +12 -6
  104. package/.docs/raw/docs/tools/defining-tools.mdx +4 -0
  105. package/.docs/raw/docs/tools/interactables-legacy.mdx +410 -0
  106. package/.docs/raw/docs/tools/interactables.mdx +892 -223
  107. package/.docs/raw/docs/tools/mcp.mdx +4 -4
  108. package/.docs/raw/docs/tools/multi-agent.mdx +5 -0
  109. package/.docs/raw/docs/tools/tool-ui.mdx +37 -1
  110. package/.docs/raw/docs/ui/composer-trigger-popover.mdx +2 -0
  111. package/.docs/raw/docs/ui/dot-matrix.mdx +133 -0
  112. package/.docs/raw/docs/ui/file.mdx +1 -1
  113. package/.docs/raw/docs/ui/model-selector.mdx +219 -52
  114. package/.docs/raw/docs/ui/number-roll.mdx +154 -0
  115. package/.docs/raw/docs/ui/part-grouping.mdx +38 -0
  116. package/.docs/raw/docs/ui/reasoning.mdx +3 -3
  117. package/.docs/raw/docs/ui/streamdown.mdx +2 -0
  118. package/.docs/raw/docs/ui/syntax-highlighting.mdx +1 -1
  119. package/.docs/raw/docs/ui/thread.mdx +52 -0
  120. package/.docs/raw/docs/ui/tool-fallback.mdx +2 -2
  121. package/.docs/raw/docs/utilities/react-o11y.mdx +92 -6
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js +18 -2
  124. package/dist/index.js.map +1 -1
  125. package/package.json +4 -4
  126. package/src/index.ts +14 -6
  127. package/src/tools/tests/mcp-protocol.test.ts +9 -0
@@ -0,0 +1,154 @@
1
+ ---
2
+ title: Number Roll
3
+ description: Animated number that rolls digits odometer-style when the value changes.
4
+ platforms: ["react"]
5
+ ---
6
+
7
+ import { PreviewCode } from "@/components/docs/preview-code.server";
8
+ import {
9
+ NumberRollSample,
10
+ NumberRollCompactSample,
11
+ NumberRollFormatSample,
12
+ NumberRollLiveSample,
13
+ } from "@/components/docs/samples/number-roll";
14
+
15
+ <Callout>
16
+ This is a **standalone component** that does not depend on the assistant-ui runtime. Use it anywhere in your application.
17
+ </Callout>
18
+
19
+ <NumberRollSample />
20
+
21
+ ## Installation
22
+
23
+ <InstallCommand shadcn={["number-roll"]} />
24
+
25
+ This adds a `/components/assistant-ui/number-roll.tsx` file to your project, which you can adjust as needed. The component has no dependencies beyond React.
26
+
27
+ ## Usage
28
+
29
+ ```tsx
30
+ import { NumberRoll } from "@/components/assistant-ui/number-roll";
31
+
32
+ export function TokenCounter({ count }: { count: number }) {
33
+ return <NumberRoll value={count} format={{ notation: "compact" }} />;
34
+ }
35
+ ```
36
+
37
+ When `value` changes, each digit rolls in place to its new value. Formatting is handled by `Intl.NumberFormat`, so structural changes animate too: when `700` becomes `1.1K` with compact notation, the surviving ones digit rolls from 0 to 1 while the leading `70` slides out and the decimal point, fraction digit, and `K` suffix slide in.
38
+
39
+ ## Examples
40
+
41
+ ### Compact Notation
42
+
43
+ Pass any `Intl.NumberFormatOptions` via the `format` prop. Crossing a compact-notation threshold animates the formatting change instead of remounting the whole string.
44
+
45
+ <PreviewCode file="components/docs/samples/number-roll" name="NumberRollCompactSample">
46
+ <NumberRollCompactSample />
47
+ </PreviewCode>
48
+
49
+ ### Formats and Locales
50
+
51
+ Currencies, percentages, suffixes, and non-English locales all work through the same `Intl.NumberFormat` pipeline. Compact thresholds and suffixes are locale-dependent (`1.2万` in `zh-CN`), so never hardcode them.
52
+
53
+ <PreviewCode file="components/docs/samples/number-roll" name="NumberRollFormatSample">
54
+ <NumberRollFormatSample />
55
+ </PreviewCode>
56
+
57
+ ### Live Counter
58
+
59
+ Rapid successive updates retarget the in-flight roll smoothly, which makes the component suitable for streaming token counts and other live metrics.
60
+
61
+ <PreviewCode file="components/docs/samples/number-roll" name="NumberRollLiveSample">
62
+ <NumberRollLiveSample />
63
+ </PreviewCode>
64
+
65
+ ## How It Works
66
+
67
+ The value is formatted with `Intl.NumberFormat.formatToParts` and split into keyed parts. Integer digits are keyed by place value counted from the right, so when `999` becomes `1,000` the existing columns keep their identity and roll in place while only the new leading digit and separator enter. Symbols (decimal point, group separators, currency signs, compact suffixes) cross-fade and collapse via animated `grid-template-columns`.
68
+
69
+ Each digit renders a strip of 0 to 9 and animates a registered CSS custom property; CSS `mod()` math wraps the strip into an endless ribbon, so rolling from 9 to 0 continues in the trend direction instead of spinning backwards. There is no JavaScript animation loop and no animation library dependency.
70
+
71
+ In browsers without CSS `mod()` support, and during server rendering, the component renders the plain formatted string. With `prefers-reduced-motion`, values swap instantly without rolling. When server rendering, pass an explicit `locales`: the server's default locale can differ from the visitor's browser locale, and a mismatched formatted string causes a React hydration error. Locales whose default numbering system is non-Latin (such as `ar-EG`) cross-fade their digits as symbols instead of rolling.
72
+
73
+ The formatted value is exposed to screen readers as plain text while the animated digits are `aria-hidden`. Value changes are not announced automatically; wrap the component in an `aria-live` region if you need announcements.
74
+
75
+ ## API Reference
76
+
77
+ ### NumberRoll
78
+
79
+ <ParametersTable
80
+ type="NumberRollProps"
81
+ parameters={[
82
+ {
83
+ name: "value",
84
+ type: "number",
85
+ required: true,
86
+ description: "The number to display.",
87
+ },
88
+ {
89
+ name: "format",
90
+ type: "Intl.NumberFormatOptions",
91
+ description:
92
+ "Number formatting options, e.g. `{ notation: \"compact\" }` or `{ style: \"currency\", currency: \"USD\" }`.",
93
+ },
94
+ {
95
+ name: "locales",
96
+ type: "Intl.LocalesArgument",
97
+ description:
98
+ "Locale(s) passed to `Intl.NumberFormat`. Pass an explicit value in server-rendered apps so the server and client format identically.",
99
+ },
100
+ {
101
+ name: "prefix",
102
+ type: "string",
103
+ description: "Static text rendered before the number.",
104
+ },
105
+ {
106
+ name: "suffix",
107
+ type: "string",
108
+ description: "Static text rendered after the number.",
109
+ },
110
+ {
111
+ name: "trend",
112
+ type: '"auto" | "up" | "down"',
113
+ default: '"auto"',
114
+ description:
115
+ "Roll direction. `auto` follows the sign of the value change; `up` and `down` force a direction, wrapping digits through 9/0 as needed.",
116
+ },
117
+ {
118
+ name: "duration",
119
+ type: "number",
120
+ default: "500",
121
+ description:
122
+ "Digit roll duration in milliseconds. Enter/exit fades run at 60% of this value.",
123
+ },
124
+ {
125
+ name: "className",
126
+ type: "string",
127
+ description: "Additional CSS classes.",
128
+ },
129
+ ]}
130
+ />
131
+
132
+ ### Styling
133
+
134
+ The root applies `tabular-nums` so digits keep a constant width while rolling; the font you use must provide tabular figures for the layout to stay stable. Size and color are inherited from the surrounding text, so style it like any span:
135
+
136
+ ```tsx
137
+ <NumberRoll value={value} className="text-4xl font-semibold" />
138
+ ```
139
+
140
+ Change the animation speed via the `duration` prop; it drives both the CSS timing and the cleanup that unmounts exited characters, so overriding the duration variable in CSS alone would remove them mid-fade. The fade and easing variables are safe to override via the `style` prop (the defaults are set as inline styles, so plain `className` overrides do not apply):
141
+
142
+ | Variable | Default | Description |
143
+ |----------|---------|-------------|
144
+ | `--aui-number-roll-duration` | `500ms` (from the `duration` prop) | Digit roll duration. |
145
+ | `--aui-number-roll-fade` | 60% of the duration | Enter/exit fade and width-collapse duration. |
146
+ | `--aui-number-roll-ease` | `cubic-bezier(0.23, 1, 0.32, 1)` | Digit roll easing. |
147
+
148
+ Parts are targetable via data attributes: `[data-slot="number-roll"]`, `[data-slot="number-roll-part"]`, `[data-slot="number-roll-digit"]`, and `[data-slot="number-roll-symbol"]`.
149
+
150
+ ## Related Components
151
+
152
+ - [Context Display](/docs/ui/context-display) - Live token usage ring and bar
153
+ - [Message Timing](/docs/ui/message-timing) - Streaming stats badge
154
+ - [Badge](/docs/ui/badge) - Small status and metadata labels
@@ -252,6 +252,44 @@ The synthetic `"standalone-tool-call"` key on `groupPartByType` matches all of t
252
252
  The `"mcp-app"` key is deprecated in favor of `"standalone-tool-call"`, which is a superset (it also matches MCP-app tool calls). Existing `"mcp-app"` usage keeps working.
253
253
  </Callout>
254
254
 
255
+ ### Render Tool Calls as Flat Rows
256
+
257
+ The previous section keeps `"tool-call"` folded into the chain-of-thought and only lifts standalone tool UIs out. To keep reasoning collapsed in a group while every tool call renders as its own row in the message flow, map both tool keys to `[]`:
258
+
259
+ ```tsx
260
+ <MessagePrimitive.GroupedParts
261
+ groupBy={groupPartByType({
262
+ reasoning: ["group-thought", "group-reasoning"],
263
+ "tool-call": [],
264
+ "standalone-tool-call": [],
265
+ })}
266
+ >
267
+ {({ part, children }) => {
268
+ switch (part.type) {
269
+ case "group-thought":
270
+ case "group-reasoning":
271
+ return <ReasoningBlock>{children}</ReasoningBlock>;
272
+ case "reasoning":
273
+ return <Reasoning {...part} />;
274
+ case "tool-call":
275
+ return part.toolUI ?? <ToolFallback {...part} />;
276
+ case "text":
277
+ return <MarkdownText />;
278
+ case "indicator":
279
+ return <LoadingDots />;
280
+ default:
281
+ return null;
282
+ }
283
+ }}
284
+ </MessagePrimitive.GroupedParts>
285
+ ```
286
+
287
+ Adjacent `reasoning` parts still coalesce into one `group-thought`, while each tool call is a leaf at the same level as text — the `"standalone-tool-call"` entry is mapped to `[]` too so human-in-the-loop and generative-UI tools stay flat alongside routine calls instead of falling through to `"tool-call"`. Tool-call leaves keep their stable identity keys across re-renders, so rows don't remount as the message streams.
288
+
289
+ <Callout type="warn">
290
+ This renders tool calls flat **in the message flow** — each leaf sits where the part appears, in order. It does not pin, portal, float, or otherwise move tool UIs out of the flow into a sidebar or fixed region. For out-of-flow placement you still own the layout outside `GroupedParts`.
291
+ </Callout>
292
+
255
293
  ### Group by Custom Metadata
256
294
 
257
295
  Use any custom metadata in your parts for grouping:
@@ -28,7 +28,7 @@ Previously, reasoning parts were rendered via `components.Reasoning` and grouped
28
28
 
29
29
  Render reasoning parts through `MessagePrimitive.GroupedParts`. Group consecutive reasoning parts with `"group-reasoning"`, then compose `ReasoningRoot`, `ReasoningTrigger`, `ReasoningContent`, and `ReasoningText` around the grouped children.
30
30
 
31
- While reasoning is streaming, `part.status.type === "running"`. Pass that to `defaultOpen` so the accordion auto-opens during streaming and lets the user collapse it once the model moves on.
31
+ While reasoning is streaming, `part.status.type === "running"`. Pass that to `streaming` so the accordion auto-opens during streaming with a bottom-pinned live preview of the newest tokens, auto-collapses when the model moves on, and permanently defers to the first manual toggle. When `streaming` is provided it supersedes `defaultOpen`.
32
32
 
33
33
  ```tsx title="/app/components/assistant-ui/thread.tsx"
34
34
  import { MessagePrimitive, groupPartByType } from "@assistant-ui/react";
@@ -56,7 +56,7 @@ const AssistantMessage: FC = () => {
56
56
  case "group-reasoning": { // [!code ++]
57
57
  const running = part.status.type === "running"; // [!code ++]
58
58
  return ( // [!code ++]
59
- <ReasoningRoot defaultOpen={running}> // [!code ++]
59
+ <ReasoningRoot streaming={running}> // [!code ++]
60
60
  <ReasoningTrigger active={running} /> // [!code ++]
61
61
  <ReasoningContent aria-busy={running}> // [!code ++]
62
62
  <ReasoningText>{children}</ReasoningText> // [!code ++]
@@ -139,7 +139,7 @@ const ReasoningGroupImpl: ReasoningGroupComponent = ({
139
139
  });
140
140
 
141
141
  return (
142
- <ReasoningRoot defaultOpen={isReasoningStreaming}>
142
+ <ReasoningRoot streaming={isReasoningStreaming}>
143
143
  <ReasoningTrigger active={isReasoningStreaming} />
144
144
  <ReasoningContent aria-busy={isReasoningStreaming}>
145
145
  <ReasoningText>{children}</ReasoningText>
@@ -119,6 +119,8 @@ const StreamdownText = () => (
119
119
  | `components` | `object` | - | Custom components including `SyntaxHighlighter` and `CodeHeader` |
120
120
  | `componentsByLanguage` | `object` | - | Language-specific component overrides |
121
121
  | `preprocess` | `(text: string) => string` | - | Text preprocessing function |
122
+ | `defer` | `boolean` | `false` | Defer markdown parsing to a lower priority via `useDeferredValue` so typing and scrolling stay responsive during streaming |
123
+ | `smooth` | `boolean \| SmoothOptions` | `false` | Typewriter-style reveal via `useSmooth`; the caret and controls stay active until the reveal catches up. Prefer the native `animated` prop for entrance animations |
122
124
  | `controls` | `boolean \| object` | `true` | Enable/disable UI controls for code blocks and tables |
123
125
  | `caret` | `"block" \| "circle"` | - | Streaming caret style |
124
126
  | `mermaid` | `MermaidOptions` | - | Mermaid diagram configuration |
@@ -29,7 +29,7 @@ import { SyntaxHighlightingSample } from "@/components/docs/samples/syntax-highl
29
29
 
30
30
  This adds a `/components/assistant-ui/shiki-highlighter.tsx` file to your project and
31
31
  installs the `react-shiki` dependency. The highlighter can be customized by editing
32
- the config in the `shiki-highlighter.tsx` file.
32
+ the config in the `shiki-highlighter.tsx` file. While a message part is still streaming, the component renders the plain code without tokenization and highlights once the part settles, so streaming stays cheap.
33
33
 
34
34
  </Step>
35
35
  <Step>
@@ -133,6 +133,58 @@ const SuggestionItem = () => (
133
133
  );
134
134
  ```
135
135
 
136
+ ## Component Overrides
137
+
138
+ `Thread` accepts an optional `components` prop that swaps parts of the rendering without editing the copied file. All slots are optional; omitted slots keep the built-in rendering.
139
+
140
+ ```tsx
141
+ import { Thread, type ThreadComponents } from "@/components/assistant-ui/thread";
142
+
143
+ const THREAD_COMPONENTS: ThreadComponents = {
144
+ ToolFallback: MyToolFallback,
145
+ ToolGroup: MyToolGroup,
146
+ };
147
+
148
+ export default function Chat() {
149
+ return <Thread components={THREAD_COMPONENTS} />;
150
+ }
151
+ ```
152
+
153
+ <ParametersTable
154
+ type="ThreadComponents"
155
+ parameters={[
156
+ {
157
+ name: "AssistantMessage",
158
+ type: "ComponentType",
159
+ description: "Replaces the entire assistant message, including the action bar and branch picker.",
160
+ },
161
+ {
162
+ name: "Welcome",
163
+ type: "ComponentType",
164
+ description: "Replaces the welcome screen shown for a new chat.",
165
+ },
166
+ {
167
+ name: "ToolFallback",
168
+ type: "ToolCallMessagePartComponent",
169
+ description: "Renders tool calls that have no tool UI registered by name. Registered tool UIs take precedence over this slot.",
170
+ },
171
+ {
172
+ name: "ToolGroup",
173
+ type: "ComponentType<PropsWithChildren<{ group: ThreadGroupPart }>>",
174
+ description: "Wraps runs of consecutive tool calls. Receives the group part (`indices`, `status`) and the rendered children.",
175
+ },
176
+ {
177
+ name: "ReasoningGroup",
178
+ type: "ComponentType<PropsWithChildren<{ group: ThreadGroupPart }>>",
179
+ description: "Wraps runs of consecutive reasoning parts. Receives the group part and the rendered children.",
180
+ },
181
+ ]}
182
+ />
183
+
184
+ Define the `components` object once at module scope (or memoize it) so message subtrees do not re-render whenever the parent re-renders.
185
+
186
+ For per-tool UI, prefer registering a renderer by tool name over overriding `ToolFallback`: put `render` on the matching toolkit entry (see [Tool UI](/docs/tools/tool-ui)). `data` message parts render through renderers registered with `useAssistantDataUI`; parts without a registered renderer are not displayed.
187
+
136
188
  ## API Reference
137
189
 
138
190
  The following primitives are used within the Thread component and can be customized in your `/components/assistant-ui/thread.tsx` file.
@@ -74,7 +74,7 @@ Shows a muted appearance when a tool call was cancelled.
74
74
 
75
75
  ### Approval State
76
76
 
77
- Shows the default Allow / Deny buttons rendered when a tool call enters `requires-action`. The block auto-expands so the decision is visible without a click. Edit your project's copy of `tool-fallback.tsx` to add trust escalation, edit-args, custom labels, or analytics hooks — the shadcn philosophy is you own the file.
77
+ Shows the default Allow / Deny buttons rendered when a tool call enters `requires-action`. A tool call whose approval gate is already decided renders no buttons. The block auto-expands so the decision is visible without a click. Edit your project's copy of `tool-fallback.tsx` to add trust escalation, edit-args, custom labels, or analytics hooks — the shadcn philosophy is you own the file.
78
78
 
79
79
  <ToolFallbackRequiresActionSample />
80
80
 
@@ -90,7 +90,7 @@ All sub-components are exported for custom layouts:
90
90
  | `ToolFallback.Args` | Displays tool arguments |
91
91
  | `ToolFallback.Result` | Displays tool execution result |
92
92
  | `ToolFallback.Error` | Displays error or cancellation messages |
93
- | `ToolFallback.Approval` | Renders Allow / Deny buttons for `requires-action` tools; wires `resume` / `addResult` / `respondToApproval` |
93
+ | `ToolFallback.Approval` | Renders Allow / Deny buttons for `requires-action` tools until a decision is recorded; wires `resume` / `addResult` / `respondToApproval` |
94
94
 
95
95
  ```tsx
96
96
  import {
@@ -1,11 +1,18 @@
1
1
  ---
2
2
  title: react-o11y
3
- description: Headless primitives for visualizing observability spans (traces, waterfalls).
3
+ description: Headless primitives for visualizing observability spans as collapsible trace trees, waterfalls, and agent and LLM call timelines.
4
4
  platforms: ["react"]
5
5
  ---
6
6
 
7
+ import { WaterfallSample } from "@/components/docs/samples/o11y/waterfall";
8
+ import {
9
+ StatusSample,
10
+ CollapseSample,
11
+ StreamingSample,
12
+ } from "@/components/docs/samples/o11y/capability-samples";
13
+
7
14
  <Callout type="warn">
8
- `@assistant-ui/react-o11y` is currently v0.0.11 and experimental. The API may change without notice.
15
+ `@assistant-ui/react-o11y` is experimental. The API may change without notice.
9
16
  </Callout>
10
17
 
11
18
  `@assistant-ui/react-o11y` provides headless, Radix-style primitives for rendering observability spans as collapsible, indented hierarchical UIs. Use it to build trace inspectors, waterfall debug panels, or LLM call timelines on top of your own span data source.
@@ -15,6 +22,10 @@ platforms: ["react"]
15
22
  - **Tree-aware** — Automatic depth, parent / child collapse, time range computation.
16
23
  - **Reactive** — Built on the same reactive core as the runtimes, so spans stream into your UI as they change.
17
24
 
25
+ <div className="not-prose my-6">
26
+ <WaterfallSample />
27
+ </div>
28
+
18
29
  ## Installation
19
30
 
20
31
  <InstallCommand npm={["@assistant-ui/react-o11y"]} />
@@ -43,13 +54,12 @@ function SpanRow() {
43
54
  <SpanPrimitive.StatusIndicator className="size-2 rounded-full data-[span-status=running]:bg-yellow-500 data-[span-status=completed]:bg-green-500 data-[span-status=failed]:bg-red-500" />
44
55
  <SpanPrimitive.TypeBadge className="rounded bg-muted px-1.5 text-xs" />
45
56
  <SpanPrimitive.Name className="text-sm" />
46
- <SpanPrimitive.Children components={{ Span: SpanRow }} />
47
57
  </SpanPrimitive.Root>
48
58
  );
49
59
  }
50
60
 
51
61
  export function TraceView({ spans }: { spans: SpanData[] }) {
52
- const aui = useAui({ resource: SpanResource({ spans }) });
62
+ const aui = useAui({ span: SpanResource({ spans }) });
53
63
 
54
64
  return (
55
65
  <AuiProvider value={aui}>
@@ -61,6 +71,32 @@ export function TraceView({ spans }: { spans: SpanData[] }) {
61
71
 
62
72
  `SpanData` is the input shape your code produces (see [SpanData](#spandata)). `SpanResource` enriches it with depth, child counts, time range, and collapse state.
63
73
 
74
+ ## Examples
75
+
76
+ ### Status states
77
+
78
+ `data-span-status` exposes each span's lifecycle, so running, completed, failed, and skipped are styled with pure CSS.
79
+
80
+ <div className="not-prose my-6">
81
+ <StatusSample />
82
+ </div>
83
+
84
+ ### Collapsible subtrees
85
+
86
+ `SpanPrimitive.CollapseToggle` removes a span's descendants from the visible flat list, not just hides them. Click a chevron to collapse a subtree.
87
+
88
+ <div className="not-prose my-6">
89
+ <CollapseSample />
90
+ </div>
91
+
92
+ ### Streaming spans
93
+
94
+ `SpanResource` re-derives the tree whenever the `spans` prop changes, so spans stream in and update live.
95
+
96
+ <div className="not-prose my-6">
97
+ <StreamingSample />
98
+ </div>
99
+
64
100
  ## Anatomy
65
101
 
66
102
  ```tsx
@@ -71,7 +107,7 @@ import {
71
107
  } from "@assistant-ui/react-o11y";
72
108
 
73
109
  // Top-level: mount SpanResource via useAui
74
- const aui = useAui({ resource: SpanResource({ spans }) });
110
+ const aui = useAui({ span: SpanResource({ spans }) });
75
111
 
76
112
  <AuiProvider value={aui}>
77
113
  {/* Render visible spans (flat-list output respecting collapsed state) */}
@@ -104,7 +140,7 @@ Resource that ingests raw span data and exposes a tree-aware reactive state to p
104
140
  SpanResource({ spans }: { spans: SpanData[] }): ClientOutput<"span">;
105
141
  ```
106
142
 
107
- Mount through `useAui({ resource: SpanResource({ spans }) })`.
143
+ Mount through `useAui({ span: SpanResource({ spans }) })`.
108
144
 
109
145
  The resource computes:
110
146
 
@@ -243,6 +279,56 @@ Convenience component that pairs `SpanByIndexProvider` with a render component:
243
279
 
244
280
  Useful when you want explicit control over which child indices to render (e.g. for virtualization).
245
281
 
282
+ ### SpanPrimitive.Timeline
283
+
284
+ A `<div>` that provides a timeline range to descendant `TimelineBar` primitives and exposes range CSS variables.
285
+
286
+ | Prop | Type | Default | Description |
287
+ | --- | --- | --- | --- |
288
+ | `timeRange` | `{ min: number; max: number }` | `span.timeRange` | Override the range used for positioning bars. |
289
+ | `paddingEnd` | `number` | `0` | Extends the max by this ratio of the range duration, useful for right-side breathing room. |
290
+
291
+ Exposes `data-span-timeline` and CSS variables:
292
+
293
+ | Variable | Description |
294
+ | --- | --- |
295
+ | `--span-timeline-min-ms` | Timeline start timestamp. |
296
+ | `--span-timeline-max-ms` | Timeline end timestamp after padding. |
297
+ | `--span-timeline-range-ms` | Timeline duration after padding. |
298
+
299
+ ### SpanPrimitive.TimelineBar
300
+
301
+ A `<div>` that positions the current span within the nearest `SpanPrimitive.Timeline` range. It reads `startedAt`, `endedAt`, `status`, `type`, and `timeRange` from the current span scope.
302
+
303
+ ```tsx
304
+ <SpanPrimitive.Timeline>
305
+ <SpanPrimitive.Children>
306
+ {() => (
307
+ <div className="relative h-8">
308
+ <SpanPrimitive.TimelineBar className="top-1 h-6 rounded bg-blue-500" />
309
+ </div>
310
+ )}
311
+ </SpanPrimitive.Children>
312
+ </SpanPrimitive.Timeline>
313
+ ```
314
+
315
+ Running spans use the timeline range max when `now` is omitted, keeping SSR and hydration deterministic. For live ticking, update a timestamp from an effect or animation loop and pass it as `now`.
316
+
317
+ | Prop | Type | Default | Description |
318
+ | --- | --- | --- | --- |
319
+ | `now` | `number` | `undefined` | Explicit timestamp used as the end of running spans. |
320
+ | `timeRange` | `{ min: number; max: number }` | nearest `Timeline` range | Override the range used for this bar. |
321
+
322
+ Exposes `data-span-status`, `data-span-type`, `data-span-running`, and these CSS variables:
323
+
324
+ | Variable | Description |
325
+ | --- | --- |
326
+ | `--span-timeline-left` | Bar start position as a percentage. |
327
+ | `--span-timeline-end` | Bar end position as a percentage. |
328
+ | `--span-timeline-width` | Bar width as a percentage. |
329
+ | `--span-timeline-duration-ms` | Bar duration in milliseconds. |
330
+ | `--span-timeline-min-width` | Optional consumer-set minimum width used by the default inline size. |
331
+
246
332
  ## Styling
247
333
 
248
334
  All primitives forward refs and accept standard DOM props (`className`, `style`, etc.). Use the data attributes for state-based styling:
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;cAca,MAAA,EAAM,SAGjB;AAAA,iBAeoB,SAAA,IAAS,OAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;cAca,MAAA,EAAM,SAGjB;AAAA,iBAuBoB,SAAA,IAAS,OAAA"}
package/dist/index.js CHANGED
@@ -12,8 +12,24 @@ const server = new McpServer({
12
12
  name: "assistant-ui-docs",
13
13
  version: packageJson.version
14
14
  });
15
- server.tool(docsTools.name, docsTools.description, docsTools.parameters, docsTools.execute);
16
- server.tool(examplesTools.name, examplesTools.description, examplesTools.parameters, examplesTools.execute);
15
+ server.registerTool(docsTools.name, {
16
+ title: "assistant-ui Documentation",
17
+ description: docsTools.description,
18
+ inputSchema: docsTools.parameters,
19
+ annotations: {
20
+ readOnlyHint: true,
21
+ openWorldHint: false
22
+ }
23
+ }, docsTools.execute);
24
+ server.registerTool(examplesTools.name, {
25
+ title: "assistant-ui Examples",
26
+ description: examplesTools.description,
27
+ inputSchema: examplesTools.parameters,
28
+ annotations: {
29
+ readOnlyHint: true,
30
+ openWorldHint: false
31
+ }
32
+ }, examplesTools.execute);
17
33
  async function runServer() {
18
34
  try {
19
35
  logger.info(`Starting assistant-ui MCP docs server v${packageJson.version}`);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { docsTools } from \"./tools/docs.js\";\nimport { examplesTools } from \"./tools/examples.js\";\nimport { logger } from \"./utils/logger.js\";\nimport { PACKAGE_DIR } from \"./constants.js\";\n\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst packageJson = JSON.parse(\n readFileSync(join(PACKAGE_DIR, \"package.json\"), \"utf-8\"),\n);\n\nexport const server = new McpServer({\n name: \"assistant-ui-docs\",\n version: packageJson.version,\n});\n\nserver.tool(\n docsTools.name,\n docsTools.description,\n docsTools.parameters,\n docsTools.execute,\n);\nserver.tool(\n examplesTools.name,\n examplesTools.description,\n examplesTools.parameters,\n examplesTools.execute,\n);\n\nexport async function runServer() {\n try {\n logger.info(\n `Starting assistant-ui MCP docs server v${packageJson.version}`,\n );\n const transport = new StdioServerTransport();\n await server.connect(transport);\n } catch (error) {\n logger.error(\"Failed to start MCP server\", error);\n process.exit(1);\n }\n}\n\nif (import.meta.url === `file://${process.argv[1]}`) {\n void runServer().catch((error) => {\n console.error(\"Failed to start server:\", error);\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;AAUA,MAAM,cAAc,KAAK,MACvB,aAAa,KAAK,aAAa,cAAc,GAAG,OAAO,CACzD;AAEA,MAAa,SAAS,IAAI,UAAU;CAClC,MAAM;CACN,SAAS,YAAY;AACvB,CAAC;AAED,OAAO,KACL,UAAU,MACV,UAAU,aACV,UAAU,YACV,UAAU,OACZ;AACA,OAAO,KACL,cAAc,MACd,cAAc,aACd,cAAc,YACd,cAAc,OAChB;AAEA,eAAsB,YAAY;CAChC,IAAI;EACF,OAAO,KACL,0CAA0C,YAAY,SACxD;EACA,MAAM,YAAY,IAAI,qBAAqB;EAC3C,MAAM,OAAO,QAAQ,SAAS;CAChC,SAAS,OAAO;EACd,OAAO,MAAM,8BAA8B,KAAK;EAChD,QAAQ,KAAK,CAAC;CAChB;AACF;AAEA,IAAI,OAAO,KAAK,QAAQ,UAAU,QAAQ,KAAK,MAC7C,UAAe,CAAC,CAAC,OAAO,UAAU;CAChC,QAAQ,MAAM,2BAA2B,KAAK;CAC9C,QAAQ,KAAK,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { docsTools } from \"./tools/docs.js\";\nimport { examplesTools } from \"./tools/examples.js\";\nimport { logger } from \"./utils/logger.js\";\nimport { PACKAGE_DIR } from \"./constants.js\";\n\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst packageJson = JSON.parse(\n readFileSync(join(PACKAGE_DIR, \"package.json\"), \"utf-8\"),\n);\n\nexport const server = new McpServer({\n name: \"assistant-ui-docs\",\n version: packageJson.version,\n});\n\nserver.registerTool(\n docsTools.name,\n {\n title: \"assistant-ui Documentation\",\n description: docsTools.description,\n inputSchema: docsTools.parameters,\n annotations: { readOnlyHint: true, openWorldHint: false },\n },\n docsTools.execute,\n);\nserver.registerTool(\n examplesTools.name,\n {\n title: \"assistant-ui Examples\",\n description: examplesTools.description,\n inputSchema: examplesTools.parameters,\n annotations: { readOnlyHint: true, openWorldHint: false },\n },\n examplesTools.execute,\n);\n\nexport async function runServer() {\n try {\n logger.info(\n `Starting assistant-ui MCP docs server v${packageJson.version}`,\n );\n const transport = new StdioServerTransport();\n await server.connect(transport);\n } catch (error) {\n logger.error(\"Failed to start MCP server\", error);\n process.exit(1);\n }\n}\n\nif (import.meta.url === `file://${process.argv[1]}`) {\n void runServer().catch((error) => {\n console.error(\"Failed to start server:\", error);\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;AAUA,MAAM,cAAc,KAAK,MACvB,aAAa,KAAK,aAAa,cAAc,GAAG,OAAO,CACzD;AAEA,MAAa,SAAS,IAAI,UAAU;CAClC,MAAM;CACN,SAAS,YAAY;AACvB,CAAC;AAED,OAAO,aACL,UAAU,MACV;CACE,OAAO;CACP,aAAa,UAAU;CACvB,aAAa,UAAU;CACvB,aAAa;EAAE,cAAc;EAAM,eAAe;CAAM;AAC1D,GACA,UAAU,OACZ;AACA,OAAO,aACL,cAAc,MACd;CACE,OAAO;CACP,aAAa,cAAc;CAC3B,aAAa,cAAc;CAC3B,aAAa;EAAE,cAAc;EAAM,eAAe;CAAM;AAC1D,GACA,cAAc,OAChB;AAEA,eAAsB,YAAY;CAChC,IAAI;EACF,OAAO,KACL,0CAA0C,YAAY,SACxD;EACA,MAAM,YAAY,IAAI,qBAAqB;EAC3C,MAAM,OAAO,QAAQ,SAAS;CAChC,SAAS,OAAO;EACd,OAAO,MAAM,8BAA8B,KAAK;EAChD,QAAQ,KAAK,CAAC;CAChB;AACF;AAEA,IAAI,OAAO,KAAK,QAAQ,UAAU,QAAQ,KAAK,MAC7C,UAAe,CAAC,CAAC,OAAO,UAAU;CAChC,QAAQ,MAAM,2BAA2B,KAAK;CAC9C,QAAQ,KAAK,CAAC;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/mcp-docs-server",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "description": "MCP server for assistant-ui documentation and examples",
5
5
  "keywords": [
6
6
  "mcp",
@@ -37,10 +37,10 @@
37
37
  "zod": "^4.4.3"
38
38
  },
39
39
  "devDependencies": {
40
- "@types/node": "^25.9.2",
40
+ "@types/node": "^26.0.0",
41
41
  "tsx": "^4.22.4",
42
- "vitest": "^4.1.8",
43
- "@assistant-ui/x-buildutils": "0.0.12"
42
+ "vitest": "^4.1.9",
43
+ "@assistant-ui/x-buildutils": "0.0.16"
44
44
  },
45
45
  "publishConfig": {
46
46
  "access": "public",
package/src/index.ts CHANGED
@@ -17,16 +17,24 @@ export const server = new McpServer({
17
17
  version: packageJson.version,
18
18
  });
19
19
 
20
- server.tool(
20
+ server.registerTool(
21
21
  docsTools.name,
22
- docsTools.description,
23
- docsTools.parameters,
22
+ {
23
+ title: "assistant-ui Documentation",
24
+ description: docsTools.description,
25
+ inputSchema: docsTools.parameters,
26
+ annotations: { readOnlyHint: true, openWorldHint: false },
27
+ },
24
28
  docsTools.execute,
25
29
  );
26
- server.tool(
30
+ server.registerTool(
27
31
  examplesTools.name,
28
- examplesTools.description,
29
- examplesTools.parameters,
32
+ {
33
+ title: "assistant-ui Examples",
34
+ description: examplesTools.description,
35
+ inputSchema: examplesTools.parameters,
36
+ annotations: { readOnlyHint: true, openWorldHint: false },
37
+ },
30
38
  examplesTools.execute,
31
39
  );
32
40
 
@@ -73,6 +73,15 @@ describe("MCP Protocol Integration", () => {
73
73
  expect(examplesTool.inputSchema).toBeDefined();
74
74
  expect(examplesTool.inputSchema.type).toBe("object");
75
75
  expect(examplesTool.inputSchema.properties).toBeDefined();
76
+
77
+ // registerTool metadata is surfaced on tools/list: `title` is a
78
+ // top-level field, `annotations` carries only the hint flags.
79
+ expect(docsTool.title).toBe("assistant-ui Documentation");
80
+ expect(docsTool.annotations?.readOnlyHint).toBe(true);
81
+ expect(docsTool.annotations?.openWorldHint).toBe(false);
82
+ expect(examplesTool.title).toBe("assistant-ui Examples");
83
+ expect(examplesTool.annotations?.readOnlyHint).toBe(true);
84
+ expect(examplesTool.annotations?.openWorldHint).toBe(false);
76
85
  });
77
86
 
78
87
  it("should handle CallTool request for assistantUIDocs", async () => {