The Grok Build adapter runs xAI's Grok Build coding agent as a chat backend. Unlike HTTP provider adapters, this is a harness adapter: Grok Build runs its own agent loop and executes its own tools — shell commands, file edits, search — by spawning the grok CLI inside a sandbox. Each chat() call runs one full harness turn; the harness's tool activity streams back as already-resolved tool-call events your UI can render.
Requires a sandbox. grok-build declares requires: [SandboxCapability], so chat() errors at the call site unless you provide a sandbox with withSandbox(...) middleware. The sandbox — your laptop, a Docker container, or a cloud VM — is the filesystem and safety boundary the agent runs in. See the Sandboxes overview for the full picture.
npm install @tanstack/ai-grok-build @tanstack/ai-sandboxYou also need a sandbox provider (e.g. @tanstack/ai-sandbox-docker) and the grok CLI available inside the sandbox image.
Grok Build resolves credentials the same way the grok CLI does:
the XAI_API_KEY environment variable (headless / sandbox — inject it as a workspace secret), or
an existing grok.com browser login on the machine (local dev).
The two auth modes expose the model under slightly different ids; the adapter maps the short alias for you (see Models).
import { chat } from '@tanstack/ai'
import { grokBuildText } from '@tanstack/ai-grok-build'
import {
createSecrets,
defineSandbox,
defineWorkspace,
githubRepo,
withSandbox,
} from '@tanstack/ai-sandbox'
import { dockerSandbox } from '@tanstack/ai-sandbox-docker'
import { messages, threadId } from './chat-context'
const sandbox = defineSandbox({
id: 'grok-build-agent',
provider: dockerSandbox({ image: 'node:22' }),
workspace: defineWorkspace({
source: githubRepo({ repo: 'owner/app' }),
setup: ['corepack enable', 'pnpm install'],
secrets: createSecrets({ XAI_API_KEY: process.env.XAI_API_KEY ?? '' }),
}),
})
const stream = chat({
threadId,
adapter: grokBuildText('grok-build'),
messages,
middleware: [withSandbox(sandbox)],
})Grok Build accepts any xAI model id its backend supports; the known ids get autocomplete (any string is allowed):
| Model id | Notes |
|---|---|
| grok-build | The short alias. With a grok.com browser login the CLI lists it under this name. |
| grok-build-0.1 | The fully-qualified id the CLI lists when authenticated with XAI_API_KEY. |
| composer-2.5 | Also runnable through the Grok Build harness. |
Pass any of these to grokBuildText(...) — the adapter resolves grok-build to the CLI's grok-build-0.1 automatically, so the same code works under both auth modes.
Adapter config (second argument to grokBuildText):
| Option | Description |
|---|---|
| cwd | Working directory inside the sandbox. Defaults to /workspace. |
| grokExecutable | Path/name of the grok executable inside the sandbox. Defaults to grok. |
| env | Extra environment variables for the grok process inside the sandbox. |
| emitDiff | Emit a file.changed CUSTOM event with the working-tree git diff after the run. Defaults to true. |
| extraArgs | Extra raw CLI flags appended verbatim (advanced). |
Per-call overrides go through modelOptions:
| modelOptions | Description |
|---|---|
| sessionId | Resume an existing Grok Build session (see below). |
| cwd | Per-call override of the harness working directory. |
| maxTurns | Per-call cap on the number of harness turns. |
Grok Build sessions are stateful — the harness keeps the working context (files read, commands run, conclusions reached) between turns. The adapter surfaces the session id of every fresh run as a custom stream event named grok-build.session-id; thread it back via modelOptions.sessionId to resume. When resuming, only the latest user message is sent — the harness already holds the prior context.
import { chat, chatParamsFromRequest, toServerSentEventsResponse } from '@tanstack/ai'
import { grokBuildText } from '@tanstack/ai-grok-build'
import { withSandbox } from '@tanstack/ai-sandbox'
import { sandbox } from './sandbox'
export async function POST(request: Request) {
const params = await chatParamsFromRequest(request)
const sessionId =
typeof params.forwardedProps.sessionId === 'string'
? params.forwardedProps.sessionId
: undefined
const stream = chat({
adapter: grokBuildText('grok-build'),
messages: params.messages,
middleware: [withSandbox(sandbox)],
modelOptions: { sessionId },
})
return toServerSentEventsResponse(stream)
}Two kinds of tools flow through this adapter:
Built-in harness tools are executed by Grok Build itself (shell, file edits, search) and stream back as tool-call events with results already attached. Your code never executes them.
Your TanStack tools are bridged into the harness over an authenticated MCP tool-proxy: define them with toolDefinition().server() and pass them to chat({ tools }). Tool-call events come back under the names you registered. Because the harness runs in a sandbox, see Sandbox tools for how the bridge reaches your host across providers (local/Docker vs cloud).
Client-side and approval-gated tools are not supported — the harness runs tools inside a live process and can't pause across an HTTP round-trip. A tool without a server execute() (or marked needsApproval) fails fast; run those with a regular provider adapter.