Skip to content

AI SDK UI (useChat) ​

Runcell speaks the AI SDK UI Message Stream protocol, so useChat consumes a runcell agent exactly as it consumes a streamText backend. No adapter is needed.

Backend ​

ts
// app/api/chat/route.ts
import { createAgent } from 'runcell';

const agent = createAgent({ model: 'anthropic/claude-sonnet-4-5' });

export async function POST(req: Request): Promise<Response> {
  const { messages } = await req.json();
  return agent
    .stream({ messages, signal: req.signal })
    .toUIMessageStreamResponse();
}

Frontend ​

tsx
import { useChat } from '@ai-sdk/react';

export function Chat() {
  const { messages, sendMessage } = useChat(); // posts to /api/chat by default
  return (
    <>
      {messages.map(message => (
        <div key={message.id}>
          {message.parts.map((part, i) =>
            part.type === 'text' ? <span key={i}>{part.text}</span> : null,
          )}
        </div>
      ))}
    </>
  );
}

Messages carry the full part structure: text, reasoning, and tool-* parts with their input, output, and state. message.metadata carries the run's usage (token counts, costUsd, costMeasured) and sessionId from the finish chunk.

Controlling what crosses the wire ​

toUIMessageStreamResponse accepts the same option names as the AI SDK's, plus tool redaction:

ts
return agent.stream({ messages }).toUIMessageStreamResponse({
  sendReasoning: false, // keep thinking server-side
  sendTools: 'names-only', // tool names without payloads
  onError: error => 'Something went wrong.', // masked by default
});

See wire-level controls for the full option reference, and threads for durable server-side conversation state as an alternative to replaying messages.