claude_agent_sdk/
types.rs

1//! Type definitions for Claude Agent SDK
2//!
3//! This module contains all the type definitions used throughout the SDK,
4//! including newtypes for type safety, message types, option types, and more.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::future::Future;
9use std::path::PathBuf;
10use std::pin::Pin;
11use std::sync::Arc;
12
13use crate::error::Result;
14
15// ============================================================================
16// Newtype Wrappers for Type Safety
17// ============================================================================
18
19/// Session ID newtype for type safety
20#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
21#[serde(transparent)]
22pub struct SessionId(String);
23
24impl SessionId {
25    /// Create a new session ID
26    pub fn new(id: impl Into<String>) -> Self {
27        Self(id.into())
28    }
29
30    /// Get the session ID as a string slice
31    pub fn as_str(&self) -> &str {
32        &self.0
33    }
34}
35
36impl Default for SessionId {
37    fn default() -> Self {
38        Self("default".to_string())
39    }
40}
41
42impl From<String> for SessionId {
43    fn from(s: String) -> Self {
44        Self(s)
45    }
46}
47
48impl From<&str> for SessionId {
49    fn from(s: &str) -> Self {
50        Self(s.to_string())
51    }
52}
53
54/// Tool name newtype
55#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
56#[serde(transparent)]
57pub struct ToolName(String);
58
59impl ToolName {
60    /// Create a new tool name
61    pub fn new(name: impl Into<String>) -> Self {
62        Self(name.into())
63    }
64
65    /// Get the tool name as a string slice
66    pub fn as_str(&self) -> &str {
67        &self.0
68    }
69}
70
71impl From<String> for ToolName {
72    fn from(s: String) -> Self {
73        Self(s)
74    }
75}
76
77impl From<&str> for ToolName {
78    fn from(s: &str) -> Self {
79        Self(s.to_string())
80    }
81}
82
83/// Request ID newtype for control protocol
84#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
85#[serde(transparent)]
86pub struct RequestId(String);
87
88impl RequestId {
89    /// Create a new request ID
90    pub fn new(id: impl Into<String>) -> Self {
91        Self(id.into())
92    }
93
94    /// Get the request ID as a string slice
95    pub fn as_str(&self) -> &str {
96        &self.0
97    }
98}
99
100impl From<String> for RequestId {
101    fn from(s: String) -> Self {
102        Self(s)
103    }
104}
105
106impl From<&str> for RequestId {
107    fn from(s: &str) -> Self {
108        Self(s.to_string())
109    }
110}
111
112// ============================================================================
113// Permission Types
114// ============================================================================
115
116/// Permission modes for tool execution
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
118#[serde(rename_all = "camelCase")]
119pub enum PermissionMode {
120    /// Default mode - CLI prompts for dangerous tools
121    Default,
122    /// Auto-accept file edits
123    AcceptEdits,
124    /// Plan mode
125    Plan,
126    /// Allow all tools (use with caution)
127    BypassPermissions,
128}
129
130/// Setting source types
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
132#[serde(rename_all = "lowercase")]
133pub enum SettingSource {
134    /// User-level settings
135    User,
136    /// Project-level settings
137    Project,
138    /// Local settings
139    Local,
140}
141
142/// Permission update destination
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
144#[serde(rename_all = "camelCase")]
145pub enum PermissionUpdateDestination {
146    /// Save to user settings
147    UserSettings,
148    /// Save to project settings
149    ProjectSettings,
150    /// Save to local settings
151    LocalSettings,
152    /// Save to session only (temporary)
153    Session,
154}
155
156/// Permission behavior
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
158#[serde(rename_all = "lowercase")]
159pub enum PermissionBehavior {
160    /// Allow the action
161    Allow,
162    /// Deny the action
163    Deny,
164    /// Ask the user
165    Ask,
166}
167
168/// Permission rule value
169#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct PermissionRuleValue {
171    /// Name of the tool
172    pub tool_name: String,
173    /// Optional rule content
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub rule_content: Option<String>,
176}
177
178/// Permission update configuration
179#[derive(Debug, Clone, Serialize, Deserialize)]
180#[serde(tag = "type", rename_all = "camelCase")]
181pub enum PermissionUpdate {
182    /// Add permission rules
183    AddRules {
184        /// Rules to add
185        #[serde(skip_serializing_if = "Option::is_none")]
186        rules: Option<Vec<PermissionRuleValue>>,
187        /// Where to save the rules
188        #[serde(skip_serializing_if = "Option::is_none")]
189        destination: Option<PermissionUpdateDestination>,
190    },
191    /// Replace existing permission rules
192    ReplaceRules {
193        /// New rules
194        #[serde(skip_serializing_if = "Option::is_none")]
195        rules: Option<Vec<PermissionRuleValue>>,
196        /// Where to save the rules
197        #[serde(skip_serializing_if = "Option::is_none")]
198        destination: Option<PermissionUpdateDestination>,
199    },
200    /// Remove permission rules
201    RemoveRules {
202        /// Rules to remove
203        #[serde(skip_serializing_if = "Option::is_none")]
204        rules: Option<Vec<PermissionRuleValue>>,
205        /// Where to remove from
206        #[serde(skip_serializing_if = "Option::is_none")]
207        destination: Option<PermissionUpdateDestination>,
208    },
209    /// Set permission mode
210    SetMode {
211        /// New permission mode
212        mode: PermissionMode,
213        /// Where to save the mode
214        #[serde(skip_serializing_if = "Option::is_none")]
215        destination: Option<PermissionUpdateDestination>,
216    },
217    /// Add directories to allowed list
218    AddDirectories {
219        /// Directories to add
220        #[serde(skip_serializing_if = "Option::is_none")]
221        directories: Option<Vec<String>>,
222        /// Where to save
223        #[serde(skip_serializing_if = "Option::is_none")]
224        destination: Option<PermissionUpdateDestination>,
225    },
226    /// Remove directories from allowed list
227    RemoveDirectories {
228        /// Directories to remove
229        #[serde(skip_serializing_if = "Option::is_none")]
230        directories: Option<Vec<String>>,
231        /// Where to remove from
232        #[serde(skip_serializing_if = "Option::is_none")]
233        destination: Option<PermissionUpdateDestination>,
234    },
235}
236
237/// Context for tool permission callbacks
238#[derive(Debug, Clone, Serialize, Deserialize)]
239pub struct ToolPermissionContext {
240    /// Permission suggestions from CLI
241    pub suggestions: Vec<PermissionUpdate>,
242}
243
244/// Permission request from CLI
245#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct PermissionRequest {
247    /// Tool name being requested
248    pub tool_name: ToolName,
249    /// Tool input parameters
250    pub tool_input: serde_json::Value,
251    /// Permission context
252    pub context: ToolPermissionContext,
253}
254
255/// Permission result for allowing tool use
256#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct PermissionResultAllow {
258    /// Modified input for the tool
259    #[serde(skip_serializing_if = "Option::is_none")]
260    pub updated_input: Option<serde_json::Value>,
261    /// Permission updates to apply
262    #[serde(skip_serializing_if = "Option::is_none")]
263    pub updated_permissions: Option<Vec<PermissionUpdate>>,
264}
265
266/// Permission result for denying tool use
267#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct PermissionResultDeny {
269    /// Reason for denying
270    pub message: String,
271    /// Whether to interrupt the conversation
272    #[serde(default)]
273    pub interrupt: bool,
274}
275
276/// Permission result enum
277#[derive(Debug, Clone, Serialize, Deserialize)]
278#[serde(tag = "type", rename_all = "lowercase")]
279pub enum PermissionResult {
280    /// Allow the tool use
281    Allow(PermissionResultAllow),
282    /// Deny the tool use
283    Deny(PermissionResultDeny),
284}
285
286/// Callback type for tool permission checks
287pub type CanUseToolCallback = Arc<
288    dyn Fn(
289            ToolName,
290            serde_json::Value,
291            ToolPermissionContext,
292        ) -> Pin<Box<dyn Future<Output = Result<PermissionResult>> + Send>>
293        + Send
294        + Sync,
295>;
296
297// ============================================================================
298// Hook Types
299// ============================================================================
300
301/// Hook event types
302#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
303pub enum HookEvent {
304    /// Before a tool is used
305    PreToolUse,
306    /// After a tool is used
307    PostToolUse,
308    /// When user submits a prompt
309    UserPromptSubmit,
310    /// When conversation stops
311    Stop,
312    /// When a subagent stops
313    SubagentStop,
314    /// Before compacting the conversation
315    PreCompact,
316}
317
318/// Hook decision
319#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
320#[serde(rename_all = "lowercase")]
321pub enum HookDecision {
322    /// Block the action
323    Block,
324}
325
326/// Hook output
327#[derive(Debug, Clone, Default, Serialize, Deserialize)]
328pub struct HookOutput {
329    /// Decision to block or allow
330    #[serde(skip_serializing_if = "Option::is_none")]
331    pub decision: Option<HookDecision>,
332    /// System message to add
333    #[serde(skip_serializing_if = "Option::is_none", rename = "systemMessage")]
334    pub system_message: Option<String>,
335    /// Hook-specific output data
336    #[serde(skip_serializing_if = "Option::is_none", rename = "hookSpecificOutput")]
337    pub hook_specific_output: Option<serde_json::Value>,
338}
339
340/// Context for hook callbacks
341#[derive(Debug, Clone)]
342pub struct HookContext {
343    // Future: abort signal support
344}
345
346/// Hook callback type
347pub type HookCallback = Arc<
348    dyn Fn(
349            serde_json::Value,
350            Option<String>,
351            HookContext,
352        ) -> Pin<Box<dyn Future<Output = Result<HookOutput>> + Send>>
353        + Send
354        + Sync,
355>;
356
357/// Hook matcher configuration
358#[derive(Clone)]
359pub struct HookMatcher {
360    /// Matcher pattern (e.g., tool name like "Bash" or pattern like "Write|Edit")
361    pub matcher: Option<String>,
362    /// List of hook callbacks
363    pub hooks: Vec<HookCallback>,
364}
365
366impl std::fmt::Debug for HookMatcher {
367    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
368        f.debug_struct("HookMatcher")
369            .field("matcher", &self.matcher)
370            .field("hooks", &format!("[{} callbacks]", self.hooks.len()))
371            .finish()
372    }
373}
374
375// ============================================================================
376// MCP Server Types
377// ============================================================================
378
379/// MCP stdio server configuration
380#[derive(Debug, Clone, Serialize, Deserialize)]
381pub struct McpStdioServerConfig {
382    /// Server type (stdio)
383    #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
384    pub server_type: Option<String>,
385    /// Command to execute
386    pub command: String,
387    /// Command arguments
388    #[serde(skip_serializing_if = "Option::is_none")]
389    pub args: Option<Vec<String>>,
390    /// Environment variables
391    #[serde(skip_serializing_if = "Option::is_none")]
392    pub env: Option<HashMap<String, String>>,
393}
394
395/// MCP SSE server configuration
396#[derive(Debug, Clone, Serialize, Deserialize)]
397pub struct McpSseServerConfig {
398    /// Server type (sse)
399    #[serde(rename = "type")]
400    pub server_type: String,
401    /// Server URL
402    pub url: String,
403    /// HTTP headers
404    #[serde(skip_serializing_if = "Option::is_none")]
405    pub headers: Option<HashMap<String, String>>,
406}
407
408/// MCP HTTP server configuration
409#[derive(Debug, Clone, Serialize, Deserialize)]
410pub struct McpHttpServerConfig {
411    /// Server type (http)
412    #[serde(rename = "type")]
413    pub server_type: String,
414    /// Server URL
415    pub url: String,
416    /// HTTP headers
417    #[serde(skip_serializing_if = "Option::is_none")]
418    pub headers: Option<HashMap<String, String>>,
419}
420
421/// SDK MCP server marker (not serialized directly)
422#[derive(Debug, Clone)]
423pub struct SdkMcpServerMarker {
424    /// Server name
425    #[allow(dead_code)]
426    pub name: String,
427}
428
429/// MCP server configuration enum
430#[derive(Debug, Clone)]
431pub enum McpServerConfig {
432    /// Stdio-based MCP server
433    Stdio(McpStdioServerConfig),
434    /// SSE-based MCP server
435    Sse(McpSseServerConfig),
436    /// HTTP-based MCP server
437    Http(McpHttpServerConfig),
438    /// SDK-based in-process MCP server
439    Sdk(SdkMcpServerMarker),
440}
441
442/// MCP servers container
443#[derive(Debug, Clone, Default)]
444pub enum McpServers {
445    /// No MCP servers
446    #[default]
447    None,
448    /// Dictionary of MCP servers
449    Dict(HashMap<String, McpServerConfig>),
450    /// Path to MCP servers configuration file
451    Path(PathBuf),
452}
453
454// ============================================================================
455// Message Types
456// ============================================================================
457
458/// Content value for tool results
459#[derive(Debug, Clone, Serialize, Deserialize)]
460#[serde(untagged)]
461pub enum ContentValue {
462    /// String content
463    String(String),
464    /// Structured content blocks
465    Blocks(Vec<serde_json::Value>),
466}
467
468/// Content block types
469#[derive(Debug, Clone, Serialize, Deserialize)]
470#[serde(tag = "type", rename_all = "snake_case")]
471pub enum ContentBlock {
472    /// Text content block
473    Text {
474        /// Text content
475        text: String,
476    },
477    /// Thinking content block (extended thinking)
478    Thinking {
479        /// Thinking content
480        thinking: String,
481        /// Signature for verification
482        signature: String,
483    },
484    /// Tool use request
485    ToolUse {
486        /// Tool use ID
487        id: String,
488        /// Tool name
489        name: String,
490        /// Tool input parameters
491        input: serde_json::Value,
492    },
493    /// Tool execution result
494    ToolResult {
495        /// ID of the tool use this is a result for
496        tool_use_id: String,
497        /// Result content
498        #[serde(skip_serializing_if = "Option::is_none")]
499        content: Option<ContentValue>,
500        /// Whether this is an error result
501        #[serde(skip_serializing_if = "Option::is_none")]
502        is_error: Option<bool>,
503    },
504}
505
506/// User message content
507#[derive(Debug, Clone, Serialize, Deserialize)]
508pub struct UserMessageContent {
509    /// Message role (always "user")
510    pub role: String,
511    /// Message content
512    #[serde(skip_serializing_if = "Option::is_none")]
513    pub content: Option<UserContent>,
514}
515
516/// User content can be string or blocks
517#[derive(Debug, Clone, Serialize, Deserialize)]
518#[serde(untagged)]
519pub enum UserContent {
520    /// Plain string content
521    String(String),
522    /// Structured content blocks
523    Blocks(Vec<ContentBlock>),
524}
525
526/// Assistant message content
527#[derive(Debug, Clone, Serialize, Deserialize)]
528pub struct AssistantMessageContent {
529    /// Model that generated the message
530    pub model: String,
531    /// Message content blocks
532    pub content: Vec<ContentBlock>,
533}
534
535/// Message types
536#[derive(Debug, Clone, Serialize, Deserialize)]
537#[serde(tag = "type", rename_all = "snake_case")]
538pub enum Message {
539    /// User message
540    User {
541        /// Parent tool use ID for nested conversations
542        #[serde(skip_serializing_if = "Option::is_none")]
543        parent_tool_use_id: Option<String>,
544        /// Message content
545        message: UserMessageContent,
546        /// Session ID
547        #[serde(skip_serializing_if = "Option::is_none")]
548        session_id: Option<SessionId>,
549    },
550    /// Assistant message
551    Assistant {
552        /// Parent tool use ID for nested conversations
553        #[serde(skip_serializing_if = "Option::is_none")]
554        parent_tool_use_id: Option<String>,
555        /// Message content
556        message: AssistantMessageContent,
557        /// Session ID
558        #[serde(skip_serializing_if = "Option::is_none")]
559        session_id: Option<SessionId>,
560    },
561    /// System message
562    System {
563        /// System message subtype
564        subtype: String,
565        /// Additional system message data
566        #[serde(flatten)]
567        data: serde_json::Value,
568    },
569    /// Result message with metrics
570    Result {
571        /// Result subtype
572        subtype: String,
573        /// Total duration in milliseconds
574        duration_ms: u64,
575        /// API call duration in milliseconds
576        duration_api_ms: u64,
577        /// Whether this is an error result
578        is_error: bool,
579        /// Number of conversation turns
580        num_turns: u32,
581        /// Session ID
582        session_id: SessionId,
583        /// Total cost in USD
584        #[serde(skip_serializing_if = "Option::is_none")]
585        total_cost_usd: Option<f64>,
586        /// Token usage statistics
587        #[serde(skip_serializing_if = "Option::is_none")]
588        usage: Option<serde_json::Value>,
589        /// Result message
590        #[serde(skip_serializing_if = "Option::is_none")]
591        result: Option<String>,
592    },
593    /// Stream event for partial messages
594    StreamEvent {
595        /// Event UUID
596        uuid: String,
597        /// Session ID
598        session_id: SessionId,
599        /// Raw stream event data
600        event: serde_json::Value,
601        /// Parent tool use ID
602        #[serde(skip_serializing_if = "Option::is_none")]
603        parent_tool_use_id: Option<String>,
604    },
605}
606
607// ============================================================================
608// System Prompt Types
609// ============================================================================
610
611/// System prompt preset
612#[derive(Debug, Clone, Serialize, Deserialize)]
613pub struct SystemPromptPreset {
614    /// Prompt type (always "preset")
615    #[serde(rename = "type")]
616    pub prompt_type: String,
617    /// Preset name (e.g., "claude_code")
618    pub preset: String,
619    /// Additional text to append to the preset
620    #[serde(skip_serializing_if = "Option::is_none")]
621    pub append: Option<String>,
622}
623
624/// System prompt configuration
625#[derive(Debug, Clone)]
626pub enum SystemPrompt {
627    /// Plain string system prompt
628    String(String),
629    /// Preset-based system prompt
630    Preset(SystemPromptPreset),
631}
632
633// ============================================================================
634// Agent Definition
635// ============================================================================
636
637/// Agent definition configuration
638#[derive(Debug, Clone, Serialize, Deserialize)]
639pub struct AgentDefinition {
640    /// Agent description
641    pub description: String,
642    /// Agent system prompt
643    pub prompt: String,
644    /// Tools available to the agent
645    #[serde(skip_serializing_if = "Option::is_none")]
646    pub tools: Option<Vec<String>>,
647    /// Model to use for the agent
648    #[serde(skip_serializing_if = "Option::is_none")]
649    pub model: Option<String>,
650}
651
652// ============================================================================
653// Claude Agent Options
654// ============================================================================
655
656/// Main options for Claude Agent SDK
657#[derive(Clone, Default)]
658pub struct ClaudeAgentOptions {
659    /// List of tools that Claude is allowed to use
660    pub allowed_tools: Vec<ToolName>,
661    /// System prompt configuration
662    pub system_prompt: Option<SystemPrompt>,
663    /// MCP server configurations
664    pub mcp_servers: McpServers,
665    /// Permission mode for tool execution
666    pub permission_mode: Option<PermissionMode>,
667    /// Whether to continue from the previous conversation
668    pub continue_conversation: bool,
669    /// Session ID to resume from
670    pub resume: Option<SessionId>,
671    /// Maximum number of turns before stopping
672    pub max_turns: Option<u32>,
673    /// List of tools that Claude is not allowed to use
674    pub disallowed_tools: Vec<ToolName>,
675    /// AI model to use
676    pub model: Option<String>,
677    /// Tool name to use for permission prompts
678    pub permission_prompt_tool_name: Option<String>,
679    /// Working directory for the CLI process
680    pub cwd: Option<PathBuf>,
681    /// Path to settings file
682    pub settings: Option<PathBuf>,
683    /// Additional directories to add to the context
684    pub add_dirs: Vec<PathBuf>,
685    /// Environment variables for the CLI process
686    pub env: HashMap<String, String>,
687    /// Extra CLI arguments to pass
688    pub extra_args: HashMap<String, Option<String>>,
689    /// Maximum buffer size for JSON messages (default: 1MB)
690    pub max_buffer_size: Option<usize>,
691    /// Callback for tool permission checks
692    pub can_use_tool: Option<CanUseToolCallback>,
693    /// Hook configurations
694    pub hooks: Option<HashMap<HookEvent, Vec<HookMatcher>>>,
695    /// User identifier
696    pub user: Option<String>,
697    /// Whether to include partial messages in stream
698    pub include_partial_messages: bool,
699    /// Whether to fork the session when resuming
700    pub fork_session: bool,
701    /// Custom agent definitions
702    pub agents: Option<HashMap<String, AgentDefinition>>,
703    /// Setting sources to load
704    pub setting_sources: Option<Vec<SettingSource>>,
705}
706
707impl ClaudeAgentOptions {
708    /// Create a new builder for ClaudeAgentOptions
709    pub fn builder() -> ClaudeAgentOptionsBuilder {
710        ClaudeAgentOptionsBuilder::default()
711    }
712}
713
714impl std::fmt::Debug for ClaudeAgentOptions {
715    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
716        f.debug_struct("ClaudeAgentOptions")
717            .field("allowed_tools", &self.allowed_tools)
718            .field("system_prompt", &self.system_prompt)
719            .field("mcp_servers", &self.mcp_servers)
720            .field("permission_mode", &self.permission_mode)
721            .field("continue_conversation", &self.continue_conversation)
722            .field("resume", &self.resume)
723            .field("max_turns", &self.max_turns)
724            .field("disallowed_tools", &self.disallowed_tools)
725            .field("model", &self.model)
726            .field(
727                "permission_prompt_tool_name",
728                &self.permission_prompt_tool_name,
729            )
730            .field("cwd", &self.cwd)
731            .field("settings", &self.settings)
732            .field("add_dirs", &self.add_dirs)
733            .field("env", &self.env)
734            .field("extra_args", &self.extra_args)
735            .field("max_buffer_size", &self.max_buffer_size)
736            .field(
737                "can_use_tool",
738                &self.can_use_tool.as_ref().map(|_| "<callback>"),
739            )
740            .field(
741                "hooks",
742                &self
743                    .hooks
744                    .as_ref()
745                    .map(|h| format!("[{} hook types]", h.len())),
746            )
747            .field("user", &self.user)
748            .field("include_partial_messages", &self.include_partial_messages)
749            .field("fork_session", &self.fork_session)
750            .field("agents", &self.agents)
751            .field("setting_sources", &self.setting_sources)
752            .finish()
753    }
754}
755
756// ============================================================================
757// Builder for ClaudeAgentOptions
758// ============================================================================
759
760/// Builder for ClaudeAgentOptions
761#[derive(Debug, Default)]
762pub struct ClaudeAgentOptionsBuilder {
763    options: ClaudeAgentOptions,
764}
765
766impl ClaudeAgentOptionsBuilder {
767    /// Set allowed tools
768    pub fn allowed_tools(mut self, tools: Vec<impl Into<ToolName>>) -> Self {
769        self.options.allowed_tools = tools.into_iter().map(|t| t.into()).collect();
770        self
771    }
772
773    /// Add an allowed tool
774    pub fn add_allowed_tool(mut self, tool: impl Into<ToolName>) -> Self {
775        self.options.allowed_tools.push(tool.into());
776        self
777    }
778
779    /// Set system prompt
780    pub fn system_prompt(mut self, prompt: impl Into<SystemPrompt>) -> Self {
781        self.options.system_prompt = Some(prompt.into());
782        self
783    }
784
785    /// Set MCP servers
786    pub fn mcp_servers(mut self, servers: HashMap<String, McpServerConfig>) -> Self {
787        self.options.mcp_servers = McpServers::Dict(servers);
788        self
789    }
790
791    /// Set permission mode
792    pub fn permission_mode(mut self, mode: PermissionMode) -> Self {
793        self.options.permission_mode = Some(mode);
794        self
795    }
796
797    /// Set max turns
798    pub fn max_turns(mut self, turns: u32) -> Self {
799        const MAX_ALLOWED_TURNS: u32 = 1000;
800        if turns > MAX_ALLOWED_TURNS {
801            panic!(
802                "max_turns {turns} exceeds maximum allowed: {MAX_ALLOWED_TURNS}"
803            );
804        }
805        self.options.max_turns = Some(turns);
806        self
807    }
808
809    /// Set working directory
810    pub fn cwd(mut self, path: impl Into<PathBuf>) -> Self {
811        self.options.cwd = Some(path.into());
812        self
813    }
814
815    /// Set can_use_tool callback
816    pub fn can_use_tool(mut self, callback: CanUseToolCallback) -> Self {
817        self.options.can_use_tool = Some(callback);
818        self
819    }
820
821    /// Set hooks
822    pub fn hooks(mut self, hooks: HashMap<HookEvent, Vec<HookMatcher>>) -> Self {
823        self.options.hooks = Some(hooks);
824        self
825    }
826
827    /// Build the options
828    pub fn build(self) -> ClaudeAgentOptions {
829        self.options
830    }
831}
832
833// Implement conversions for SystemPrompt
834impl From<String> for SystemPrompt {
835    fn from(s: String) -> Self {
836        SystemPrompt::String(s)
837    }
838}
839
840impl From<&str> for SystemPrompt {
841    fn from(s: &str) -> Self {
842        SystemPrompt::String(s.to_string())
843    }
844}
845
846impl From<SystemPromptPreset> for SystemPrompt {
847    fn from(preset: SystemPromptPreset) -> Self {
848        SystemPrompt::Preset(preset)
849    }
850}