Skip to main content

AgentConfig

Struct AgentConfig 

Source
#[non_exhaustive]
pub struct AgentConfig<Tools = NoTools, Schema = NoSchema> {
Show 15 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, /* 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.

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 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 resume(self, session_id: &str) -> Self

Set a session ID to resume a previous conversation.

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 · 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>
where Tools: RefUnwindSafe, Schema: RefUnwindSafe,

§

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

§

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

§

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

§

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

§

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

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> 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: 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: 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 = Infallible

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
Source§

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