Skip to main content

Claude Agent SDK integration

View Markdown

Temporal's integration with the Claude Agent SDK gives you the same tools, agent loop, and context management that power Claude Code, programmable in a Temporal Workflow.

The Claude Agent SDK allows you to drive the Claude Code agent from your own application, in a process you operate. Temporal provides the durable execution of the hooks, tools, and prompts to steer the agent.

Code snippets on this page come from the Claude Agent SDK samples directory.

Get started​

Prerequisites

Install the Claude Agent SDK integration

Install the temporalio-claude-agent-sdk package.

uv add temporalio-claude-agent-sdk

Drive Claude Code from a Workflow​

The Claude Agent SDK communicates with Claude Code through a Transport. Its default transport starts a subprocess and communicates with it over standard input and output (stdio). Those operations aren't safe to perform in a Workflow.

To drive Claude Code from a Workflow, use TemporalTransport. It performs the process communication through Activities, so the Workflow can replay safely. Pass it as the transport argument to query() or the ClaudeSDKClient constructor, using the same options for both the transport and the SDK call.

claude_agent_sdk_plugin/structured_output/workflow.py

transport = TemporalTransport(options)
async with ClaudeSDKClient(
options=options, transport=transport
) as client:
await client.query(query_prompt)
danger

Don't pass secrets in ClaudeAgentOptions.env. Those values are recorded in Workflow Event History as Activity input. Set secrets in the environment where Claude Code runs, or use a credential proxy to keep them out of the sandbox.

Register ClaudeAgentSDKPlugin on the Worker. The plugin registers the Activities that TemporalTransport uses.

claude_agent_sdk_plugin/tools/run_worker.py

worker = Worker(
client,
task_queue="claude-agent-sdk-tools",
workflows=[ToolsWorkflow],
activities=[fetch_weather],
plugins=[ClaudeAgentSDKPlugin()],
)

Import ClaudeAgentSDKPlugin, TemporalTransport, and the connector APIs from temporalio.claude_agent_sdk.

Provide custom tools​

In addition to Claude Code's built-in tools, the Claude Agent SDK lets you define custom tools through an in-process MCP server. With the Temporal integration, custom tools you define in the Workflow execute in Workflow context and benefit from durable execution. Their functions must follow the Workflow determinism rules, so use an Activity or Child Workflow for non-deterministic work such as calling an external API.

In the tools sample, get_weather calls the fetch_weather Activity, which returns a fixed response as a placeholder for an API call. Pass get_weather and any other tools to create_sdk_mcp_server, then add the server to ClaudeAgentOptions.mcp_servers.

Claude Code needs permission to use the tools. In the sample, allowed_tools grants that permission using each tool's fully qualified name.

Add guardrails to agent behavior​

Intercept agent behavior with hooks

The Claude Agent SDK lets you intercept and customize agent behavior at key execution points with hooks. With the Temporal integration, hooks you define in the Workflow execute in Workflow context and benefit from durable execution. Their callbacks must follow the Workflow determinism rules, so use an Activity or Child Workflow for non-deterministic work such as writing to an external audit service. Temporal can retry Activities when those operations fail.

In the hooks sample, a PreToolUse hook denies Bash commands containing rm and logs the command through an Activity. The sample registers the hook in ClaudeAgentOptions.hooks with a HookMatcher that selects Bash tool calls.

Add human approval

The Claude Agent SDK lets you provide a can_use_tool callback to decide whether Claude Code can use a tool. With the Temporal integration, the callback runs in the Workflow and can wait durably for a person's approval through a Signal or Update. The callback must follow the Workflow determinism rules, so use an Activity or Child Workflow for non-deterministic work.

In the human-in-the-loop sample, the can_use_tool callback waits for a person's decision and returns the corresponding permission result. The sample is designed for one pending approval at a time. A Query shows the pending tool call, and an Update approves or denies it.

Stream output from a Workflow​

