Skip to main content
query() is the primary way to run the Claude Code agent loop from TypeScript. It takes a prompt and an options object, then returns an async generator that yields SDKMessage objects as the agent works through the task.

Signature

import { query } from '@anthropic-ai/claude-code'
import type { SDKMessage, SDKUserMessage, Options } from '@anthropic-ai/claude-code'

function query(params: {
  prompt: string | AsyncIterable<SDKUserMessage>
  options?: Options
}): AsyncGenerator<SDKMessage>

Parameters

prompt
string | AsyncIterable<SDKUserMessage>
required
The user prompt to send to the agent. Pass a plain string for a single turn, or an AsyncIterable<SDKUserMessage> to stream a multi-turn conversation.
options
Options
Configuration for the agent run. All fields are optional unless noted.

Return value

query() returns AsyncGenerator<SDKMessage>. Iterate it with for await. The generator completes (returns) after yielding the final result message. The result is always the last message in the stream.
SDKMessage
union
A discriminated union over type. The important members:

Examples

Basic usage

import { query } from '@anthropic-ai/claude-code'

for await (const message of query({
  prompt: 'What is 2 + 2?',
  options: { model: 'claude-opus-4-5' },
})) {
  if (message.type === 'result' && message.subtype === 'success') {
    console.log(message.result)
    // => '4'
  }
}

Collecting the text result

import { query } from '@anthropic-ai/claude-code'

async function runAgent(prompt: string): Promise<string> {
  for await (const message of query({ prompt })) {
    if (message.type === 'result') {
      if (message.subtype === 'success') return message.result
      throw new Error(`Agent failed: ${message.errors?.join(', ')}`)
    }
  }
  throw new Error('No result received')
}

const answer = await runAgent('Summarize the README in one sentence.')
console.log(answer)

Streaming text tokens

import { query } from '@anthropic-ai/claude-code'

for await (const message of query({ prompt: 'Write a haiku about TypeScript.' })) {
  if (message.type === 'stream_event') {
    const event = message.event
    if (
      event.type === 'content_block_delta' &&
      event.delta.type === 'text_delta'
    ) {
      process.stdout.write(event.delta.text)
    }
  }
}

Cancelling a run

import { query, AbortError } from '@anthropic-ai/claude-code'

const controller = new AbortController()

// Cancel after 10 seconds
setTimeout(() => controller.abort(), 10_000)

try {
  for await (const message of query({
    prompt: 'Refactor the entire codebase.',
    options: { signal: controller.signal },
  })) {
    // process messages
  }
} catch (err) {
  if (err instanceof AbortError) {
    console.log('Cancelled.')
  } else {
    throw err
  }
}

Limiting turns and budget

import { query } from '@anthropic-ai/claude-code'

for await (const message of query({
  prompt: 'Fix all the lint errors.',
  options: {
    maxTurns: 20,
    maxBudgetUsd: 0.50,
    permissionMode: 'acceptEdits',
  },
})) {
  if (message.type === 'result') {
    if (message.subtype === 'error_max_turns') {
      console.warn('Stopped: max turns reached')
    } else if (message.subtype === 'error_max_budget_usd') {
      console.warn('Stopped: budget exceeded')
    }
  }
}

Structured output

import { query } from '@anthropic-ai/claude-code'

for await (const message of query({
  prompt: 'List the exported functions in src/index.ts as JSON.',
  options: {
    outputFormat: {
      type: 'json_schema',
      schema: {
        type: 'object',
        properties: {
          functions: {
            type: 'array',
            items: { type: 'string' },
          },
        },
        required: ['functions'],
      },
    },
  },
})) {
  if (message.type === 'result' && message.subtype === 'success') {
    const output = message.structured_output as { functions: string[] }
    console.log(output.functions)
  }
}

Using an MCP server

import { query } from '@anthropic-ai/claude-code'

for await (const message of query({
  prompt: 'Query the users table and count active accounts.',
  options: {
    mcpServers: {
      database: {
        type: 'stdio',
        command: 'mcp-server-sqlite',
        args: ['--db', './myapp.db'],
      },
    },
  },
})) {
  if (message.type === 'result' && message.subtype === 'success') {
    console.log(message.result)
  }
}

Error handling

The generator throws AbortError when cancelled via AbortSignal. Other fatal errors (network failures, authentication errors) propagate as standard Error instances. Non-fatal errors — such as a rate-limit retry — are emitted as system (api_retry) messages rather than thrown, so your iteration loop continues.
import { query, AbortError } from '@anthropic-ai/claude-code'

try {
  for await (const message of query({ prompt: 'Do something.' })) {
    if (message.type === 'system' && message.subtype === 'api_retry') {
      console.warn(`API error, retrying (attempt ${message.attempt})…`)
    }
  }
} catch (err) {
  if (err instanceof AbortError) {
    // user cancelled
  } else {
    // unexpected error
    throw err
  }
}

Permission denials

When the agent attempts a tool call and permission is denied, the denial is recorded in SDKResultSuccess.permission_denials — an array of { tool_name, tool_use_id, tool_input } objects. The agent sees the denial as a tool result and may adjust its approach or stop.
permissionMode: 'bypassPermissions' skips all permission checks and is intended for controlled, sandboxed environments only. Never use it on production machines or directories with sensitive data.

Build docs developers (and LLMs) love