Skip to main content

AgentConfig

Struct AgentConfig 

Source
#[non_exhaustive]
pub struct AgentConfig<Tools = NoTools, Schema = NoSchema> {
Show 20 fields pub system_prompt: Option<String>, pub prompt: String, pub model: String, pub allowed_tools: Vec<String>, pub disallowed_tools: Vec<String>, pub max_turns: Option<u32>, pub max_budget_usd: Option<f64>, pub working_dir: Option<String>, pub mcp_config: Option<String>, pub strict_mcp_config: bool, pub bare: bool, pub permission_mode: PermissionMode, pub json_schema: Option<String>, pub resume_session_id: Option<String>, pub verbose: bool, pub pod_labels: BTreeMap<String, String>, pub inputs: Vec<AgentInput>, pub allow_failure: bool, pub retry: Option<RetryPolicy>, pub trace_context: Option<WorkflowTraceContext>, /* private fields */
}
Expand description

Serializable configuration passed to an AgentProvider for a single invocation.

Built by Agent::run from the builder state. Provider implementations translate these fields into whatever format the underlying backend expects.

§Typestate: tools vs structured output

Claude CLI has a known bug where combining --json-schema with --allowedTools always returns structured_output: null. To prevent this at compile time, allow_tool and output / output_schema_raw are mutually exclusive: using one removes the other from the available API.

use ironflow_core::provider::AgentConfig;

// OK: tools only
let _ = AgentConfig::new("search").allow_tool("WebSearch");