The Claude Agent SDK can stream a reply as it's generated. The plugin reads each event from Claude Code in an Activity, and the Workflow publishes it to a topic of a Workflow Stream. Any client can subscribe to the topic by Workflow Id to show tokens as they arrive.

Create a topic when the Workflow starts. Set include_partial_messages=True in the options, as in the Claude Agent SDK, and pass the topic to TemporalTransport as stream. The two go together. Setting one without the other raises ValueError.

In the streaming sample, a client subscribes to the topic and receives stream_event messages from Claude Code. For details on event delivery and stream completion, see Workflow Streams.

Configure a custom connector​

By default, Claude Code runs in a separate subprocess started by the Worker, with the Worker's user, environment, and files. The Claude Agent SDK documents this as its subprocess model. To run Claude Code somewhere else, such as a container, a VM, or a remote host, provide a custom connector to the plugin. Your connector provides access to the Claude Code process, and TemporalTransport uses Activities to manage communication between the Worker and the process.

A custom connector implements the Connector protocol. Its connect method starts Claude Code and returns a ClaudeCodeProcess, which provides the methods for communicating with the process. The default SubprocessConnector implements this protocol for a local subprocess.

For example, the custom connector sample runs Claude Code in a Docker Sandbox, providing an isolation boundary between the Worker and Claude Code. Its connector sets up the sandbox and uses SubprocessConnector to run sbx as the local process.

The Workflow continues to use TemporalTransport as before, while you pass your custom connector to ClaudeAgentSDKPlugin on the Worker.

claude_agent_sdk_plugin/plugin_with_custom_transport/run_worker.py

plugin = ClaudeAgentSDKPlugin(
connector=SandboxConnector(uploads={PLUGIN_DIR: LOCAL_PLUGIN_DIR})
)

Copy files in. A sandbox starts with a clean filesystem, and paths in ClaudeAgentOptions, such as cwd and plugin directories, are paths on Claude Code's host. Anything Claude Code needs must be copied there before it starts. The sample connector copies the configured directories from the Worker host to their paths in the sandbox.

Keep secrets out of the sandbox. Use the Docker Sandbox credential proxy to keep the real API key outside the sandbox. The sample connector registers the key with the proxy, which adds it to requests to Anthropic API hosts. Claude Code only sees a placeholder. See Manage credentials in the Docker Sandbox docs.

Manage the Worker connection to Claude Code​

A connection to the Claude Code process can be held by only one Worker. Any eligible Worker can establish the connection, but once established, that Worker handles all Activities that communicate with the process. If the connection is lost, the Workflow can create a new ClaudeSDKClient and resume the Claude Code session. When a Worker fails, Temporal replays the Workflow on another Worker, where it can establish a new connection.

To resume the session, Claude Code needs access to its session files. With the default connector, these files are stored on the Worker host. Use shared storage or set activity_options={"connect": {"task_queue": ...}} on TemporalTransport to route connections to a Task Queue polled by a Worker that can access the session files.

To resume the interrupted turn, configure these Claude Agent SDK options.

  • resume selects the Claude Code session.
  • resume_session_at resumes from the interrupted prompt.
  • extra_args={"replay-user-messages": None} makes the prompt identifier available in the response stream.

The sample sets these options in ClaudeAgentOptions.

claude_agent_sdk_plugin/tools/workflow.py

extra_args={"replay-user-messages": None},
resume=session_id if prompt_id else None,
resume_session_at=prompt_id,

The following loop uses those options to resume the session after a lost connection.

claude_agent_sdk_plugin/tools/workflow.py

async def ask(self, prompt: str) -> str:
"""Answer the prompt, redoing the turn if the connection is lost."""
CONTINUE = "Continue from where you left off."
session_id = None # Claude Code's, once it started
prompt_id = None # The prompt's, once Claude Code saved it

while True:
try:
options = self.options(session_id, prompt_id)
transport = TemporalTransport(options)
async with ClaudeSDKClient(
options=options, transport=transport
) as client:
await client.query(CONTINUE if prompt_id else prompt)

