Skip to main content

Claude Agent Activities

One activity, claude_agent.query, runs a full Claude Agent SDK loop inside a single activity. The agent reasons over multiple turns and calls tools autonomously; the activity returns its final result.

Use it when a step is open-ended enough that you cannot specify it in advance — "investigate why this job failed and summarise the cause". For a single prompt-and-response, use openai.chat.completions instead: an agent loop is slower and more expensive.

Setup

The Anthropic API key is named, not inlined: apikey_secret_key is the name of a secret holding it. A bare NAME resolves a user-scoped secret, global/NAME a global one.

ANTHROPIC_BASE_URL on the worker points the agent at a gateway.

Deployment-enforced ceilings clamp what a workflow may ask for — and supply the value when the workflow omits it:

VariableClamps
MOCO_CLAUDE_AGENT_MAX_TURNSmax_turns
MOCO_CLAUDE_AGENT_MAX_BUDGET_USDmax_budget_usd
MOCO_CLAUDE_AGENT_MAX_TOOL_CALLSmax_tool_calls
MOCO_CLAUDE_AGENT_WORK_DIRRoot of each run's private workspace
MOCO_CLAUDE_AGENT_PLUGIN_ROOTWhere installed plugins are discovered
This activity runs on the agent worker

claude_agent.query is the only activity served by the agent worker type (task queue agent) rather than the base worker — the Claude Agent SDK bundles a large native CLI that does not belong in the base image. Workflows still run on the base worker and route the activity across automatically; nothing in the workflowspec changes. Because it is remote-queued, execute_locally has no effect.

Defaults

86400 s (1 day) timeout, 1 attempt, heartbeating every 20 s against a 60 s timeout.

An agent run has side effects — tool calls and real spend — so it is never retried. Note the consequence: a missed heartbeat is a permanent failure.

Use timeout_sec in the input, not the activity timeout, to bound a run: it stops the agent and returns a partial result, where exhausting the activity timeout just fails the activity.


claude_agent.query

Input

FieldTypeRequiredDefaultDescription
promptstryesThe task for the agent
apikey_secret_keystryesSecret name holding the Anthropic API key
capabilitiesClaudeCapabilitiesnoemptyWhat the agent may do. Empty means text only
system_promptstrnonullCustom system prompt. Mutually exclusive with use_claude_code_preset
use_claude_code_presetboolnofalseUse the built-in coding-agent harness prompt
modelstrnoCLI defaultAlias (opus, sonnet, haiku) or a full model ID
effortenumnonullReasoning effort: low, medium, high, xhigh, max
max_turnsintnodeployment ceilingMaximum agentic turns
max_budget_usdnumbernodeployment ceilingCost ceiling for the run
max_tool_callsintnodeployment ceilingCeiling on bridged moco tool calls
timeout_secnumbernonullWall-clock limit; stops the agent and returns a partial result
workspace_subdirstrnonullName of a subdirectory inside the run's private workspace. A name, never a path
envdict[str, str]no{}Extra environment variables for the agent process. Names that could redirect the client or inject code (ANTHROPIC_*, CLAUDE_*, AWS_*, PATH, NODE_OPTIONS, LD_PRELOAD, …) are rejected
relay_topicstrno"default"Event topic progress is relayed on — see Watching a run
relay_event_typestrnonullevent_type stamped on relayed events
relay_granularityenumno"result"none, result, turn or message
relay_thinkingboolnofalseInclude thinking blocks in relayed events
target_workflow_idstrnocurrent workflowWorkflow relayed events are delivered to
debug_thinkingboolnofalseInclude thinking blocks in the debug stream
debug_tool_inputboolnofalseInclude tool argument values in the debug stream, not just their keys
include_transcriptboolnofalseReturn the full message transcript
max_result_charsintnonullTruncate the returned result text
max_transcript_charsintnonullTruncate the returned transcript

Output

FieldTypeDescription
resultstrThe agent's final answer
is_errorboolWhether the run ended in an error state
subtypestr | nullResult subtype reported by the SDK (success, error, …)
terminal_reasonstr | nullWhy the run ended, e.g. aborted_tools
num_turnsint | nullAgentic turns used
duration_msint | nullRun duration
total_cost_usdnumber | nullEstimated cost
usagedict | nullToken usage
tool_callslist[object]Tools invoked, each {name, activity_type, is_error}
denied_toolslist[str]Tools the agent tried that the permission gate refused
timed_outboolWhether timeout_sec stopped the run
truncatedboolWhether result or transcript was cut to fit its cap
session_idstr | nullCLI session id, informational only
transcriptlist[object] | nullFull message transcript, when include_transcript is set

A successful run with a non-empty denied_tools usually means the capability grant was too narrow for the prompt.

Example

From moco-examples/claude-agent-demo/src/claude-agent-demo.yaml:

- activity:
type: claude_agent.query
name: plan_the_work
input_data:
apikey_secret_key: "{{ claude_apikey_secret_key }}"
prompt: |
Break the following question into at most three concrete lookup steps.
Answer with a short numbered list and nothing else.

