1use std::collections::HashMap;
7
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use validator::Validate;
11
12use crate::validated::Normalizable;
13
14#[serde_with::skip_serializing_none]
22#[derive(Debug, Clone, Serialize, Deserialize, Validate, schemars::JsonSchema)]
23#[validate(schema(function = "validate_mcp_config"))]
24pub struct CreateMessageRequest {
25 #[validate(length(min = 1, message = "model field is required and cannot be empty"))]
27 pub model: String,
28
29 #[validate(length(min = 1, message = "messages array is required and cannot be empty"))]
31 pub messages: Vec<InputMessage>,
32
33 #[validate(range(min = 1, message = "max_tokens must be greater than 0"))]
35 pub max_tokens: u32,
36
37 pub metadata: Option<Metadata>,
39
40 pub service_tier: Option<ServiceTier>,
42
43 pub stop_sequences: Option<Vec<String>>,
45
46 pub stream: Option<bool>,
48
49 pub system: Option<SystemContent>,
51
52 pub temperature: Option<f64>,
54
55 pub thinking: Option<ThinkingConfig>,
57
58 pub tool_choice: Option<ToolChoice>,
60
61 pub tools: Option<Vec<Tool>>,
63
64 pub top_k: Option<u32>,
66
67 pub top_p: Option<f64>,
69
70 pub container: Option<ContainerConfig>,
73
74 pub mcp_servers: Option<Vec<McpServerConfig>>,
76}
77
78impl Normalizable for CreateMessageRequest {
79 }
81
82impl CreateMessageRequest {
83 pub fn is_stream(&self) -> bool {
85 self.stream.unwrap_or(false)
86 }
87
88 pub fn get_model(&self) -> &str {
90 &self.model
91 }
92
93 pub fn has_mcp_toolset(&self) -> bool {
95 self.tools
96 .as_ref()
97 .is_some_and(|tools| tools.iter().any(|t| matches!(t, Tool::McpToolset(_))))
98 }
99
100 pub fn mcp_server_configs(&self) -> Option<&[McpServerConfig]> {
102 self.mcp_servers
103 .as_deref()
104 .filter(|servers| !servers.is_empty())
105 }
106}
107
108fn validate_mcp_config(req: &CreateMessageRequest) -> Result<(), validator::ValidationError> {
110 if req.has_mcp_toolset() && req.mcp_server_configs().is_none() {
111 let mut e = validator::ValidationError::new("mcp_servers_required");
112 e.message = Some("mcp_servers is required when mcp_toolset tools are present".into());
113 return Err(e);
114 }
115 Ok(())
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
120pub struct Metadata {
121 #[serde(skip_serializing_if = "Option::is_none")]
123 pub user_id: Option<String>,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
128#[serde(rename_all = "snake_case")]
129pub enum ServiceTier {
130 Auto,
131 StandardOnly,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
136#[serde(untagged)]
137pub enum SystemContent {
138 String(String),
139 Blocks(Vec<TextBlock>),
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
144pub struct InputMessage {
145 pub role: Role,
147
148 pub content: InputContent,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)]
154#[serde(rename_all = "lowercase")]
155pub enum Role {
156 User,
157 Assistant,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
162#[serde(untagged)]
163pub enum InputContent {
164 String(String),
165 Blocks(Vec<InputContentBlock>),
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
174#[serde(tag = "type", rename_all = "snake_case")]
175pub enum InputContentBlock {
176 Text(TextBlock),
178 Image(ImageBlock),
180 Document(DocumentBlock),
182 ToolUse(ToolUseBlock),
184 ToolResult(ToolResultBlock),
186 Thinking(ThinkingBlock),
188 RedactedThinking(RedactedThinkingBlock),
190 ServerToolUse(ServerToolUseBlock),
192 SearchResult(SearchResultBlock),
194 WebSearchToolResult(WebSearchToolResultBlock),
196 ToolSearchToolResult(ToolSearchToolResultBlock),
198 ToolReference(ToolReferenceBlock),
200}
201
202#[serde_with::skip_serializing_none]
204#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
205pub struct TextBlock {
206 pub text: String,
208
209 pub cache_control: Option<CacheControl>,
211
212 pub citations: Option<Vec<Citation>>,
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
218pub struct ImageBlock {
219 pub source: ImageSource,
221
222 #[serde(skip_serializing_if = "Option::is_none")]
224 pub cache_control: Option<CacheControl>,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
229#[serde(tag = "type", rename_all = "snake_case")]
230pub enum ImageSource {
231 Base64 { media_type: String, data: String },
232 Url { url: String },
233}
234
235#[serde_with::skip_serializing_none]
237#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
238pub struct DocumentBlock {
239 pub source: DocumentSource,
241
242 pub cache_control: Option<CacheControl>,
244
245 pub title: Option<String>,
247
248 pub context: Option<String>,
250
251 pub citations: Option<CitationsConfig>,
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
257#[serde(tag = "type", rename_all = "snake_case")]
258pub enum DocumentSource {
259 Base64 { media_type: String, data: String },
260 Text { data: String },
261 Url { url: String },
262 Content { content: Vec<InputContentBlock> },
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
267pub struct ToolUseBlock {
268 pub id: String,
270
271 pub name: String,
273
274 pub input: Value,
276
277 #[serde(skip_serializing_if = "Option::is_none")]
279 pub cache_control: Option<CacheControl>,
280}
281
282#[serde_with::skip_serializing_none]
284#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
285pub struct ToolResultBlock {
286 pub tool_use_id: String,
288
289 pub content: Option<ToolResultContent>,
291
292 pub is_error: Option<bool>,
294
295 pub cache_control: Option<CacheControl>,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
301#[serde(untagged)]
302pub enum ToolResultContent {
303 String(String),
304 Blocks(Vec<ToolResultContentBlock>),
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
309#[serde(tag = "type", rename_all = "snake_case")]
310pub enum ToolResultContentBlock {
311 Text(TextBlock),
312 Image(ImageBlock),
313 Document(DocumentBlock),
314 SearchResult(SearchResultBlock),
315}
316
317#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
319pub struct ThinkingBlock {
320 pub thinking: String,
322
323 pub signature: String,
325}
326
327#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
329pub struct RedactedThinkingBlock {
330 pub data: String,
332}
333
334#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
336pub struct ServerToolUseBlock {
337 pub id: String,
339
340 pub name: String,
342
343 pub input: Value,
345
346 #[serde(skip_serializing_if = "Option::is_none")]
348 pub cache_control: Option<CacheControl>,
349}
350
351#[serde_with::skip_serializing_none]
353#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
354pub struct SearchResultBlock {
355 pub source: String,
357
358 pub title: String,
360
361 pub content: Vec<TextBlock>,
363
364 pub cache_control: Option<CacheControl>,
366
367 pub citations: Option<CitationsConfig>,
369}
370
371#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
373pub struct WebSearchToolResultBlock {
374 pub tool_use_id: String,
376
377 pub content: WebSearchToolResultContent,
379
380 #[serde(skip_serializing_if = "Option::is_none")]
382 pub cache_control: Option<CacheControl>,
383}
384
385#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
387#[serde(untagged)]
388pub enum WebSearchToolResultContent {
389 Results(Vec<WebSearchResultBlock>),
390 Error(WebSearchToolResultError),
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
395pub struct WebSearchResultBlock {
396 pub title: String,
398
399 pub url: String,
401
402 pub encrypted_content: String,
404
405 #[serde(skip_serializing_if = "Option::is_none")]
407 pub page_age: Option<String>,
408}
409
410#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
412pub struct WebSearchToolResultError {
413 #[serde(rename = "type")]
414 pub error_type: String,
415 pub error_code: WebSearchToolResultErrorCode,
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
420#[serde(rename_all = "snake_case")]
421pub enum WebSearchToolResultErrorCode {
422 InvalidToolInput,
423 Unavailable,
424 MaxUsesExceeded,
425 TooManyRequests,
426 QueryTooLong,
427}
428
429#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
431#[serde(tag = "type", rename_all = "snake_case")]
432pub enum CacheControl {
433 Ephemeral,
434}
435
436#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
438pub struct CitationsConfig {
439 #[serde(skip_serializing_if = "Option::is_none")]
440 pub enabled: Option<bool>,
441}
442
443#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
445#[serde(tag = "type", rename_all = "snake_case")]
446#[expect(
447 clippy::enum_variant_names,
448 reason = "variant names match the OpenAI API citation type discriminators (char_location, page_location, etc.)"
449)]
450pub enum Citation {
451 CharLocation(CharLocationCitation),
452 PageLocation(PageLocationCitation),
453 ContentBlockLocation(ContentBlockLocationCitation),
454 WebSearchResultLocation(WebSearchResultLocationCitation),
455 SearchResultLocation(SearchResultLocationCitation),
456}
457
458#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
460pub struct CharLocationCitation {
461 pub cited_text: String,
462 pub document_index: u32,
463 pub document_title: Option<String>,
464 pub start_char_index: u32,
465 pub end_char_index: u32,
466 #[serde(skip_serializing_if = "Option::is_none")]
467 pub file_id: Option<String>,
468}
469
470#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
472pub struct PageLocationCitation {
473 pub cited_text: String,
474 pub document_index: u32,
475 pub document_title: Option<String>,
476 pub start_page_number: u32,
477 pub end_page_number: u32,
478}
479
480#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
482pub struct ContentBlockLocationCitation {
483 pub cited_text: String,
484 pub document_index: u32,
485 pub document_title: Option<String>,
486 pub start_block_index: u32,
487 pub end_block_index: u32,
488}
489
490#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
492pub struct WebSearchResultLocationCitation {
493 pub cited_text: String,
494 pub url: String,
495 pub title: Option<String>,
496 pub encrypted_index: String,
497}
498
499#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
501pub struct SearchResultLocationCitation {
502 pub cited_text: String,
503 pub search_result_index: u32,
504 pub source: String,
505 pub title: Option<String>,
506 pub start_block_index: u32,
507 pub end_block_index: u32,
508}
509
510#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
516#[serde(untagged)]
517#[expect(
518 clippy::enum_variant_names,
519 reason = "ToolSearch matches Anthropic API naming"
520)]
521#[schemars(rename = "MessagesTool")]
522pub enum Tool {
523 McpToolset(McpToolset),
525 Custom(CustomTool),
530 ToolSearch(ToolSearchTool),
532 Bash(BashTool),
534 TextEditor(TextEditorTool),
536 WebSearch(WebSearchTool),
538}
539
540#[serde_with::skip_serializing_none]
542#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
543pub struct CustomTool {
544 pub name: String,
546
547 #[serde(rename = "type")]
549 pub tool_type: Option<String>,
550
551 pub description: Option<String>,
553
554 pub input_schema: InputSchema,
556
557 pub defer_loading: Option<bool>,
559
560 pub cache_control: Option<CacheControl>,
562}
563
564#[serde_with::skip_serializing_none]
566#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
567pub struct InputSchema {
568 #[serde(rename = "type")]
569 pub schema_type: String,
570
571 pub properties: Option<HashMap<String, Value>>,
572
573 pub required: Option<Vec<String>>,
574
575 #[serde(flatten)]
577 pub additional: HashMap<String, Value>,
578}
579
580#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
582pub struct BashTool {
583 #[serde(rename = "type")]
584 pub tool_type: String, pub name: String, #[serde(skip_serializing_if = "Option::is_none")]
589 pub cache_control: Option<CacheControl>,
590}
591
592#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
594pub struct TextEditorTool {
595 #[serde(rename = "type")]
596 pub tool_type: String, pub name: String, #[serde(skip_serializing_if = "Option::is_none")]
601 pub cache_control: Option<CacheControl>,
602}
603
604#[serde_with::skip_serializing_none]
606#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
607pub struct WebSearchTool {
608 #[serde(rename = "type")]
609 pub tool_type: String, pub name: String, pub allowed_domains: Option<Vec<String>>,
614
615 pub blocked_domains: Option<Vec<String>>,
616
617 pub max_uses: Option<u32>,
618
619 pub user_location: Option<UserLocation>,
620
621 pub cache_control: Option<CacheControl>,
622}
623
624#[serde_with::skip_serializing_none]
626#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
627pub struct UserLocation {
628 #[serde(rename = "type")]
629 pub location_type: String, pub city: Option<String>,
632
633 pub region: Option<String>,
634
635 pub country: Option<String>,
636
637 pub timezone: Option<String>,
638}
639
640#[serde_with::skip_serializing_none]
646#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
647#[serde(tag = "type", rename_all = "snake_case")]
648#[schemars(rename = "MessagesToolChoice")]
649pub enum ToolChoice {
650 Auto {
652 disable_parallel_tool_use: Option<bool>,
653 },
654 Any {
656 disable_parallel_tool_use: Option<bool>,
657 },
658 Tool {
660 name: String,
661 disable_parallel_tool_use: Option<bool>,
662 },
663 None,
665}
666
667#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
673#[serde(tag = "type", rename_all = "snake_case")]
674pub enum ThinkingConfig {
675 Enabled {
677 budget_tokens: u32,
679 },
680 Disabled,
682}
683
684#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
690pub struct Message {
691 pub id: String,
693
694 #[serde(rename = "type")]
696 pub message_type: String,
697
698 pub role: String,
700
701 pub content: Vec<ContentBlock>,
703
704 pub model: String,
706
707 pub stop_reason: Option<StopReason>,
709
710 pub stop_sequence: Option<String>,
712
713 pub usage: Usage,
715}
716
717#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
719#[serde(tag = "type", rename_all = "snake_case")]
720pub enum ContentBlock {
721 Text {
723 text: String,
724 #[serde(skip_serializing_if = "Option::is_none")]
725 citations: Option<Vec<Citation>>,
726 },
727 ToolUse {
729 id: String,
730 name: String,
731 input: Value,
732 },
733 Thinking { thinking: String, signature: String },
735 RedactedThinking { data: String },
737 ServerToolUse {
739 id: String,
740 name: String,
741 input: Value,
742 },
743 WebSearchToolResult {
745 tool_use_id: String,
746 content: WebSearchToolResultContent,
747 },
748 ToolSearchToolResult {
750 tool_use_id: String,
751 content: ToolSearchResultContent,
752 },
753 ToolReference {
755 tool_name: String,
756 #[serde(skip_serializing_if = "Option::is_none")]
757 description: Option<String>,
758 },
759 McpToolUse {
761 id: String,
762 name: String,
763 server_name: String,
764 input: Value,
765 },
766 McpToolResult {
768 tool_use_id: String,
769 content: Option<ToolResultContent>,
770 is_error: Option<bool>,
771 },
772}
773
774#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)]
776#[serde(rename_all = "snake_case")]
777pub enum StopReason {
778 EndTurn,
780 MaxTokens,
782 StopSequence,
784 ToolUse,
786 PauseTurn,
788 Refusal,
790}
791
792#[serde_with::skip_serializing_none]
794#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
795#[schemars(rename = "MessagesUsage")]
796pub struct Usage {
797 pub input_tokens: u32,
799
800 pub output_tokens: u32,
802
803 pub cache_creation_input_tokens: Option<u32>,
805
806 pub cache_read_input_tokens: Option<u32>,
808
809 pub cache_creation: Option<CacheCreation>,
811
812 pub server_tool_use: Option<ServerToolUsage>,
814
815 pub service_tier: Option<String>,
817}
818
819#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
821pub struct CacheCreation {
822 #[serde(flatten)]
823 pub tokens_by_ttl: HashMap<String, u32>,
824}
825
826#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
828pub struct ServerToolUsage {
829 pub web_search_requests: u32,
830}
831
832#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
838#[serde(tag = "type", rename_all = "snake_case")]
839pub enum MessageStreamEvent {
840 MessageStart { message: Message },
842 MessageDelta {
844 delta: MessageDelta,
845 usage: MessageDeltaUsage,
846 },
847 MessageStop,
849 ContentBlockStart {
851 index: u32,
852 content_block: ContentBlock,
853 },
854 ContentBlockDelta {
856 index: u32,
857 delta: ContentBlockDelta,
858 },
859 ContentBlockStop { index: u32 },
861 Ping,
863 Error { error: ErrorResponse },
865}
866
867#[serde_with::skip_serializing_none]
869#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
870pub struct MessageDelta {
871 pub stop_reason: Option<StopReason>,
872
873 pub stop_sequence: Option<String>,
874}
875
876#[serde_with::skip_serializing_none]
878#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
879pub struct MessageDeltaUsage {
880 pub output_tokens: u32,
881
882 pub input_tokens: Option<u32>,
883
884 pub cache_creation_input_tokens: Option<u32>,
885
886 pub cache_read_input_tokens: Option<u32>,
887
888 pub server_tool_use: Option<ServerToolUsage>,
889}
890
891#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
893#[serde(tag = "type", rename_all = "snake_case")]
894#[expect(
895 clippy::enum_variant_names,
896 reason = "variant names match the OpenAI/Anthropic streaming delta type discriminators (text_delta, input_json_delta, etc.)"
897)]
898pub enum ContentBlockDelta {
899 TextDelta { text: String },
901 InputJsonDelta { partial_json: String },
903 ThinkingDelta { thinking: String },
905 SignatureDelta { signature: String },
907 CitationsDelta { citation: Citation },
909}
910
911#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
917#[schemars(rename = "MessagesErrorResponse")]
918pub struct ErrorResponse {
919 #[serde(rename = "type")]
920 pub error_type: String,
921
922 pub message: String,
923}
924
925#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
927#[serde(tag = "type", rename_all = "snake_case")]
928#[expect(
929 clippy::enum_variant_names,
930 reason = "variant names match the OpenAI API error type discriminators (invalid_request_error, authentication_error, etc.)"
931)]
932pub enum ApiError {
933 InvalidRequestError { message: String },
934 AuthenticationError { message: String },
935 BillingError { message: String },
936 PermissionError { message: String },
937 NotFoundError { message: String },
938 RateLimitError { message: String },
939 TimeoutError { message: String },
940 ApiError { message: String },
941 OverloadedError { message: String },
942}
943
944#[serde_with::skip_serializing_none]
950#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
951pub struct CountMessageTokensRequest {
952 pub model: String,
954
955 pub messages: Vec<InputMessage>,
957
958 pub system: Option<SystemContent>,
960
961 pub thinking: Option<ThinkingConfig>,
963
964 pub tool_choice: Option<ToolChoice>,
966
967 pub tools: Option<Vec<Tool>>,
969}
970
971#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
973pub struct CountMessageTokensResponse {
974 pub input_tokens: u32,
975}
976
977#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
983pub struct ModelInfo {
984 #[serde(rename = "type")]
986 pub model_type: String,
987
988 pub id: String,
990
991 pub display_name: String,
993
994 pub created_at: String,
996}
997
998#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1000pub struct ListModelsResponse {
1001 pub data: Vec<ModelInfo>,
1002 pub has_more: bool,
1003 pub first_id: Option<String>,
1004 pub last_id: Option<String>,
1005}
1006
1007#[serde_with::skip_serializing_none]
1013#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1014pub struct ContainerConfig {
1015 pub id: Option<String>,
1017
1018 pub skills: Option<Vec<String>>,
1020}
1021
1022#[serde_with::skip_serializing_none]
1024#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1025pub struct McpServerConfig {
1026 #[serde(rename = "type", default = "McpServerConfig::default_type")]
1028 pub server_type: String,
1029
1030 pub name: String,
1032
1033 pub url: String,
1035
1036 pub authorization_token: Option<String>,
1038
1039 pub tool_configuration: Option<McpToolConfiguration>,
1041}
1042
1043impl McpServerConfig {
1044 fn default_type() -> String {
1045 "url".to_string()
1046 }
1047}
1048
1049#[serde_with::skip_serializing_none]
1051#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1052pub struct McpToolConfiguration {
1053 pub enabled: Option<bool>,
1055
1056 pub allowed_tools: Option<Vec<String>>,
1058}
1059
1060#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1066pub struct McpToolUseBlock {
1067 pub id: String,
1069
1070 pub name: String,
1072
1073 pub server_name: String,
1075
1076 pub input: Value,
1078
1079 #[serde(skip_serializing_if = "Option::is_none")]
1081 pub cache_control: Option<CacheControl>,
1082}
1083
1084#[serde_with::skip_serializing_none]
1086#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1087pub struct McpToolResultBlock {
1088 pub tool_use_id: String,
1090
1091 pub content: Option<ToolResultContent>,
1093
1094 pub is_error: Option<bool>,
1096
1097 pub cache_control: Option<CacheControl>,
1099}
1100
1101#[serde_with::skip_serializing_none]
1103#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1104pub struct McpToolset {
1105 #[serde(rename = "type")]
1106 pub toolset_type: String, pub mcp_server_name: String,
1110
1111 pub default_config: Option<McpToolDefaultConfig>,
1113
1114 pub configs: Option<HashMap<String, McpToolConfig>>,
1116
1117 pub cache_control: Option<CacheControl>,
1119}
1120
1121#[serde_with::skip_serializing_none]
1123#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1124pub struct McpToolDefaultConfig {
1125 pub enabled: Option<bool>,
1127
1128 pub defer_loading: Option<bool>,
1130}
1131
1132#[serde_with::skip_serializing_none]
1134#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1135pub struct McpToolConfig {
1136 pub enabled: Option<bool>,
1138
1139 pub defer_loading: Option<bool>,
1141}
1142
1143#[serde_with::skip_serializing_none]
1149#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1150pub struct CodeExecutionTool {
1151 #[serde(rename = "type")]
1152 pub tool_type: String, pub name: String, pub allowed_callers: Option<Vec<String>>,
1158
1159 pub defer_loading: Option<bool>,
1161
1162 pub strict: Option<bool>,
1164
1165 pub cache_control: Option<CacheControl>,
1167}
1168
1169#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1171pub struct CodeExecutionResultBlock {
1172 pub stdout: String,
1174
1175 pub stderr: String,
1177
1178 pub return_code: i32,
1180
1181 pub content: Vec<CodeExecutionOutputBlock>,
1183}
1184
1185#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1187pub struct CodeExecutionOutputBlock {
1188 #[serde(rename = "type")]
1189 pub block_type: String, pub file_id: String,
1193}
1194
1195#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1197pub struct CodeExecutionToolResultBlock {
1198 pub tool_use_id: String,
1200
1201 pub content: CodeExecutionToolResultContent,
1203
1204 #[serde(skip_serializing_if = "Option::is_none")]
1206 pub cache_control: Option<CacheControl>,
1207}
1208
1209#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1211#[serde(untagged)]
1212pub enum CodeExecutionToolResultContent {
1213 Success(CodeExecutionResultBlock),
1214 Error(CodeExecutionToolResultError),
1215}
1216
1217#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1219pub struct CodeExecutionToolResultError {
1220 #[serde(rename = "type")]
1221 pub error_type: String, pub error_code: CodeExecutionToolResultErrorCode,
1224}
1225
1226#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1228#[serde(rename_all = "snake_case")]
1229pub enum CodeExecutionToolResultErrorCode {
1230 Unavailable,
1231 CodeExecutionExceededTimeout,
1232 ContainerExpired,
1233 InvalidToolInput,
1234}
1235
1236#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1238pub struct BashCodeExecutionResultBlock {
1239 pub stdout: String,
1241
1242 pub stderr: String,
1244
1245 pub return_code: i32,
1247
1248 pub content: Vec<BashCodeExecutionOutputBlock>,
1250}
1251
1252#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1254pub struct BashCodeExecutionOutputBlock {
1255 #[serde(rename = "type")]
1256 pub block_type: String, pub file_id: String,
1260}
1261
1262#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1264pub struct BashCodeExecutionToolResultBlock {
1265 pub tool_use_id: String,
1267
1268 pub content: BashCodeExecutionToolResultContent,
1270
1271 #[serde(skip_serializing_if = "Option::is_none")]
1273 pub cache_control: Option<CacheControl>,
1274}
1275
1276#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1278#[serde(untagged)]
1279pub enum BashCodeExecutionToolResultContent {
1280 Success(BashCodeExecutionResultBlock),
1281 Error(BashCodeExecutionToolResultError),
1282}
1283
1284#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1286pub struct BashCodeExecutionToolResultError {
1287 #[serde(rename = "type")]
1288 pub error_type: String, pub error_code: BashCodeExecutionToolResultErrorCode,
1291}
1292
1293#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1295#[serde(rename_all = "snake_case")]
1296pub enum BashCodeExecutionToolResultErrorCode {
1297 Unavailable,
1298 CodeExecutionExceededTimeout,
1299 ContainerExpired,
1300 InvalidToolInput,
1301}
1302
1303#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1305pub struct TextEditorCodeExecutionToolResultBlock {
1306 pub tool_use_id: String,
1308
1309 pub content: TextEditorCodeExecutionToolResultContent,
1311
1312 #[serde(skip_serializing_if = "Option::is_none")]
1314 pub cache_control: Option<CacheControl>,
1315}
1316
1317#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1319#[serde(untagged)]
1320pub enum TextEditorCodeExecutionToolResultContent {
1321 CreateResult(TextEditorCodeExecutionCreateResultBlock),
1322 StrReplaceResult(TextEditorCodeExecutionStrReplaceResultBlock),
1323 ViewResult(TextEditorCodeExecutionViewResultBlock),
1324 Error(TextEditorCodeExecutionToolResultError),
1325}
1326
1327#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1329pub struct TextEditorCodeExecutionCreateResultBlock {
1330 #[serde(rename = "type")]
1331 pub block_type: String, }
1333
1334#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1336pub struct TextEditorCodeExecutionStrReplaceResultBlock {
1337 #[serde(rename = "type")]
1338 pub block_type: String, #[serde(skip_serializing_if = "Option::is_none")]
1342 pub snippet: Option<String>,
1343}
1344
1345#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1347pub struct TextEditorCodeExecutionViewResultBlock {
1348 #[serde(rename = "type")]
1349 pub block_type: String, pub content: String,
1353}
1354
1355#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1357pub struct TextEditorCodeExecutionToolResultError {
1358 #[serde(rename = "type")]
1359 pub error_type: String,
1360
1361 pub error_code: TextEditorCodeExecutionToolResultErrorCode,
1362}
1363
1364#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1366#[serde(rename_all = "snake_case")]
1367pub enum TextEditorCodeExecutionToolResultErrorCode {
1368 Unavailable,
1369 InvalidToolInput,
1370 FileNotFound,
1371 ContainerExpired,
1372}
1373
1374#[serde_with::skip_serializing_none]
1380#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1381pub struct WebFetchTool {
1382 #[serde(rename = "type")]
1383 pub tool_type: String, pub name: String, pub allowed_callers: Option<Vec<String>>,
1389
1390 pub max_uses: Option<u32>,
1392
1393 pub cache_control: Option<CacheControl>,
1395}
1396
1397#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1399pub struct WebFetchResultBlock {
1400 #[serde(rename = "type")]
1401 pub block_type: String, pub url: String,
1405
1406 pub content: DocumentBlock,
1408
1409 #[serde(skip_serializing_if = "Option::is_none")]
1411 pub retrieved_at: Option<String>,
1412}
1413
1414#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1416pub struct WebFetchToolResultBlock {
1417 pub tool_use_id: String,
1419
1420 pub content: WebFetchToolResultContent,
1422
1423 #[serde(skip_serializing_if = "Option::is_none")]
1425 pub cache_control: Option<CacheControl>,
1426}
1427
1428#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1430#[serde(untagged)]
1431pub enum WebFetchToolResultContent {
1432 Success(WebFetchResultBlock),
1433 Error(WebFetchToolResultError),
1434}
1435
1436#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1438pub struct WebFetchToolResultError {
1439 #[serde(rename = "type")]
1440 pub error_type: String, pub error_code: WebFetchToolResultErrorCode,
1443}
1444
1445#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1447#[serde(rename_all = "snake_case")]
1448pub enum WebFetchToolResultErrorCode {
1449 InvalidToolInput,
1450 Unavailable,
1451 MaxUsesExceeded,
1452 TooManyRequests,
1453 UrlNotAllowed,
1454 FetchFailed,
1455 ContentTooLarge,
1456}
1457
1458#[serde_with::skip_serializing_none]
1464#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1465pub struct ToolSearchTool {
1466 #[serde(rename = "type")]
1467 pub tool_type: String, pub name: String,
1470
1471 pub allowed_callers: Option<Vec<String>>,
1473
1474 pub cache_control: Option<CacheControl>,
1476}
1477
1478#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1480pub struct ToolReferenceBlock {
1481 #[serde(rename = "type")]
1482 pub block_type: String, pub tool_name: String,
1486
1487 #[serde(skip_serializing_if = "Option::is_none")]
1489 pub description: Option<String>,
1490}
1491
1492#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1494pub struct ToolSearchResultContent {
1495 #[serde(rename = "type")]
1496 pub block_type: String, pub tool_references: Vec<ToolReferenceBlock>,
1500}
1501
1502#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1504pub struct ToolSearchToolResultBlock {
1505 pub tool_use_id: String,
1507
1508 pub content: ToolSearchResultContent,
1510
1511 #[serde(skip_serializing_if = "Option::is_none")]
1513 pub cache_control: Option<CacheControl>,
1514}
1515
1516#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1522pub struct ContainerUploadBlock {
1523 #[serde(rename = "type")]
1524 pub block_type: String, pub file_id: String,
1528
1529 pub file_name: String,
1531
1532 #[serde(skip_serializing_if = "Option::is_none")]
1534 pub file_path: Option<String>,
1535}
1536
1537#[serde_with::skip_serializing_none]
1543#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1544pub struct MemoryTool {
1545 #[serde(rename = "type")]
1546 pub tool_type: String, pub name: String, pub allowed_callers: Option<Vec<String>>,
1552
1553 pub defer_loading: Option<bool>,
1555
1556 pub strict: Option<bool>,
1558
1559 pub input_examples: Option<Vec<Value>>,
1561
1562 pub cache_control: Option<CacheControl>,
1564}
1565
1566#[serde_with::skip_serializing_none]
1572#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1573pub struct ComputerUseTool {
1574 #[serde(rename = "type")]
1575 pub tool_type: String, pub name: String, pub display_width_px: u32,
1581
1582 pub display_height_px: u32,
1584
1585 pub display_number: Option<u32>,
1587
1588 pub allowed_callers: Option<Vec<String>>,
1590
1591 pub cache_control: Option<CacheControl>,
1593}
1594
1595#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1601#[serde(tag = "type", rename_all = "snake_case")]
1602pub enum BetaInputContentBlock {
1603 Text(TextBlock),
1605 Image(ImageBlock),
1606 Document(DocumentBlock),
1607 ToolUse(ToolUseBlock),
1608 ToolResult(ToolResultBlock),
1609 Thinking(ThinkingBlock),
1610 RedactedThinking(RedactedThinkingBlock),
1611 ServerToolUse(ServerToolUseBlock),
1612 SearchResult(SearchResultBlock),
1613 WebSearchToolResult(WebSearchToolResultBlock),
1614
1615 McpToolUse(McpToolUseBlock),
1617 McpToolResult(McpToolResultBlock),
1618
1619 CodeExecutionToolResult(CodeExecutionToolResultBlock),
1621 BashCodeExecutionToolResult(BashCodeExecutionToolResultBlock),
1622 TextEditorCodeExecutionToolResult(TextEditorCodeExecutionToolResultBlock),
1623
1624 WebFetchToolResult(WebFetchToolResultBlock),
1626
1627 ToolSearchToolResult(ToolSearchToolResultBlock),
1629 ToolReference(ToolReferenceBlock),
1630
1631 ContainerUpload(ContainerUploadBlock),
1633}
1634
1635#[serde_with::skip_serializing_none]
1637#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1638#[serde(tag = "type", rename_all = "snake_case")]
1639pub enum BetaContentBlock {
1640 Text {
1642 text: String,
1643 citations: Option<Vec<Citation>>,
1644 },
1645 ToolUse {
1646 id: String,
1647 name: String,
1648 input: Value,
1649 },
1650 Thinking {
1651 thinking: String,
1652 signature: String,
1653 },
1654 RedactedThinking {
1655 data: String,
1656 },
1657 ServerToolUse {
1658 id: String,
1659 name: String,
1660 input: Value,
1661 },
1662 WebSearchToolResult {
1663 tool_use_id: String,
1664 content: WebSearchToolResultContent,
1665 },
1666
1667 McpToolUse {
1669 id: String,
1670 name: String,
1671 server_name: String,
1672 input: Value,
1673 },
1674 McpToolResult {
1675 tool_use_id: String,
1676 content: Option<ToolResultContent>,
1677 is_error: Option<bool>,
1678 },
1679
1680 CodeExecutionToolResult {
1682 tool_use_id: String,
1683 content: CodeExecutionToolResultContent,
1684 },
1685 BashCodeExecutionToolResult {
1686 tool_use_id: String,
1687 content: BashCodeExecutionToolResultContent,
1688 },
1689 TextEditorCodeExecutionToolResult {
1690 tool_use_id: String,
1691 content: TextEditorCodeExecutionToolResultContent,
1692 },
1693
1694 WebFetchToolResult {
1696 tool_use_id: String,
1697 content: WebFetchToolResultContent,
1698 },
1699
1700 ToolSearchToolResult {
1702 tool_use_id: String,
1703 content: ToolSearchResultContent,
1704 },
1705 ToolReference {
1706 tool_name: String,
1707 description: Option<String>,
1708 },
1709
1710 ContainerUpload {
1712 file_id: String,
1713 file_name: String,
1714 file_path: Option<String>,
1715 },
1716}
1717
1718#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1720#[serde(untagged)]
1721pub enum BetaTool {
1722 Custom(CustomTool),
1724 Bash(BashTool),
1725 TextEditor(TextEditorTool),
1726 WebSearch(WebSearchTool),
1727
1728 CodeExecution(CodeExecutionTool),
1730 McpToolset(McpToolset),
1731 WebFetch(WebFetchTool),
1732 ToolSearch(ToolSearchTool),
1733 Memory(MemoryTool),
1734 ComputerUse(ComputerUseTool),
1735}
1736
1737#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1739#[serde(rename_all = "snake_case")]
1740pub enum BetaServerToolName {
1741 WebSearch,
1742 WebFetch,
1743 CodeExecution,
1744 BashCodeExecution,
1745 TextEditorCodeExecution,
1746 ToolSearchToolRegex,
1747 ToolSearchToolBm25,
1748}
1749
1750#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1752#[serde(tag = "type", rename_all = "snake_case")]
1753pub enum ServerToolCaller {
1754 Direct,
1756 #[serde(rename = "code_execution_20250825")]
1758 CodeExecution20250825,
1759}
1760
1761#[cfg(test)]
1762mod tests {
1763 use serde_json;
1764
1765 use super::*;
1766
1767 #[test]
1768 fn test_tool_mcp_toolset_defer_loading_deserialization() {
1769 let json = r#"{
1770 "type": "mcp_toolset",
1771 "mcp_server_name": "brave",
1772 "default_config": {"defer_loading": true}
1773 }"#;
1774
1775 let tool: Tool = serde_json::from_str(json).expect("Failed to deserialize McpToolset Tool");
1776 match tool {
1777 Tool::McpToolset(ts) => {
1778 assert_eq!(ts.mcp_server_name, "brave");
1779 let default_config = ts.default_config.expect("default_config should be Some");
1780 assert_eq!(default_config.defer_loading, Some(true));
1781 }
1782 other => panic!(
1783 "Expected McpToolset, got {:?}",
1784 std::mem::discriminant(&other)
1785 ),
1786 }
1787 }
1788
1789 #[test]
1790 fn test_tool_search_tool_deserialization() {
1791 let json = r#"{
1792 "type": "tool_search_tool_regex_20251119",
1793 "name": "tool_search_tool_regex"
1794 }"#;
1795
1796 let tool: Tool = serde_json::from_str(json).expect("Failed to deserialize ToolSearch Tool");
1797 match tool {
1798 Tool::ToolSearch(ts) => {
1799 assert_eq!(ts.name, "tool_search_tool_regex");
1800 assert_eq!(ts.tool_type, "tool_search_tool_regex_20251119");
1801 }
1802 other => panic!(
1803 "Expected ToolSearch, got {:?}",
1804 std::mem::discriminant(&other)
1805 ),
1806 }
1807 }
1808
1809 #[test]
1810 fn test_content_block_tool_search_tool_result_deserialization() {
1811 let json = r#"{
1812 "type": "tool_search_tool_result",
1813 "tool_use_id": "srvtoolu_015dw5iXvktXLmqwpyzo4Dp2",
1814 "content": {
1815 "type": "tool_search_tool_search_result",
1816 "tool_references": [
1817 {"type": "tool_reference", "tool_name": "get_weather"}
1818 ]
1819 }
1820 }"#;
1821
1822 let block: ContentBlock = serde_json::from_str(json)
1823 .expect("Failed to deserialize tool_search_tool_result ContentBlock");
1824 match block {
1825 ContentBlock::ToolSearchToolResult {
1826 tool_use_id,
1827 content,
1828 } => {
1829 assert_eq!(tool_use_id, "srvtoolu_015dw5iXvktXLmqwpyzo4Dp2");
1830 assert_eq!(content.tool_references.len(), 1);
1831 assert_eq!(content.tool_references[0].tool_name, "get_weather");
1832 }
1833 _ => panic!("Expected ToolSearchToolResult variant"),
1834 }
1835 }
1836
1837 #[test]
1838 fn test_content_block_server_tool_use_deserialization() {
1839 let json = r#"{
1840 "type": "server_tool_use",
1841 "id": "srvtoolu_015dw5iXvktXLmqwpyzo4Dp2",
1842 "name": "tool_search_tool_regex",
1843 "input": {"query": "weather"}
1844 }"#;
1845
1846 let block: ContentBlock =
1847 serde_json::from_str(json).expect("Failed to deserialize server_tool_use ContentBlock");
1848 match block {
1849 ContentBlock::ServerToolUse { id, name, input: _ } => {
1850 assert_eq!(id, "srvtoolu_015dw5iXvktXLmqwpyzo4Dp2");
1851 assert_eq!(name, "tool_search_tool_regex");
1852 }
1853 _ => panic!("Expected ServerToolUse variant"),
1854 }
1855 }
1856
1857 #[test]
1858 fn test_content_block_tool_reference_deserialization() {
1859 let json = r#"{
1860 "type": "tool_reference",
1861 "tool_name": "get_weather",
1862 "description": "Get the weather for a location"
1863 }"#;
1864
1865 let block: ContentBlock =
1866 serde_json::from_str(json).expect("Failed to deserialize tool_reference ContentBlock");
1867 match block {
1868 ContentBlock::ToolReference {
1869 tool_name,
1870 description,
1871 } => {
1872 assert_eq!(tool_name, "get_weather");
1873 assert_eq!(description.unwrap(), "Get the weather for a location");
1874 }
1875 _ => panic!("Expected ToolReference variant"),
1876 }
1877 }
1878
1879 #[test]
1880 fn test_full_message_with_tool_search_flow_deserialization() {
1881 let json = r#"{
1883 "id": "msg_01TEST",
1884 "type": "message",
1885 "role": "assistant",
1886 "model": "claude-sonnet-4-5-20250929",
1887 "content": [
1888 {
1889 "type": "server_tool_use",
1890 "id": "srvtoolu_015dw5iXvktXLmqwpyzo4Dp2",
1891 "name": "tool_search_tool_regex",
1892 "input": {"query": "weather"}
1893 },
1894 {
1895 "type": "tool_search_tool_result",
1896 "tool_use_id": "srvtoolu_015dw5iXvktXLmqwpyzo4Dp2",
1897 "content": {
1898 "type": "tool_search_tool_search_result",
1899 "tool_references": [
1900 {"type": "tool_reference", "tool_name": "get_weather"}
1901 ]
1902 }
1903 },
1904 {
1905 "type": "tool_use",
1906 "id": "toolu_01ABC",
1907 "name": "get_weather",
1908 "input": {"location": "San Francisco"}
1909 }
1910 ],
1911 "stop_reason": "tool_use",
1912 "stop_sequence": null,
1913 "usage": {
1914 "input_tokens": 100,
1915 "output_tokens": 50
1916 }
1917 }"#;
1918
1919 let msg: Message = serde_json::from_str(json)
1920 .expect("Failed to deserialize Message with tool search flow");
1921 assert_eq!(msg.content.len(), 3);
1922 assert!(matches!(msg.content[0], ContentBlock::ServerToolUse { .. }));
1923 assert!(matches!(
1924 msg.content[1],
1925 ContentBlock::ToolSearchToolResult { .. }
1926 ));
1927 assert!(matches!(msg.content[2], ContentBlock::ToolUse { .. }));
1928 }
1929}