# Google ADK
URL: /docs/runtimes/google-adk
Connect to Google ADK (Agent Development Kit) agents with streaming, tool calls, and multi-agent support.
The `@assistant-ui/react-google-adk` package provides integration with [Google ADK JS](https://github.com/google/adk-js), Google's official agent framework for TypeScript. It supports streaming text, tool calls, multi-agent orchestration, code execution, session state, tool confirmations, auth flows, and more.
Requirements \[#requirements]
You need a Google ADK agent running on a server. ADK supports `LlmAgent` with Gemini models, tool use, multi-agent orchestration (sequential, parallel, loop agents), and session management.
Installation \[#installation]
`@google/adk` is only needed on the server side. The client-side runtime has no dependency on it.
Getting Started \[#getting-started]
Create a backend API endpoint \[#create-a-backend-api-endpoint]
Use `createAdkApiRoute` to create an API route in one line:
```typescript title="app/api/chat/route.ts"
import { createAdkApiRoute } from "@assistant-ui/react-google-adk/server";
import { InMemoryRunner, LlmAgent } from "@google/adk";
const agent = new LlmAgent({
name: "my_agent",
model: "gemini-2.5-flash",
instruction: "You are a helpful assistant.",
});
const runner = new InMemoryRunner({ agent, appName: "my-app" });
export const POST = createAdkApiRoute({
runner,
userId: "user_1",
sessionId: (req) =>
new URL(req.url).searchParams.get("sessionId") ?? "default",
});
```
Set up the client runtime \[#set-up-the-client-runtime]
Use `createAdkStream` to connect to your API route — no manual SSE parsing needed:
```tsx title="components/MyAssistant.tsx"
"use client";
import { AssistantRuntimeProvider } from "@assistant-ui/react";
import {
useAdkRuntime,
createAdkStream,
} from "@assistant-ui/react-google-adk";
import { Thread } from "@/components/assistant-ui/thread";
export function MyAssistant() {
const runtime = useAdkRuntime({
stream: createAdkStream({ api: "/api/chat" }),
});
return (
);
}
```
Use the component \[#use-the-component]
```tsx title="app/page.tsx"
import { MyAssistant } from "@/components/MyAssistant";
export default function Home() {
return (
);
}
```
Setup UI components \[#setup-ui-components]
Follow the [UI Components](/docs/ui/thread) guide to setup the Thread and other UI components.
`createAdkStream` \[#createadkstream]
Creates an `AdkStreamCallback` that connects to an ADK endpoint via SSE. Supports two modes:
**Proxy mode** — POST to your own API route:
```typescript
import { createAdkStream } from "@assistant-ui/react-google-adk";
const stream = createAdkStream({ api: "/api/chat" });
```
**Direct mode** — connect directly to an ADK server:
```typescript
const stream = createAdkStream({
api: "http://localhost:8000",
appName: "my-app",
userId: "user-1",
});
```
| Option | Type | Description |
| --------- | --------------------------------------- | --------------------------------------------------- |
| `api` | `string` | URL to POST to (proxy route or ADK server base URL) |
| `appName` | `string?` | ADK app name (enables direct mode when set) |
| `userId` | `string?` | ADK user ID (required with `appName`) |
| `headers` | `Record \| (() => ...)` | Static or dynamic request headers |
Direct ADK Server Connection \[#direct-adk-server-connection]
When connecting directly to an ADK server (without a proxy API route), use `createAdkSessionAdapter` to back your thread list with ADK sessions:
```tsx
import {
useAdkRuntime,
createAdkStream,
createAdkSessionAdapter,
} from "@assistant-ui/react-google-adk";
const ADK_URL = "http://localhost:8000";
const { adapter, load, artifacts } = createAdkSessionAdapter({
apiUrl: ADK_URL,
appName: "my-app",
userId: "user-1",
});
const runtime = useAdkRuntime({
stream: createAdkStream({
api: ADK_URL,
appName: "my-app",
userId: "user-1",
}),
sessionAdapter: adapter,
load,
});
```
The session adapter maps ADK sessions to assistant-ui threads:
* **`adapter`** — a `RemoteThreadListAdapter` that uses ADK's session REST API for thread CRUD
* **`load`** — reconstructs messages from session events via `AdkEventAccumulator`
* **`artifacts`** — functions to fetch, list, and delete session artifacts (see [Artifact Fetching](#artifact-fetching))
| Option | Type | Description |
| --------- | --------------------------------------- | --------------------------------- |
| `apiUrl` | `string` | ADK server base URL |
| `appName` | `string` | ADK app name |
| `userId` | `string` | ADK user ID |
| `headers` | `Record \| (() => ...)` | Static or dynamic request headers |
Server Helpers \[#server-helpers]
`createAdkApiRoute` \[#createadkapiroute]
One-liner API route handler that combines request parsing and SSE streaming:
```typescript
import { createAdkApiRoute } from "@assistant-ui/react-google-adk/server";
export const POST = createAdkApiRoute({
runner,
userId: "default-user",
sessionId: (req) =>
new URL(req.url).searchParams.get("sessionId") ?? "default",
});
```
Both `userId` and `sessionId` accept a static string or a function `(req: Request) => string` for dynamic resolution (e.g. from cookies, headers, or query params).
`adkEventStream` \[#adkeventstream]
Converts an `AsyncGenerator` from ADK's `Runner.runAsync()` into an SSE `Response`. Sends an initial `:ok` comment to keep connections alive through proxies.
```typescript
import { adkEventStream } from "@assistant-ui/react-google-adk/server";
const events = runner.runAsync({ userId, sessionId, newMessage });
return adkEventStream(events);
```
`parseAdkRequest` / `toAdkContent` \[#parseadkrequest--toadkcontent]
Lower-level helpers for custom API routes. Parse incoming requests and convert to ADK's `Content` format. Supports user messages, tool results, `stateDelta`, `checkpointId`, and multimodal content:
```typescript
import { parseAdkRequest, toAdkContent } from "@assistant-ui/react-google-adk/server";
const parsed = await parseAdkRequest(req);
// parsed.type is "message" or "tool-result"
// parsed.config contains runConfig, checkpointId
// parsed.stateDelta contains session state changes
const newMessage = toAdkContent(parsed);
const events = runner.runAsync({
userId,
sessionId,
newMessage,
stateDelta: parsed.stateDelta,
});
return adkEventStream(events);
```
Hooks \[#hooks]
Agent & Session State \[#agent--session-state]
```typescript
import {
useAdkAgentInfo,
useAdkSessionState,
useAdkSend,
} from "@assistant-ui/react-google-adk";
function MyComponent() {
// Current active agent name and branch path (multi-agent)
const agentInfo = useAdkAgentInfo();
// agentInfo?.name = "search_agent"
// agentInfo?.branch = "root.search_agent"
// Accumulated session state delta
const state = useAdkSessionState();
// Send raw ADK messages programmatically
const send = useAdkSend();
}
```
Tool Confirmations \[#tool-confirmations]
When ADK's `SecurityPlugin` or tool callbacks request user confirmation before executing a tool, use `useAdkToolConfirmations` to read pending requests and `useAdkConfirmTool` to respond:
```typescript
import {
useAdkToolConfirmations,
useAdkConfirmTool,
} from "@assistant-ui/react-google-adk";
function ToolConfirmationUI() {
const confirmations = useAdkToolConfirmations();
const confirmTool = useAdkConfirmTool();
if (confirmations.length === 0) return null;
return confirmations.map((conf) => (
Tool "{conf.toolName}" wants to run. {conf.hint}
));
}
```
Auth Requests \[#auth-requests]
When a tool requires OAuth or other authentication, use `useAdkAuthRequests` to read pending requests and `useAdkSubmitAuth` to submit credentials:
```typescript
import {
useAdkAuthRequests,
useAdkSubmitAuth,
type AdkAuthCredential,
} from "@assistant-ui/react-google-adk";
function AuthUI() {
const authRequests = useAdkAuthRequests();
const submitAuth = useAdkSubmitAuth();
if (authRequests.length === 0) return null;
return authRequests.map((req) => (
));
}
```
`AdkAuthCredential` supports all ADK auth types: `apiKey`, `http`, `oauth2`, `openIdConnect`, `serviceAccount`.
Artifacts \[#artifacts]
Track file artifacts created or modified by the agent:
```typescript
import { useAdkArtifacts } from "@assistant-ui/react-google-adk";
function ArtifactList() {
const artifacts = useAdkArtifacts();
// Record — filename to version number
}
```
Artifact Fetching \[#artifact-fetching]
When using `createAdkSessionAdapter`, the returned `artifacts` object provides functions to fetch artifact content from the ADK server:
```typescript
const { artifacts } = createAdkSessionAdapter({ apiUrl, appName, userId });
// List all artifact filenames in a session
const filenames = await artifacts.list(sessionId);
// Load artifact content (latest version)
const data = await artifacts.load(sessionId, "document.pdf");
// data.inlineData?.data — base64 content
// data.inlineData?.mimeType — MIME type
// data.text — text content (if text artifact)
// Load a specific version
const v1 = await artifacts.load(sessionId, "document.pdf", 1);
// List all versions
const versions = await artifacts.listVersions(sessionId, "document.pdf");
// Delete an artifact
await artifacts.delete(sessionId, "document.pdf");
```
Escalation \[#escalation]
Detect when an agent requests escalation to a human operator:
```typescript
import { useAdkEscalation } from "@assistant-ui/react-google-adk";
function EscalationBanner() {
const escalated = useAdkEscalation();
if (!escalated) return null;
return
Agent has requested human assistance.
;
}
```
Long-Running Tools \[#long-running-tools]
Track tools that are executing asynchronously and awaiting external input:
```typescript
import { useAdkLongRunningToolIds } from "@assistant-ui/react-google-adk";
function PendingToolsIndicator() {
const pendingToolIds = useAdkLongRunningToolIds();
if (pendingToolIds.length === 0) return null;
return
{pendingToolIds.length} tool(s) awaiting input
;
}
```
Per-Message Metadata \[#per-message-metadata]
Access grounding, citation, and token usage metadata per message:
```typescript
import { useAdkMessageMetadata } from "@assistant-ui/react-google-adk";
function MessageMetadata({ messageId }: { messageId: string }) {
const metadataMap = useAdkMessageMetadata();
const meta = metadataMap.get(messageId);
// meta?.groundingMetadata — Google Search grounding sources
// meta?.citationMetadata — citation references
// meta?.usageMetadata — token counts
}
```
Session State by Scope \[#session-state-by-scope]
ADK uses key prefixes to scope state. These helpers filter and strip the prefix:
```typescript
import {
useAdkAppState,
useAdkUserState,
useAdkTempState,
} from "@assistant-ui/react-google-adk";
function StateDebug() {
const appState = useAdkAppState(); // app:* keys (app-level, shared)
const userState = useAdkUserState(); // user:* keys (user-level)
const tempState = useAdkTempState(); // temp:* keys (not persisted)
}
```
Use `useAdkSessionState()` for the full unfiltered state delta.
Structured Events \[#structured-events]
Convert raw ADK events into typed, structured events for custom renderers:
```typescript
import {
toAdkStructuredEvents,
AdkEventType,
type AdkStructuredEvent,
} from "@assistant-ui/react-google-adk";
const structured = toAdkStructuredEvents(event);
for (const e of structured) {
switch (e.type) {
case AdkEventType.CONTENT:
console.log("Text:", e.content);
break;
case AdkEventType.THOUGHT:
console.log("Reasoning:", e.content);
break;
case AdkEventType.TOOL_CALL:
console.log("Tool:", e.call.name, e.call.args);
break;
case AdkEventType.ERROR:
console.error(e.errorMessage);
break;
}
}
```
State Delta \[#state-delta]
Send session state mutations along with messages using `stateDelta`:
```typescript
const send = useAdkSend();
// Pre-populate session state before the agent runs
send(
[{ id: "1", type: "human", content: "Start task" }],
{ stateDelta: { taskId: "abc", mode: "verbose" } },
);
```
This maps to ADK's `stateDelta` parameter on `/run_sse`.
RunConfig \[#runconfig]
Pass `AdkRunConfig` to control agent behavior:
```typescript
const send = useAdkSend();
send(messages, {
runConfig: {
streamingMode: "sse",
maxLlmCalls: 10,
pauseOnToolCalls: true, // pause for client-side tool execution
},
});
```
Event Handlers \[#event-handlers]
Listen to streaming events:
```typescript
const runtime = useAdkRuntime({
stream: createAdkStream({ api: "/api/chat" }),
eventHandlers: {
onError: (error) => {
console.error("Stream error:", error);
},
onAgentTransfer: (toAgent) => {
console.log("Agent transferred to:", toAgent);
},
onCustomEvent: (key, value) => {
// Fired for each entry in event.customMetadata
console.log("Custom metadata:", key, value);
},
},
});
```
Thread Management \[#thread-management]
ADK Session Adapter \[#adk-session-adapter]
Use `createAdkSessionAdapter` to persist threads via ADK's session API (see [Direct ADK Server Connection](#direct-adk-server-connection) above).
Custom Thread Management \[#custom-thread-management]
```typescript
const runtime = useAdkRuntime({
stream: createAdkStream({ api: "/api/chat" }),
create: async () => {
const sessionId = await createSession();
return { externalId: sessionId };
},
load: async (externalId) => {
const history = await loadSession(externalId);
return { messages: history };
},
delete: async (externalId) => {
await deleteSession(externalId);
},
});
```
Cloud Persistence \[#cloud-persistence]
For persistent thread history via assistant-cloud:
```typescript
import { AssistantCloud } from "assistant-cloud";
const runtime = useAdkRuntime({
cloud: new AssistantCloud({
baseUrl: process.env.NEXT_PUBLIC_ASSISTANT_BASE_URL,
anonymous: true,
}),
stream: createAdkStream({ api: "/api/chat" }),
});
```
Message Editing & Regeneration \[#message-editing--regeneration]
Provide a `getCheckpointId` callback to enable edit and regenerate buttons:
```typescript
const runtime = useAdkRuntime({
stream: createAdkStream({ api: "/api/chat" }),
getCheckpointId: async (threadId, parentMessages) => {
// Resolve checkpoint ID for server-side forking
return checkpointId;
},
});
```
When `getCheckpointId` is provided:
* **Edit buttons** appear on user messages
* **Regenerate buttons** appear on assistant messages
The resolved `checkpointId` is passed to your `stream` callback via `config.checkpointId`.
Without `getCheckpointId`, edit and regenerate buttons will not appear.
Hooks Reference \[#hooks-reference]
| Hook | Description |
| ---------------------------- | ----------------------------------------------------- |
| `useAdkAgentInfo()` | Current agent name and branch path |
| `useAdkSessionState()` | Full accumulated session state delta |
| `useAdkAppState()` | App-level state (`app:*` prefix, stripped) |
| `useAdkUserState()` | User-level state (`user:*` prefix, stripped) |
| `useAdkTempState()` | Temp state (`temp:*` prefix, stripped, not persisted) |
| `useAdkSend()` | Send raw ADK messages |
| `useAdkConfirmTool()` | Confirm or deny a pending tool confirmation |
| `useAdkSubmitAuth()` | Submit auth credentials for a pending auth request |
| `useAdkToolConfirmations()` | Pending tool confirmation requests |
| `useAdkAuthRequests()` | Pending auth credential requests |
| `useAdkLongRunningToolIds()` | IDs of long-running tools awaiting input |
| `useAdkArtifacts()` | Artifact delta (filename → version) |
| `useAdkEscalation()` | Whether escalation was requested |
| `useAdkMessageMetadata()` | Per-message grounding/citation/usage metadata |
Features \[#features]
| Feature | Status |
| ------------------------------------------- | --------- |
| Streaming text (SSE) | Supported |
| Tool calls & results | Supported |
| Tool confirmations (`useAdkConfirmTool`) | Supported |
| Auth credential flow (`useAdkSubmitAuth`) | Supported |
| Multi-agent (author/branch tracking) | Supported |
| Agent transfer events | Supported |
| Escalation detection | Supported |
| Chain-of-thought / reasoning | Supported |
| Code execution (executableCode + result) | Supported |
| Inline images & file data | Supported |
| Session state delta + scoped state | Supported |
| Artifact delta tracking + fetching | Supported |
| Long-running tools (HITL) | Supported |
| Grounding / citation / usage metadata | Supported |
| Structured events (`toAdkStructuredEvents`) | Supported |
| Typed `AdkRunConfig` | Supported |
| Client → server `stateDelta` | Supported |
| `finishReason` mapping (17 values) | Supported |
| `interrupted` event handling | Supported |
| Snake\_case events (Python ADK) | Supported |
| Cloud thread persistence | Supported |
| ADK session-backed thread persistence | Supported |
| Direct ADK server connection (no proxy) | Supported |
| One-liner API route (`createAdkApiRoute`) | Supported |
| Message editing & regeneration | Supported |
| Automatic tool invocations | Supported |
ADK Python Backend \[#adk-python-backend]
The package automatically normalizes snake\_case event fields from ADK Python backends to camelCase. No configuration needed — connect to either ADK JS or ADK Python servers. This includes all nested fields: `function_call` → `functionCall`, `requested_tool_confirmations` → `requestedToolConfirmations`, etc.