Partial tool results and loading states

Every tool call goes through three states: call (the model emitted the call), result (the execute function returned), and error (something threw). The SDK streams each state as a separate update. Your UI can show "Calling web_search..." the instant the call starts, then fill in the result when it arrives.

components/chat/tool-call-card.tsx
tsx
import { Wrench, Loader2, Check, AlertTriangle } from 'lucide-react';

export function ToolCallCard({ toolName, args, result, state }: ToolCallCardProps) {
  const Icon =
    state === 'result' ? Check : state === 'error' ? AlertTriangle : Loader2;
  const spinning = state !== 'result' && state !== 'error';

  return (
    <div className="my-2 rounded-md border border-emerald-200 bg-emerald-50 p-3 text-xs">
      <div className="flex items-center gap-2 font-medium text-emerald-900">
        <Icon className={'h-3.5 w-3.5 ' + (spinning ? 'animate-spin' : '')} />
        <span>
          {state === 'result' ? 'Tool result: ' : 'Calling '}
          {toolName}
        </span>
      </div>
      {args !== undefined && (
        <pre className="mt-2 overflow-x-auto rounded bg-white p-2">
          {JSON.stringify(args, null, 2)}
        </pre>
      )}
      {state === 'result' && result !== undefined && (
        <pre className="mt-2 overflow-x-auto rounded bg-white p-2">
          {JSON.stringify(result, null, 2)}
        </pre>
      )}
    </div>
  );
}

One card, three looks. Spinning while the tool runs, a check when it completes, a warning on error. The arguments render immediately so users see what was called even before the result arrives.

Quiz: Quiz

Loading practice…