Question: {{ question }}
max_turns: 2
effort: low
output_name: plan

With tools granted:

- activity:
type: claude_agent.query
name: investigate
input_data:
apikey_secret_key: "global/ANTHROPIC_API_KEY"
prompt: |
Find out what https://api.github.com/zen returns and summarise it.
capabilities:
# Moco activities exposed to the agent as tools. They run through the normal
# providers under the calling user, so the agent can never exceed that user's
# own privileges.
moco_tools:
- http.request
max_turns: 8
max_tool_calls: 5
timeout_sec: 300
relay_topic: agent.progress # optional: stream progress as workflow events
relay_granularity: turn
output_name: investigation

A runnable example lives in moco-examples/claude-agent-demo/.


Capabilities

Deny by default

The agent starts with no capabilities. Every tool must be granted explicitly under capabilities.

This matters because the agent reads content you do not control — web pages, documents, tool output — and then acts with the calling user's privileges. Grant the smallest set of tools the task needs.

FieldTypePurpose
builtin_toolslist[str]Claude Code built-ins, e.g. ["Read", "Grep", "Glob"]. Tools not listed do not exist in the agent's context
moco_toolslist[str]Moco activity types exposed as mcp__moco__<type> tools
mcp_serversdict[str, object]External MCP servers, keyed by name
mcp_toolslist[str]Tool names allowed from those servers; a trailing wildcard per server is supported ("mcp__github__*")
pluginslist[str]Claude Agent plugins to load, by name
skillslist[str]Skills to enable, as <plugin>:<skill>

Bridged moco tools run through the normal activity providers under the caller's identity, so the agent can never exceed the calling user's own privileges. Secret, state, deploy, shell, workflow-execution and re-entrant agent activities are on a hard denylist and can never be bridged, at any privilege level. Tools named in moco_tools are allowed automatically and need not be repeated in mcp_tools.

External MCP servers must be remote — {"type": "http"|"sse", "url": ...}. Stdio servers ({"command": ..., "args": [...]}) are rejected: they are an unguarded process-spawn primitive with none of the checks that gate shell.run. Supply credentials with headers_secret_key, naming a secret that holds a JSON object of headers, rather than an inline headers dict.

Elevated capabilities

Granting Bash, Write, Edit or NotebookEdit, or loading any plugin, escalates the authorization check from run_agent to run_agent_elevated against internal.claude_agent_activities.

Granting Bash exposes the API key

The agent can read its own process environment, which holds the resolved Anthropic key. Write and Edit similarly make the workspace boundary decorative.

The authz gate fails open

If internal.claude_agent_activities is not deployed, the check passes. A deployment that wants this activity restricted must actually deploy the policy — see Authz Activities.

Plugins

A Claude Agent plugin bundles skills, commands, subagents and hooks. Plugins are baked into the worker image and discovered from MOCO_CLAUDE_AGENT_PLUGIN_ROOT — every immediate subdirectory of that root is one available plugin. A workflow selects among them by name; it can never supply a path.

capabilities:
plugins:
- deployment-tools # name from the deployment's catalog
skills:
- deployment-tools:rollback # <plugin-name>:<skill-name>

The plugin's name is the name in its .claude-plugin/plugin.json, falling back to its directory name. Ask your operator which plugins are installed; naming one that is not produces an error listing what is available.

Granting a skill implies the Skill tool, so you do not need to add it to builtin_tools.

Plugins run code on the worker

A plugin's hooks execute shell commands on lifecycle events, outside the tool permission system — they fire even for an agent granted no tools at all. Loading a plugin is therefore equivalent to granting code execution, and requires the same elevated authorization as Bash.

Select no plugins (the default) and no plugin code runs.

Plugin MCP servers are not available

MCP servers declared inside a plugin's .mcp.json are deliberately suppressed: surfacing them would require handing the agent the complete built-in tool set, including Bash. Declare the server under capabilities.mcp_servers instead.


Watching a run

An agent run is minutes of otherwise-silent work. Two independent channels report on it.

Relayed events go to the workflow, on relay_topic, consumed with wait_for. Use them when the workflow needs to react to progress.

relay_granularityRelays
noneNothing
result (default)The final result only
turnOne event per assistant turn
messageOne event per text or tool-use block

Each relayed event is a Temporal signal and therefore a workflow-history entry, and the engine retains at most 1000 unmatched events per topic — a 200-tool-call run at message granularity is 200 signals. Relayed text is capped at 8000 characters, and a relay failure never fails the run.

Debug progress goes to the client: session init, each turn, every tool call and its outcome, refused tools, subagent tasks and a final summary, printed live by the moco CLI. It costs no workflow history and needs no configuration, but is inert unless the run is in debug mode. debug_thinking and debug_tool_input add thinking blocks and tool argument values; both are off by default because thinking restates content the agent read, and tool arguments are the file paths, queries and request bodies themselves.

No session resumption

Each run gets a private temporary working directory that is deleted afterwards, and the CLI keys its session transcripts to that directory on local disk. Sessions therefore cannot be resumed across activity runs. Model a multi-turn conversation by looping in the workflow and passing prior context back through prompt.