---
title: DirectChatTransport
description: API Reference for the DirectChatTransport class.
---

# `DirectChatTransport`

A transport that directly communicates with an [Agent](/docs/reference/ai-sdk-core/agent) in-process, without going through HTTP. This is useful for:

- Server-side rendering scenarios
- Testing without network
- Single-process applications

Unlike `DefaultChatTransport` which sends HTTP requests to an API endpoint, `DirectChatTransport` invokes the agent's `stream()` method directly and converts the result to a UI message stream.

```tsx
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent } from 'ai';
__PROVIDER_IMPORT__;

const agent = new ToolLoopAgent({
  model: __MODEL__,
  instructions: 'You are a helpful assistant.',
});

export default function Chat() {
  const { messages, sendMessage, status } = useChat({
    transport: new DirectChatTransport({ agent }),
  });

  // ... render chat UI
}
```

## Import

<Snippet text={`import { DirectChatTransport } from "ai"`} prompt={false} />

## Constructor

### Parameters

<PropertiesTable
  content={[
    {
      name: 'agent',
      type: 'Agent',
      isRequired: true,
      description:
        'The Agent instance to use for generating responses. The agent will be called with `stream()` for each message.',
    },
    {
      name: 'options',
      type: 'CALL_OPTIONS',
      isOptional: true,
      description:
        'Options to pass to the agent when calling it. These are agent-specific options defined when creating the agent.',
    },
    {
      name: 'originalMessages',
      type: 'UIMessage[]',
      isOptional: true,
      description:
        'The original messages. If provided, persistence mode is assumed, and a message ID is provided for the response message.',
    },
    {
      name: 'generateMessageId',
      type: 'IdGenerator',
      isOptional: true,
      description:
        'Generate a message ID for the response message. If not provided, no message ID will be set for the response message.',
    },
    {
      name: 'messageMetadata',
      type: '(options: { part: TextStreamPart }) => METADATA | undefined',
      isOptional: true,
      description:
        'Extracts message metadata that will be sent to the client. Called on `start` and `finish` events.',
    },
    {
      name: 'sendReasoning',
      type: 'boolean',
      isOptional: true,
      description: 'Send reasoning parts to the client. Defaults to true.',
    },
    {
      name: 'sendSources',
      type: 'boolean',
      isOptional: true,
      description: 'Send source parts to the client. Defaults to false.',
    },
    {
      name: 'sendFinish',
      type: 'boolean',
      isOptional: true,
      description:
        'Send the finish event to the client. Set to false if you are using additional streamText calls that send additional data. Defaults to true.',
    },
    {
      name: 'sendStart',
      type: 'boolean',
      isOptional: true,
      description:
        'Send the message start event to the client. Set to false if you are using additional streamText calls and the message start event has already been sent. Defaults to true.',
    },
    {
      name: 'onError',
      type: '(error: unknown) => string',
      isOptional: true,
      description:
        "Process an error, e.g. to log it. Defaults to `() => 'An error occurred.'`. Return the error message to include in the data stream.",
    },
  ]}
/>

## Methods

### `sendMessages()`

Sends messages to the agent and returns a streaming response. This method validates and converts UI messages to model messages, calls the agent's `stream()` method, and returns the result as a UI message stream.

```ts
const stream = await transport.sendMessages({
  chatId: 'chat-123',
  trigger: 'submit-message',
  messages: [...],
  abortSignal: controller.signal,
});
```