async for message in client.receive_response():
if (
isinstance(message, SystemMessage)
and message.subtype == "init"
):
session_id = message.data["session_id"]
elif (
isinstance(message, UserMessage)
and message.uuid
and not prompt_id
):
prompt_id = message.uuid
elif isinstance(message, ResultMessage):
return message.result or "\n".join(message.errors or [])

raise RuntimeError("Claude Code ended the turn without a result")

except CLIConnectionError:
# Redo the turn from the prompt if Claude Code saved it, or start again.
pass

info

Claude Code repeats the interrupted turn, so tools and hooks may run again and previously approved tool calls may require approval again. Make your tools and hooks safe to run more than once.

The communication Activities use a 30-second schedule-to-start timeout by default. You can change it with activity_options as shown in the configuration reference.

Reference and troubleshooting​

Feature support

Other Claude Agent SDK features that need no changes for Temporal can be used as usual, subject to the limitations below.

FeatureSupportNotes
Custom toolsSupportedIn-process MCP tool functions run in the Workflow and must be deterministic.
HooksSupportedHook callbacks run in the Workflow and must be deterministic.
Tool approvalsSupportedThe can_use_tool callback runs in the Workflow and must be deterministic.
StreamingSupportedEnable partial messages and pass a Workflow Stream topic to TemporalTransport. Retried turns can produce repeated output.
Session recoverySupportedA new connection needs access to the Claude Code session files. Resuming an interrupted turn can repeat tool calls and approvals.
Custom connectorsSupportedwrite, end_input, and close must be safe to retry and call concurrently.
Remote connectionsSupportedUse a custom connector. For OpenTelemetry, pass TRACEPARENT and TRACESTATE in the Claude Code process environment when starting it.
OpenTelemetrySupportedRegister OpenTelemetryPlugin before ClaudeAgentSDKPlugin and enable Claude Code tracing where it runs. See the sample.
Default Claude Agent SDK transportNot supported in WorkflowsUse TemporalTransport for Workflow-safe process communication.
settings as a file pathNot supportedPass a JSON object string instead of a file path.

Composing with other plugins

You can register ClaudeAgentSDKPlugin alongside other Temporal plugins. Plugins configure the Worker in registration order, so check each plugin's documentation for ordering requirements.

Replay safety

TemporalTransport runs communication with Claude Code through Activities. Temporal records their results in the Workflow's Event History and uses those results during replay. Custom tool functions, hooks, and can_use_tool callbacks run in the Workflow and must be deterministic.

Each turn adds to the Workflow's Event History. For a long conversation, finish the turn, disconnect, and Continue-As-New with the session identifier. In the new Workflow run, set resume to that session identifier. See the agent chat sample.

Configuration

APIOptionDefaultBehavior
ClaudeAgentSDKPluginconnectorSubprocessConnector()Starts and connects to Claude Code. Provide a custom connector to run it in another environment.
TemporalTransportoptionsRequiredClaude Agent SDK options. Pass the same options to the transport and the client.
TemporalTransportactivity_optionsNoneConfigures Activity options for connect, write, read, end_input, or close. Use schedule_to_start_timeout to detect a lost Worker sooner.
TemporalTransportstreamNoneA Workflow Stream topic for partial messages. Set include_partial_messages=True in the Claude Agent SDK options when providing a topic.

Failure behavior

The transport raises Claude Agent SDK errors in the Workflow. The plugin treats these errors as Workflow failures if left unhandled. Other Activity errors are retried according to the Activity retry policy.

ErrorMeaning
CLIConnectionErrorThe connection to Claude Code is unavailable. After a Worker is lost, create a new client and resume the session as described in Manage the Worker connection to Claude Code.
CLINotFoundErrorThe Claude Code executable could not be found where the connector runs it.
ProcessErrorClaude Code exited with an error.
CLIJSONDecodeErrorOutput from Claude Code could not be decoded as JSON.
ValueErrorThe transport configuration is invalid. Set both include_partial_messages and stream, or neither, and pass settings as a JSON object string instead of a file path.

Resources​