NAME

Claude::Agent::Query - One conversation with the Claude Code CLI

SYNOPSIS

use Claude::Agent::Query;
use Claude::Agent::Options;

my $query = Claude::Agent::Query->new(
    prompt  => "Find all TODO comments",
    options => Claude::Agent::Options->new(
        allowed_tools => ['Read', 'Glob', 'Grep'],
    ),
);

while (my $msg = $query->next) {
    if ($msg->isa('Claude::Agent::Message::Result')) {
        print $msg->result, "\n";
        last;
    }
}

my $future = $query->next_async;

DESCRIPTION

A query starts the claude CLI as a child process, speaks the SDK control protocol with it over its standard streams, and delivers the messages it emits as Claude::Agent::Message objects. The query() function of Claude::Agent builds one.

The CLI is started with --output-format stream-json --input-format stream-json. The first thing written to it is an initialize control request carrying the hook registrations and agent definitions; the prompt is written as a user message once the CLI has answered. Hooks, can_use_tool and in-process MCP servers are served from the CLI's control_request messages. Standard input is closed as soon as the prompt is sent unless one of those is in use, in which case it stays open until the turn's result arrives and the CLI reports itself idle.

ATTRIBUTES

prompt

The first user message: a string, or an ArrayRef of content blocks. May be left unset for a session that will send its first message later.

options

A Claude::Agent::Options. Defaults to an empty one.

loop

An IO::Async::Loop to run on. One is created when none is given; pass your application's loop for event-driven use.

persistent

Keep standard input open across turns. Claude::Agent::Client sets it.

spawn, writer, closer

Test seams. With spawn => 0 no process is started: every line that would go to the CLI is passed to writer as ($line, $data), closing standard input calls closer, and feed_line injects the CLI's output.

METHODS

new

Takes a flat list of name/value pairs. Unknown names are ignored. The CLI is located (cli_path, PATH, then the usual install locations) and started before new returns; a CLI older than $Claude::Agent::Query::MINIMUM_CLAUDE_CODE_VERSION is warned about. Set CLAUDE_AGENT_SDK_SKIP_VERSION_CHECK to skip the probe.

next

my $msg = $query->next;

Blocks until the next message, returning undef when the process has finished. Waits at most the query_timeout option (default 600 seconds) and then records a timeout in error.

next_async

my $msg = await $query->next_async;

A Future of the next message, resolving to undef when the process has finished.

send_user_message

$query->send_user_message("Continue", session_id => $id);

Write a user message. Returns true when written.

interrupt, interrupt_async

Ask the CLI to stop the current turn.

set_permission_mode, set_permission_mode_async

$query->set_permission_mode('acceptEdits');

set_model, set_model_async

$query->set_model('sonnet');

set_max_thinking_tokens, set_max_thinking_tokens_async

$query->set_max_thinking_tokens(8000);

rewind_files, rewind_files_async

$query->rewind_files($result->user_message_uuid);

Undo file changes back to a prompt. Needs the enable_file_checkpointing option.

mcp_status, mcp_status_async

The CLI's MCP server status report.

get_context_usage, get_context_usage_async

The CLI's context window usage breakdown.

reconnect_mcp_server, reconnect_mcp_server_async

$query->reconnect_mcp_server('github');

Ask the CLI to reconnect an MCP server.

toggle_mcp_server, toggle_mcp_server_async

$query->toggle_mcp_server('github', 0);

Enable or disable an MCP server for the session.

stop_task, stop_task_async

$query->stop_task($task_id);

Stop a background task.

Every _async method returns a Future of the CLI's response hash, failing with the CLI's error text, on timeout (60 seconds), or when the query is not running. The plain form runs the loop until the Future is ready and returns the response, or undef on failure.

server_info

The CLI's answer to the initialize request, once the query has started. On Claude Code 2.1.300 it carries commands, models, output_style, available_output_styles, account, capabilities, claude_code_version, current_permission_mode, session_state, fast_mode_state and pid.

supported_commands, supported_models, output_style, available_output_styles

Fields of server_info.

session_id

The session id, once the CLI's init message has arrived.

cli_version

my $version = $query->cli_version;

The version the claude -v probe reported for the CLI in use, if it ran. Takes an optional path to ask about another CLI.

is_finished

True once the process has exited.

error

The error text if the process failed, timed out, or could not initialize.

close, close_async

$query->close;

Stop the process: an interrupt request, then standard input is closed, then SIGINT, SIGTERM and SIGKILL at five second intervals until it exits. Pending Futures are resolved.

cleanup

An alias for close, kept from 0.13.

feed_line

$query->feed_line($json);

Process one line as if the CLI had written it. A test seam.

ENVIRONMENT

The child inherits the environment with CLAUDECODE removed and CLAUDE_CODE_ENTRYPOINT, CLAUDE_AGENT_SDK_VERSION and CLAUDE_CODE_SDK_READS_SESSION_STATE set; the env option is applied over it. CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS (default 600000, 0 for none) bounds how long standard input stays open after a result while the CLI still reports background work. CLAUDE_AGENT_JSONL_BUFFER_MAX caps the partial-line buffer.

AUTHOR

LNATION, <email at lnation.org>

LICENSE

This software is Copyright (c) 2026 by LNATION.

This is free software, licensed under The Artistic License 2.0 (GPL Compatible).