<PropertiesTable
  content={[
    {
      name: 'chatId',
      type: 'string',
      description: 'Unique identifier for the chat session.',
    },
    {
      name: 'trigger',
      type: "'submit-message' | 'regenerate-message'",
      description:
        'The type of message submission - either new message or regeneration.',
    },
    {
      name: 'messageId',
      type: 'string | undefined',
      description:
        'ID of the message to regenerate, or undefined for new messages.',
    },
    {
      name: 'messages',
      type: 'UIMessage[]',
      description:
        'Array of UI messages representing the conversation history.',
    },
    {
      name: 'abortSignal',
      type: 'AbortSignal | undefined',
      description: 'Signal to abort the request if needed.',
    },
    {
      name: 'headers',
      type: 'Record<string, string> | Headers',
      isOptional: true,
      description: 'Additional headers (ignored by DirectChatTransport).',
    },
    {
      name: 'body',
      type: 'object',
      isOptional: true,
      description:
        'Additional body properties (ignored by DirectChatTransport).',
    },
    {
      name: 'metadata',
      type: 'unknown',
      isOptional: true,
      description: 'Custom metadata (ignored by DirectChatTransport).',
    },
  ]}
/>

#### Returns

Returns a `Promise<ReadableStream<UIMessageChunk>>` - a stream of UI message chunks that can be processed by the chat UI.

### `reconnectToStream()`

Direct transport does not support reconnection since there is no persistent server-side stream to reconnect to.

#### Returns

Always returns `Promise<null>`.

## Examples

### Basic Usage

```tsx
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent } from 'ai';
import { openai } from '@ai-sdk/openai';

const agent = new ToolLoopAgent({
  model: openai('gpt-4o'),
  instructions: 'You are a helpful assistant.',
});

export default function Chat() {
  const { messages, sendMessage, status } = useChat({
    transport: new DirectChatTransport({ agent }),
  });

  return (
    <div>
      {messages.map(message => (
        <div key={message.id}>
          {message.role === 'user' ? 'User: ' : 'AI: '}
          {message.parts.map((part, index) =>
            part.type === 'text' ? <span key={index}>{part.text}</span> : null,
          )}
        </div>
      ))}
      <button onClick={() => sendMessage({ text: 'Hello!' })}>Send</button>
    </div>
  );
}
```

### With Agent Tools

```tsx
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent, tool } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';

const weatherTool = tool({
  description: 'Get the current weather',
  parameters: z.object({
    location: z.string().describe('The city and state'),
  }),
  execute: async ({ location }) => {
    return `The weather in ${location} is sunny and 72°F.`;
  },
});

const agent = new ToolLoopAgent({
  model: openai('gpt-4o'),
  instructions: 'You are a helpful assistant with access to weather data.',
  tools: { weather: weatherTool },
});

export default function Chat() {
  const { messages, sendMessage } = useChat({
    transport: new DirectChatTransport({ agent }),
  });

  // ... render chat UI with tool results
}
```

### With Custom Agent Options

```tsx
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent } from 'ai';
import { openai } from '@ai-sdk/openai';

const agent = new ToolLoopAgent<{ userId: string }>({
  model: openai('gpt-4o'),
  prepareCall: ({ options, ...rest }) => ({
    ...rest,
    providerOptions: {
      openai: { user: options.userId },
    },
  }),
});

export default function Chat({ userId }: { userId: string }) {
  const { messages, sendMessage } = useChat({
    transport: new DirectChatTransport({
      agent,
      options: { userId },
    }),
  });

  // ... render chat UI
}
```

### With Reasoning

```tsx
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent } from 'ai';
import { openai } from '@ai-sdk/openai';

const agent = new ToolLoopAgent({
  model: openai('o1-preview'),
});

export default function Chat() {
  const { messages, sendMessage } = useChat({
    transport: new DirectChatTransport({
      agent,
      sendReasoning: true,
    }),
  });

  return (
    <div>
      {messages.map(message => (
        <div key={message.id}>
          {message.parts.map((part, index) => {
            if (part.type === 'text') {
              return <p key={index}>{part.text}</p>;
            }
            if (part.type === 'reasoning') {
              return (
                <pre key={index} style={{ opacity: 0.6 }}>
                  {part.text}
                </pre>
              );
            }
            return null;
          })}
        </div>
      ))}
    </div>
  );
}
```
