Skip to content
+

Chat - Tool approval and renderers

Approve agent tool calls and render custom message parts through the part renderer registry.

This page covers the main extension points for tool-assisted AI interactions:

  • the tool approval lifecycle from approval-requested to response
  • addToolApprovalResponse() for approving or denying tool calls
  • follow-up assistant messages via message-added events after tool resolution
  • partRenderers for registering custom part renderers on ChatProvider
  • useChatPartRenderer() for looking up renderers in components
  • custom part registration through type augmentation

Key concepts

Tool approval flow

When the stream sends a tool-approval-request chunk, the tool invocation moves to state: 'approval-requested'. The UI renders an approve/deny interface. When the user responds, call addToolApprovalResponse():

const { addToolApprovalResponse } = useChat();

// Approve
await addToolApprovalResponse({
  id: toolCall.toolCallId,
  approved: true,
});

// Deny with reason
await addToolApprovalResponse({
  id: toolCall.toolCallId,
  approved: false,
  reason: 'User declined the operation',
});

After responding, the tool invocation moves to state: 'approval-responded', and the stream continues.

Registering custom renderers

Register renderers for specific part types on ChatProvider:

const renderers: ChatPartRendererMap = {
  tool: ({ part, message, index }) => <ToolCard invocation={part.toolInvocation} />,
  'custom-widget': ({ part }) => <Widget data={part.data} />,
};

<ChatProvider adapter={adapter} partRenderers={renderers}>
  <MyChat />
</ChatProvider>;

Looking up renderers

Use useChatPartRenderer(partType) inside any component to get the registered renderer:

function MessagePart({ part, message, index }) {
  const renderer = useChatPartRenderer(part.type);

  if (renderer) {
    return renderer({ part, message, index });
  }

  // Fallback for unregistered types
  return <span>{part.type === 'text' ? part.text : null}</span>;
}

See Type augmentation for details on TypeScript module augmentation.

The demo below combines the approval flow and a custom part renderer:

Tool approval and custom renderers

Approve or deny the tool call, then render the custom poll part through the registry.

Press Enter to start editing

Key takeaways

  • Tool approval is a first-class runtime feature—the stream pauses at approval-requested until you respond.
  • addToolApprovalResponse() drives the approval/denial decision
  • After tool resolution, the adapter can emit a message-added event to deliver the assistant's follow-up interpretation of the result
  • partRenderers decouples rendering from the message loop—register once, look up anywhere.
  • Custom part types registered through module augmentation work with the renderer registry the same as built-in types.

See also

  • See Type augmentation for details on registering custom types.
  • See Streaming for details on the tool chunk protocol.
  • See Tool call events for details on the onToolCall() callback pattern.
  • See Hooks for the useChatPartRenderer() API reference.

API