Skip to main content

claude_code_sdk_rust/
types.rs

1use crate::error::Result;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::future::Future;
5use std::pin::Pin;
6use std::sync::Arc;
7
8pub mod messages;
9pub use messages::*;
10pub mod hooks;
11pub use hooks::*;
12pub mod config;
13pub use config::*;
14pub mod agent_options;
15pub use agent_options::*;
16
17// Enums and String Constants
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(rename_all = "camelCase")]
21pub enum PermissionMode {
22    Default,
23    AcceptEdits,
24    Plan,
25    BypassPermissions,
26    DontAsk,
27    Auto,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
31pub enum SdkBeta {
32    #[serde(rename = "context-1m-2025-08-07")]
33    Context1M20250807,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(rename_all = "camelCase")]
38pub enum SettingSource {
39    User,
40    Project,
41    Local,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
45#[serde(rename_all = "camelCase")]
46pub enum ThinkingConfigType {
47    Adaptive,
48    Enabled,
49    Disabled,
50}
51
52/// Controls how much effort Claude puts into its response.
53///
54/// Mirrors the upstream Python `EffortLevel` literal
55/// (`"low" | "medium" | "high" | "xhigh" | "max"`). Serializes to the bare
56/// lowercase string the CLI expects for `--effort`.
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
58#[serde(rename_all = "lowercase")]
59pub enum EffortLevel {
60    Low,
61    Medium,
62    High,
63    Xhigh,
64    Max,
65}
66
67impl EffortLevel {
68    /// The bare string value passed to the `--effort` CLI flag.
69    pub fn as_cli(&self) -> &'static str {
70        match self {
71            EffortLevel::Low => "low",
72            EffortLevel::Medium => "medium",
73            EffortLevel::High => "high",
74            EffortLevel::Xhigh => "xhigh",
75            EffortLevel::Max => "max",
76        }
77    }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
81#[serde(rename_all = "camelCase")]
82pub enum UserContentKind {
83    Text,
84    Blocks,
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
88#[serde(rename_all = "snake_case")]
89pub enum AssistantMessageErrorKind {
90    AuthenticationFailed,
91    BillingError,
92    RateLimit,
93    InvalidRequest,
94    ServerError,
95    Unknown,
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
99#[serde(rename_all = "snake_case")]
100pub enum TaskNotificationStatus {
101    Completed,
102    Failed,
103    Stopped,
104}
105
106/// Status values reported inside a `task_updated` patch.
107///
108/// `pending`/`running`/`paused` are non-terminal; `completed`/`failed`/`killed`
109/// are terminal. Note `task_updated` reports the raw `killed`; the CLI maps that
110/// to `stopped` only when it emits a `task_notification`.
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
112#[serde(rename_all = "snake_case")]
113pub enum TaskUpdatedStatus {
114    Pending,
115    Running,
116    Paused,
117    Completed,
118    Failed,
119    Killed,
120}
121
122/// Task statuses that mean the task has finished and should be cleared from any
123/// "active task" tracking. Spans both lifecycle vocabularies: `task_notification`
124/// reports `stopped` (the CLI's mapped form of a killed task) while `task_updated`
125/// reports the raw `killed`. Consumers should treat the status of a
126/// `TaskNotificationMessage` and a `TaskUpdatedMessage` the same way.
127pub const TERMINAL_TASK_STATUSES: [&str; 4] = ["completed", "failed", "stopped", "killed"];
128
129/// Returns `true` when `status` is a terminal task status (see
130/// [`TERMINAL_TASK_STATUSES`]).
131pub fn is_terminal_task_status(status: &str) -> bool {
132    TERMINAL_TASK_STATUSES.contains(&status)
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
136#[serde(rename_all = "camelCase")]
137pub enum RateLimitStatus {
138    Allowed,
139    #[serde(alias = "allowed_warning")]
140    AllowedWarning,
141    Rejected,
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
145#[serde(rename_all = "snake_case")]
146pub enum RateLimitType {
147    FiveHour,
148    SevenDay,
149    SevenDayOpus,
150    SevenDaySonnet,
151    Overage,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
155#[serde(rename_all = "kebab-case")]
156pub enum MCPServerConnectionStatus {
157    Connected,
158    Failed,
159    #[serde(rename = "needs-auth")]
160    NeedsAuth,
161    Pending,
162    Disabled,
163}
164
165// MCP Server Config Types (tagged enum pattern)
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
168#[serde(tag = "type", rename_all = "camelCase")]
169pub enum MCPServerConfig {
170    #[serde(rename = "stdio")]
171    Stdio {
172        command: String,
173        #[serde(skip_serializing_if = "Option::is_none")]
174        args: Option<Vec<String>>,
175        #[serde(skip_serializing_if = "Option::is_none")]
176        env: Option<HashMap<String, String>>,
177    },
178    Sse {
179        url: String,
180        #[serde(skip_serializing_if = "Option::is_none")]
181        headers: Option<HashMap<String, String>>,
182    },
183    Http {
184        url: String,
185        #[serde(skip_serializing_if = "Option::is_none")]
186        headers: Option<HashMap<String, String>>,
187    },
188    #[serde(rename = "sdk")]
189    Sdk { name: String },
190}
191
192// Simple Struct Types
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
195#[serde(rename_all = "camelCase")]
196pub struct ToolsPreset {
197    pub r#type: String,
198    pub preset: String,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
202#[serde(rename_all = "camelCase")]
203pub struct SystemPromptPreset {
204    pub r#type: String,
205    pub preset: String,
206    #[serde(skip_serializing_if = "Option::is_none")]
207    pub append: Option<String>,
208    #[serde(skip_serializing_if = "Option::is_none")]
209    pub exclude_dynamic_sections: Option<bool>,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
213#[serde(rename_all = "camelCase")]
214pub struct SystemPromptFile {
215    pub r#type: String,
216    pub path: String,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
220#[serde(rename_all = "camelCase")]
221pub struct ThinkingConfig {
222    pub r#type: ThinkingConfigType,
223    #[serde(skip_serializing_if = "Option::is_none")]
224    pub budget_tokens: Option<i32>,
225    #[serde(skip_serializing_if = "Option::is_none")]
226    pub display: Option<String>,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct TaskBudget {
231    pub total: i32,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
235#[serde(rename_all = "camelCase")]
236pub struct PermissionRuleValue {
237    pub tool_name: String,
238    #[serde(skip_serializing_if = "Option::is_none")]
239    pub rule_content: Option<String>,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
243#[serde(rename_all = "camelCase")]
244pub struct PermissionUpdate {
245    pub r#type: String,
246    #[serde(skip_serializing_if = "Option::is_none")]
247    pub rules: Option<Vec<PermissionRuleValue>>,
248    #[serde(skip_serializing_if = "Option::is_none")]
249    pub behavior: Option<String>,
250    #[serde(skip_serializing_if = "Option::is_none")]
251    pub mode: Option<PermissionMode>,
252    #[serde(skip_serializing_if = "Option::is_none")]
253    pub directories: Option<Vec<String>>,
254    #[serde(skip_serializing_if = "Option::is_none")]
255    pub destination: Option<String>,
256}
257
258#[derive(Debug, Clone, Default)]
259pub struct ToolPermissionContext {
260    pub suggestions: Vec<PermissionUpdate>,
261    pub tool_use_id: Option<String>,
262    pub agent_id: Option<String>,
263    pub blocked_path: Option<String>,
264    pub decision_reason: Option<String>,
265    pub title: Option<String>,
266    pub display_name: Option<String>,
267    pub description: Option<String>,
268}
269
270#[derive(Debug, Clone)]
271pub enum PermissionResult {
272    Allow {
273        updated_input: Option<serde_json::Map<String, serde_json::Value>>,
274        updated_permissions: Option<Vec<PermissionUpdate>>,
275    },
276    Deny {
277        message: String,
278        interrupt: bool,
279    },
280}
281
282impl PermissionResult {
283    pub fn allow() -> Self {
284        Self::Allow {
285            updated_input: None,
286            updated_permissions: None,
287        }
288    }
289
290    pub fn deny(message: impl Into<String>) -> Self {
291        Self::Deny {
292            message: message.into(),
293            interrupt: false,
294        }
295    }
296}
297
298pub type CanUseToolFuture = Pin<Box<dyn Future<Output = Result<PermissionResult>> + Send>>;
299type CanUseToolFn = dyn Fn(
300        String,
301        serde_json::Map<String, serde_json::Value>,
302        ToolPermissionContext,
303    ) -> CanUseToolFuture
304    + Send
305    + Sync;
306
307#[derive(Clone)]
308pub struct CanUseToolCallback(Arc<CanUseToolFn>);
309
310impl std::fmt::Debug for CanUseToolCallback {
311    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
312        f.debug_tuple("CanUseToolCallback")
313            .field(&"<callback>")
314            .finish()
315    }
316}
317
318impl CanUseToolCallback {
319    pub fn new<F, Fut>(callback: F) -> Self
320    where
321        F: Fn(String, serde_json::Map<String, serde_json::Value>, ToolPermissionContext) -> Fut
322            + Send
323            + Sync
324            + 'static,
325        Fut: Future<Output = Result<PermissionResult>> + Send + 'static,
326    {
327        Self(Arc::new(move |tool_name, input, context| {
328            Box::pin(callback(tool_name, input, context))
329        }))
330    }
331
332    pub async fn call(
333        &self,
334        tool_name: String,
335        input: serde_json::Map<String, serde_json::Value>,
336        context: ToolPermissionContext,
337    ) -> Result<PermissionResult> {
338        (self.0)(tool_name, input, context).await
339    }
340}
341
342type StderrFn = dyn Fn(String) + Send + Sync;
343
344#[derive(Clone)]
345pub struct StderrCallback(Arc<StderrFn>);
346
347impl std::fmt::Debug for StderrCallback {
348    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349        f.debug_tuple("StderrCallback")
350            .field(&"<callback>")
351            .finish()
352    }
353}
354
355impl StderrCallback {
356    pub fn new<F>(callback: F) -> Self
357    where
358        F: Fn(String) + Send + Sync + 'static,
359    {
360        Self(Arc::new(callback))
361    }
362
363    pub fn call(&self, line: String) {
364        (self.0)(line);
365    }
366}
367
368#[derive(Debug, Clone, Serialize, Deserialize)]
369#[serde(rename_all = "camelCase")]
370pub struct SDKPluginConfig {
371    pub r#type: String,
372    pub path: String,
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize)]
376#[serde(rename_all = "camelCase")]
377pub struct MCPToolAnnotations {
378    #[serde(skip_serializing_if = "Option::is_none")]
379    pub read_only: Option<bool>,
380    #[serde(skip_serializing_if = "Option::is_none")]
381    pub destructive: Option<bool>,
382    #[serde(skip_serializing_if = "Option::is_none")]
383    pub open_world: Option<bool>,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize)]
387#[serde(rename_all = "camelCase")]
388pub struct MCPToolInfo {
389    pub name: String,
390    #[serde(skip_serializing_if = "Option::is_none")]
391    pub description: Option<String>,
392    #[serde(skip_serializing_if = "Option::is_none")]
393    pub annotations: Option<MCPToolAnnotations>,
394}
395
396#[derive(Debug, Clone, Serialize, Deserialize)]
397#[serde(rename_all = "camelCase")]
398pub struct MCPServerInfo {
399    pub name: String,
400    pub version: String,
401}
402
403#[derive(Debug, Clone, Serialize, Deserialize)]
404#[serde(rename_all = "camelCase")]
405pub struct MCPServerStatus {
406    pub name: String,
407    pub status: MCPServerConnectionStatus,
408    #[serde(skip_serializing_if = "Option::is_none")]
409    pub server_info: Option<MCPServerInfo>,
410    #[serde(skip_serializing_if = "Option::is_none")]
411    pub error: Option<String>,
412    #[serde(skip_serializing_if = "Option::is_none")]
413    pub config: Option<MCPServerStatusConfig>,
414    #[serde(skip_serializing_if = "Option::is_none")]
415    pub scope: Option<String>,
416    #[serde(skip_serializing_if = "Option::is_none")]
417    pub tools: Option<Vec<MCPToolInfo>>,
418}
419
420#[derive(Debug, Clone, Serialize, Deserialize)]
421#[serde(rename_all = "camelCase")]
422pub struct MCPStatusResponse {
423    pub mcp_servers: Vec<MCPServerStatus>,
424}
425
426#[derive(Debug, Clone, Serialize, Deserialize)]
427#[serde(rename_all = "camelCase")]
428pub struct ContextUsageCategory {
429    pub name: String,
430    pub tokens: i32,
431    pub color: String,
432    #[serde(skip_serializing_if = "Option::is_none")]
433    pub is_deferred: Option<bool>,
434}
435
436#[derive(Debug, Clone, Serialize, Deserialize)]
437#[serde(rename_all = "camelCase")]
438pub struct ContextUsageResponse {
439    pub categories: Vec<ContextUsageCategory>,
440    pub total_tokens: i32,
441    pub max_tokens: i32,
442    pub raw_max_tokens: i32,
443    pub percentage: f64,
444    pub model: String,
445    pub is_auto_compact_enabled: bool,
446    pub memory_files: Vec<serde_json::Map<String, serde_json::Value>>,
447    pub mcp_tools: Vec<serde_json::Map<String, serde_json::Value>>,
448    pub agents: Vec<serde_json::Map<String, serde_json::Value>>,
449    pub grid_rows: Vec<Vec<serde_json::Map<String, serde_json::Value>>>,
450    #[serde(skip_serializing_if = "Option::is_none")]
451    pub auto_compact_threshold: Option<i32>,
452    #[serde(skip_serializing_if = "Option::is_none")]
453    pub deferred_builtin_tools: Option<Vec<serde_json::Map<String, serde_json::Value>>>,
454    #[serde(skip_serializing_if = "Option::is_none")]
455    pub system_tools: Option<Vec<serde_json::Map<String, serde_json::Value>>>,
456    #[serde(skip_serializing_if = "Option::is_none")]
457    pub system_prompt_sections: Option<Vec<serde_json::Map<String, serde_json::Value>>>,
458    #[serde(skip_serializing_if = "Option::is_none")]
459    pub slash_commands: Option<serde_json::Value>,
460    #[serde(skip_serializing_if = "Option::is_none")]
461    pub skills: Option<serde_json::Value>,
462}
463
464#[derive(Debug, Clone, Serialize, Deserialize)]
465#[serde(rename_all = "camelCase")]
466pub struct TaskUsage {
467    #[serde(alias = "total_tokens")]
468    pub total_tokens: i32,
469    #[serde(alias = "tool_uses")]
470    pub tool_uses: i32,
471    #[serde(alias = "duration_ms")]
472    pub duration_ms: i32,
473}
474
475#[derive(Debug, Clone, Serialize, Deserialize)]
476#[serde(rename_all = "camelCase")]
477pub struct RateLimitInfo {
478    pub status: RateLimitStatus,
479    #[serde(skip_serializing_if = "Option::is_none")]
480    pub resets_at: Option<i64>,
481    #[serde(skip_serializing_if = "Option::is_none")]
482    pub rate_limit_type: Option<RateLimitType>,
483    #[serde(skip_serializing_if = "Option::is_none")]
484    pub utilization: Option<f64>,
485    #[serde(skip_serializing_if = "Option::is_none")]
486    pub overage_status: Option<RateLimitStatus>,
487    #[serde(skip_serializing_if = "Option::is_none")]
488    pub overage_resets_at: Option<i64>,
489    #[serde(skip_serializing_if = "Option::is_none")]
490    pub overage_disabled_reason: Option<String>,
491    #[serde(skip_serializing_if = "Option::is_none")]
492    pub raw: Option<serde_json::Map<String, serde_json::Value>>,
493}
494
495// MCP Server Status Config (tagged enum)
496
497#[derive(Debug, Clone, Serialize, Deserialize)]
498#[serde(tag = "type", rename_all = "camelCase")]
499pub enum MCPServerStatusConfig {
500    #[serde(rename = "sdk")]
501    Sdk { name: String },
502    #[serde(rename = "claudeai-proxy")]
503    ClaudeAiProxy { url: String, id: String },
504    Sse {
505        url: String,
506        #[serde(skip_serializing_if = "Option::is_none")]
507        headers: Option<HashMap<String, String>>,
508    },
509    Http {
510        url: String,
511        #[serde(skip_serializing_if = "Option::is_none")]
512        headers: Option<HashMap<String, String>>,
513    },
514    #[serde(rename = "stdio")]
515    Stdio {
516        #[serde(skip_serializing_if = "Option::is_none")]
517        command: Option<String>,
518        #[serde(skip_serializing_if = "Option::is_none")]
519        args: Option<Vec<String>>,
520        #[serde(skip_serializing_if = "Option::is_none")]
521        env: Option<HashMap<String, String>>,
522    },
523}