- MCP Server ToolsUpdated
- Managed MCP with chat()Updated
- Manual MCPUpdated
- MCP Type GenerationUpdated
- MCP Client InputUpdated
- MCP Server Tools: AdvancedNew
- MCP EventsNew
- MCP AppsUpdated
- Serve Tools over HTTPUpdated
- Serve Resources and PromptsUpdated
- Ask for InputUpdated
- Draft Text from a ToolUpdated
- MCP Server TasksUpdated
- MCP Server AuthUpdated
- MCP Server SessionsUpdated
- MCP Server on stdioUpdated
Persistence (Advanced)
- GenerationsUpdated
- Realtime Voice ChatUpdated
- Text-to-SpeechUpdated
- TranscriptionUpdated
- Transcription: AdvancedNew
- Voice CreationUpdated
- Audio RecordingUpdated
- Audio GenerationUpdated
- Image GenerationUpdated
- Image Generation: AdvancedNew
- Video GenerationUpdated
- Video Generation: AdvancedNew
- Live GenerationUpdated
- World GenerationUpdated
- Generation HooksUpdated
- OverviewUpdated
- Quick StartUpdated
- ProvidersUpdated
- HarnessesUpdated
- Harness AuthUpdated
- WorkspaceUpdated
- ToolsUpdated
- PolicyUpdated
- Lifecycle & SnapshotsUpdated
- Portable SnapshotsUpdated
- Keep Files After ReloadUpdated
- Save a Named VersionUpdated
- Branch From a VersionUpdated
- Send a Frozen FileUpdated
- Let the Agent Save and ForkUpdated
- Pick Which Files to KeepUpdated
- What a Snapshot StoresUpdated
- Instance DurabilityUpdated
- Durable RunsUpdated
- EventsUpdated
Sandboxes (Advanced)
- Runtime ContextUpdated
- Bring Your Own KeyUpdated
- Debug LoggingUpdated
- Multimodal ContentUpdated
- Files APIUpdated
- Per-Model Type SafetyUpdated
- Runtime Adapter SwitchingUpdated
- Tree-ShakingUpdated
- Extend AdapterUpdated
- Extend Adapter: AdvancedNew
- Typed Pre-Configured OptionsUpdated
- Test with a Fake ModelUpdated
- Prompt CachingUpdated
- Mid-Conversation ChangesUpdated
- Approval Flow ProcessingUpdated
Ask the user your own question
Use defineInterrupt when the question is not about approving a tool. The server pauses the run and asks. Your component answers.
Two schemas define the exchange:
- payloadSchema is what the server sends you.
- responseSchema is what you send back.
Define it#
import { defineInterrupt } from '@tanstack/ai'
import { z } from 'zod'
export const choosePlan = defineInterrupt({
id: 'choosePlan',
payloadSchema: z.object({
options: z.array(z.string()),
}),
responseSchema: z.string(),
})The server sends a list of plan names. You send one name back.
Render it#
Add the interrupt to chatOptions, then register a component under interruptsComponents.generic. The key is the interrupt id.
import { fetchServerSentEvents } from '@tanstack/ai-react'
import { createChatHook } from '@tanstack/ai-react/ui'
const chatOptions = {
connection: fetchServerSentEvents('/api/chat'),
interrupts: [choosePlan],
}
const { useAppChat } = createChatHook({
options: chatOptions,
components: {
layout: ({ Messages, Interrupts }) => (
<main>
<Messages />
<Interrupts />
</main>
),
message: ({ Parts }) => <article><Parts /></article>,
},
partsComponents: {
text: ({ part }) => <p>{part.content}</p>,
fallback: () => null,
},
interruptsComponents: {
generic: {
choosePlan: ({ interrupt }) => (
<fieldset>
<legend>Pick a plan</legend>
{interrupt.payload?.options.map((plan) => (
<button key={plan} onClick={() => interrupt.resolveInterrupt(plan)}>
{plan}
</button>
))}
</fieldset>
),
fallback: ({ interrupt }) => <p>{interrupt.reason}</p>,
},
},
})
export function PlanChat() {
const chat = useAppChat()
return <chat.AppChat />
}interrupt.payload matches your payloadSchema, so options is a string array. resolveInterrupt accepts only what your responseSchema allows, so passing a number here is a compile error.
Generic interrupts always render in the list#
A tool approval can render next to its tool. A generic interrupt cannot, because no tool owns it. Render Interrupts in your layout or it never appears.
Always register a fallback#
fallback catches two things:
- An interrupt id you did not register.
- An unbound interrupt, which arrives with no id at all.
Branch on interrupt.kind === 'unbound' when the copy must differ.
Next#
- Approving a tool instead? See tool approval.
- More interrupt shapes and the protocol behind them: generic interrupts.