// OK: structured output only
let _ = AgentConfig::new("classify").output_schema_raw(r#"{"type":"object"}"#);
use ironflow_core::provider::AgentConfig;
// COMPILE ERROR: cannot add tools after setting structured output
let _ = AgentConfig::new("x").output_schema_raw("{}").allow_tool("Read");
use ironflow_core::provider::AgentConfig;
// COMPILE ERROR: cannot set structured output after adding tools
let _ = AgentConfig::new("x").allow_tool("Read").output_schema_raw("{}");

Workaround: split the work into two steps – one agent with tools to gather data, then a second agent with .output::<T>() to structure the result.

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§system_prompt: Option<String>

Optional system prompt that sets the agent’s persona or constraints.

§prompt: String

The user prompt - the main instruction to the agent.

§model: String

Which model to use for this invocation.

Accepts any string. Use Model constants for well-known Claude models (e.g. Model::SONNET), or pass a custom identifier for other providers.

§allowed_tools: Vec<String>

Allowlist of tool names the agent may invoke (empty = provider default).

§disallowed_tools: Vec<String>

Denylist of tool names the agent MUST NOT invoke.

Maps to --disallowedTools on the Claude CLI. Unlike allowed_tools, this does not activate any tools; it only filters out tools that would otherwise be loaded by default. As such, it is safe to combine with structured output (output) without triggering the Claude CLI bug that affects --json-schema + --allowedTools.

§max_turns: Option<u32>

Maximum number of agentic turns before the provider should stop.

§max_budget_usd: Option<f64>

Maximum spend in USD for this single invocation.

§working_dir: Option<String>

Working directory for the agent process.

§mcp_config: Option<String>

Path to an MCP server configuration file.

§strict_mcp_config: bool

When true, pass --strict-mcp-config to the Claude CLI so it only loads MCP servers from mcp_config and ignores any global/user MCP configuration (e.g. ~/.claude.json).

Useful to prevent global MCP servers from leaking tools into steps that request structured_output, which triggers the Claude CLI bug where --json-schema combined with any active tool returns structured_output: null. See https://github.com/anthropics/claude-code/issues/18536.

Combine with mcp_config set to a file containing {"mcpServers":{}} to disable every MCP server for the invocation.

§bare: bool

When true, pass --bare to Claude CLI. Bare mode disables:

  • auto-memory (automatic creation of ~/.claude/.../memory/*.md files)
  • CLAUDE.md auto-discovery (no global/project CLAUDE.md loaded)
  • hooks, LSP, plugin sync, attribution, background prefetches

Recommended for orchestrator agents that should not have any implicit side effects on the user’s filesystem or inherit user-level context.

§Authentication requirement

--bare is only compatible with an Anthropic API key (ANTHROPIC_API_KEY environment variable). It does not work with OAuth authentication (claude /login / keychain-stored credentials), because bare mode disables keychain reads.

§permission_mode: PermissionMode

Permission mode controlling how the agent handles tool-use approvals.

§json_schema: Option<String>

Optional JSON Schema string. When set, the provider should request structured (typed) output from the model.

§resume_session_id: Option<String>

Optional session ID to resume a previous conversation.

When set, the provider should continue the conversation from the specified session rather than starting a new one.

§verbose: bool

Enable verbose/debug mode to capture the full conversation trace.

When true, the provider uses streaming output (stream-json) to record every assistant message and tool call. The resulting AgentOutput::debug_messages field will contain the conversation trace for inspection.

§pod_labels: BTreeMap<String, String>

Custom labels applied to the pod (K8s providers only).

Non-K8s providers ignore this field. Labels are merged with the provider-level pod labels and the hardcoded ironflow labels. In case of conflict, hardcoded labels always win, then invocation-level labels, then provider-level defaults.

§inputs: Vec<AgentInput>

External inputs to materialize on the agent’s filesystem before invocation.

See AgentInput for the semantics. The provider is responsible for fetching each URL and placing it at mount_path before the agent runs. Add inputs with AgentConfig::input_file.

§allow_failure: bool

When true, a failure of this step does not fail the run.

§retry: Option<RetryPolicy>

Optional step-level retry policy.

§trace_context: Option<WorkflowTraceContext>

Optional W3C trace context for distributed tracing propagation.

When set, providers can inject the traceparent header into outgoing HTTP requests (LLM APIs, MCP servers) to correlate workflow spans with downstream service spans.

Implementations§

Source§

impl AgentConfig

Source

pub fn new(prompt: &str) -> Self

Create an AgentConfig with required fields and defaults for the rest.

Source§

impl<Tools, Schema> AgentConfig<Tools, Schema>

Source

pub fn system_prompt(self, prompt: &str) -> Self

Set the system prompt.

Source

pub fn model(self, model: &str) -> Self

Set the model name.

Source

pub fn max_budget_usd(self, budget: f64) -> Self

Set the maximum budget in USD.

Source

pub fn max_turns(self, turns: u32) -> Self

Set the maximum number of turns.

Source

pub fn working_dir(self, dir: &str) -> Self

Set the working directory.

Source

pub fn permission_mode(self, mode: PermissionMode) -> Self

Set the permission mode.

Source

pub fn verbose(self, enabled: bool) -> Self

Enable verbose/debug mode.

Source

pub fn mcp_config(self, config: &str) -> Self

Set the MCP server configuration file path.

Source

pub fn strict_mcp_config(self, strict: bool) -> Self

Enable strict MCP config mode.

When true, the Claude CLI is invoked with --strict-mcp-config, which disables loading of any MCP server defined outside the mcp_config file (the global ~/.claude.json and user-level configs are ignored).

This is the recommended way to prevent global MCP servers from silently injecting tools into a structured-output step and triggering the Claude CLI bug that returns structured_output: null whenever any tool is active. See https://github.com/anthropics/claude-code/issues/18536.

§Examples
use ironflow_core::provider::AgentConfig;
use schemars::JsonSchema;

#[derive(serde::Deserialize, JsonSchema)]
struct Out { ok: bool }

// Isolate the step from any global MCP server so structured output works.
let config = AgentConfig::new("classify this")
    .strict_mcp_config(true)
    .mcp_config(r#"{"mcpServers":{}}"#)
    .output::<Out>();
Source

pub fn bare(self, enabled: bool) -> Self

Enable bare mode (minimal Claude Code environment, see --bare).

When true, the Claude CLI is invoked with --bare, which disables:

  • auto-memory (no automatic ~/.claude/.../memory/*.md file creation)
  • CLAUDE.md auto-discovery (neither global nor project-level)
  • hooks, LSP, plugin sync, attribution, background prefetches, keychain reads

Sets CLAUDE_CODE_SIMPLE=1 in the child process.

Recommended for orchestrator steps that should not have any implicit side effects on the user’s filesystem or inherit user-level context (email, preferences, etc.).

§Authentication requirement

--bare is only compatible with an Anthropic API key (ANTHROPIC_API_KEY environment variable). It does not work with OAuth authentication (claude /login / keychain-stored credentials), because bare mode disables keychain reads. Invoking a bare agent on an OAuth-only host will fail with an authentication error.

§Examples
use ironflow_core::provider::AgentConfig;

let config = AgentConfig::new("classify this")
    .bare(true);
Source

pub fn allow_failure(self) -> Self

Mark this step as allowed to fail without stopping the run.

§Examples
use ironflow_core::provider::AgentConfig;

let config = AgentConfig::new("lint the code").allow_failure();
assert!(config.allow_failure);
Source

pub fn disallowed_tools<I, S>(self, tools: I) -> Self
where I: IntoIterator<Item = S>, S: Into<String>,

Replace the entire disallowed-tools list.

Maps to --disallowedTools on the Claude CLI. This method is available on every typestate variant (including AgentConfig<NoTools, WithSchema>) because, unlike allow_tool, disallowed_tools does not activate any tool – it only filters out tools that would otherwise be loaded by default.

As such, it is safe to combine with structured output:

§Examples
use ironflow_core::provider::AgentConfig;
use schemars::JsonSchema;

#[derive(serde::Deserialize, JsonSchema)]
struct Out { ok: bool }

let config = AgentConfig::new("classify this")
    .disallowed_tools(["Write", "Edit"])
    .output::<Out>();
Source

pub fn pod_label(self, key: &str, value: &str) -> Self

Add a single custom pod label (K8s providers only).

Can be called multiple times. Non-K8s providers ignore this field.

§Examples
use ironflow_core::provider::AgentConfig;

let config = AgentConfig::new("analyze")
    .pod_label("ironflow.io/network-profile", "grafana-only")
    .pod_label("team", "observability");
Source

pub fn pod_labels(self, labels: BTreeMap<String, String>) -> Self

Replace the entire custom pod labels map (K8s providers only).

Non-K8s providers ignore this field.

§Examples
use std::collections::BTreeMap;
use ironflow_core::provider::AgentConfig;

let mut labels = BTreeMap::new();
labels.insert("env".to_string(), "staging".to_string());
let config = AgentConfig::new("deploy").pod_labels(labels);
Source

pub fn resume(self, session_id: &str) -> Self

Set a session ID to resume a previous conversation.

Source

pub fn retry_policy(self, policy: RetryPolicy) -> Self

Set a step-level retry policy.

§Examples
use ironflow_core::provider::AgentConfig;
use ironflow_core::retry::RetryPolicy;

let config = AgentConfig::new("Summarize this document")
    .retry_policy(RetryPolicy::new(3));
assert!(config.retry.is_some());
Source

pub fn trace_context(self, ctx: WorkflowTraceContext) -> Self

Attach a WorkflowTraceContext for distributed tracing.

When set, providers can inject the traceparent header into outgoing HTTP requests to correlate workflow spans with downstream service spans.

§Examples
use ironflow_core::provider::AgentConfig;
use ironflow_core::trace_context::WorkflowTraceContext;

let ctx = WorkflowTraceContext::new_root();
let config = AgentConfig::new("classify this")
    .trace_context(ctx);
assert!(config.trace_context.is_some());
Source

pub fn input_file(self, url: &str, mount_path: &str) -> Self

Declare an external input that the provider must materialize on the agent’s filesystem before invocation.

url is fetched (HTTP/HTTPS) and written to mount_path (absolute path) inside the agent’s runtime. Each provider materializes inputs in its own way:

  • Local provider: downloads to a temp dir on the host.
  • K8s providers: spawn a curlimages/curl initContainer that downloads into a shared emptyDir mounted on the main container.

Can be called multiple times to declare several inputs.

§Examples
use ironflow_core::provider::AgentConfig;

let config = AgentConfig::new("Read /work/dossier.pdf and summarize")
    .allow_tool("Read")
    .input_file("https://r2.example.com/dossier.pdf", "/work/dossier.pdf");
Source§

impl<Tools> AgentConfig<Tools, NoSchema>

Source

pub fn allow_tool(self, tool: &str) -> AgentConfig<WithTools, NoSchema>

Add an allowed tool.

Can be called multiple times to allow several tools. Returns an AgentConfig<WithTools, NoSchema>, which cannot call output or output_schema_raw.

This restriction exists because Claude CLI has a known bug where --json-schema combined with --allowedTools always returns structured_output: null.

Workaround: use two sequential agent steps – one with tools to gather data, then one with .output::<T>() to structure the result.

§Examples
use ironflow_core::provider::AgentConfig;

let config = AgentConfig::new("search the web")
    .allow_tool("WebSearch")
    .allow_tool("WebFetch");
use ironflow_core::provider::AgentConfig;
// ERROR: cannot set structured output after adding tools
let _ = AgentConfig::new("x")
    .allow_tool("Read")
    .output_schema_raw(r#"{"type":"object"}"#);
Source§

impl<Schema> AgentConfig<NoTools, Schema>

Source

pub fn output<T: JsonSchema>(self) -> AgentConfig<NoTools, WithSchema>

Set structured output from a Rust type implementing JsonSchema.

The schema is serialized once at build time. When set, the provider will request typed output conforming to this schema.

Important: structured output requires max_turns >= 2.

Returns an AgentConfig<NoTools, WithSchema>, which cannot call allow_tool.

This restriction exists because Claude CLI has a known bug where --json-schema combined with --allowedTools always returns structured_output: null.

Workaround: use two sequential agent steps – one with tools to gather data, then one with .output::<T>() to structure the result.

§Known limitations of Claude CLI structured output

The Claude CLI does not guarantee strict schema conformance for structured output. The following upstream bugs affect the behavior:

  • Schema flattening (anthropics/claude-agent-sdk-python#502): a schema like {"type":"object","properties":{"items":{"type":"array",...}}} may return a bare array instead of the wrapper object. The CLI non-deterministically flattens schemas with a single array field.
  • Non-deterministic wrapping (anthropics/claude-agent-sdk-python#374): the same prompt can produce differently wrapped output across runs.
  • No conformance guarantee (anthropics/claude-code#9058): the CLI does not validate output against the provided JSON schema.

Because of these bugs, ironflow’s provider layer applies multiple fallback strategies when extracting the structured value (see extract_structured_value).

§Examples
use ironflow_core::provider::AgentConfig;
use schemars::JsonSchema;

#[derive(serde::Deserialize, JsonSchema)]
struct Labels { labels: Vec<String> }

let config = AgentConfig::new("classify this text")
    .output::<Labels>();
use ironflow_core::provider::AgentConfig;
use schemars::JsonSchema;
#[derive(serde::Deserialize, JsonSchema)]
struct Out { x: i32 }
// ERROR: cannot add tools after setting structured output
let _ = AgentConfig::new("x").output::<Out>().allow_tool("Read");
§Panics

Panics if the schema generated by schemars cannot be serialized to JSON. This indicates a bug in the type’s JsonSchema derive, not a recoverable runtime error.

Source

pub fn output_schema_raw(self, schema: &str) -> AgentConfig<NoTools, WithSchema>

Set structured output from a pre-serialized JSON Schema string.

Returns an AgentConfig<NoTools, WithSchema>, which cannot call allow_tool. See output for the rationale and workaround.

Trait Implementations§

Source§

impl<Tools: Clone, Schema: Clone> Clone for AgentConfig<Tools, Schema>

Source§

fn clone(&self) -> AgentConfig<Tools, Schema>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<Tools: Debug, Schema: Debug> Debug for AgentConfig<Tools, Schema>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de, Tools, Schema> Deserialize<'de> for AgentConfig<Tools, Schema>

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl From<AgentConfig<NoTools, WithSchema>> for AgentConfig

Source§

fn from(config: AgentConfig<NoTools, WithSchema>) -> Self

Converts to this type from the input type.
Source§

impl From<AgentConfig<WithTools>> for AgentConfig

Source§

fn from(config: AgentConfig<WithTools, NoSchema>) -> Self

Converts to this type from the input type.
Source§

impl<Tools, Schema> Serialize for AgentConfig<Tools, Schema>

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl<Tools, Schema> Freeze for AgentConfig<Tools, Schema>

§

impl<Tools, Schema> RefUnwindSafe for AgentConfig<Tools, Schema>

§

impl<Tools, Schema> Send for AgentConfig<Tools, Schema>

§

impl<Tools, Schema> Sync for AgentConfig<Tools, Schema>

§

impl<Tools, Schema> Unpin for AgentConfig<Tools, Schema>

§

impl<Tools, Schema> UnsafeUnpin for AgentConfig<Tools, Schema>

§

impl<Tools, Schema> UnwindSafe for AgentConfig<Tools, Schema>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more