Skip to main content

composio_sdk/models/
request.rs

1//! Request models for Composio API
2//!
3//! This module contains all request body structures used when making API calls
4//! to the Composio Tool Router. These models are serialized to JSON and sent
5//! in HTTP request bodies.
6//!
7//! # Main Request Types
8//!
9//! - [`SessionConfig`] - Configuration for creating a Tool Router session
10//! - [`ToolExecutionRequest`] - Request to execute a tool
11//! - [`MetaToolExecutionRequest`] - Request to execute a meta tool
12//! - [`LinkRequest`] - Request to create an authentication link
13//!
14//! # Configuration Types
15//!
16//! - [`ToolkitFilter`] - Enable or disable specific toolkits
17//! - [`ToolsConfig`] - Per-toolkit tool filtering
18//! - [`ToolFilter`] - Enable or disable specific tools within a toolkit
19//! - [`TagsConfig`] - Tag-based tool filtering (readOnlyHint, destructiveHint, etc.)
20//! - [`WorkbenchConfig`] - Workbench execution settings
21//! - [`ManageConnectionsConfig`] - Connection management settings
22//!
23//! # Example
24//!
25//! ```rust
26//! use composio_sdk::models::{SessionConfig, ToolkitFilter};
27//!
28//! let config = SessionConfig {
29//!     user_id: "user_123".to_string(),
30//!     toolkits: Some(ToolkitFilter::Enable(vec!["github".to_string()])),
31//!     auth_configs: None,
32//!     connected_accounts: None,
33//!     manage_connections: None,
34//!     tools: None,
35//!     tags: None,
36//!     workbench: None,
37//! };
38//! ```
39
40use serde::{Deserialize, Serialize};
41use std::collections::HashMap;
42
43use super::enums::{AuthScheme, MetaToolSlug, TagType};
44
45/// Configuration for creating a Tool Router session
46///
47/// This struct defines all the options available when creating a new session.
48/// Sessions provide scoped access to tools and toolkits for a specific user.
49///
50/// # Fields
51///
52/// * `user_id` - User identifier for session isolation (required)
53/// * `toolkits` - Optional toolkit filter (enable or disable specific toolkits)
54/// * `auth_configs` - Optional per-toolkit auth config overrides
55/// * `connected_accounts` - Optional per-toolkit connected account selection
56/// * `manage_connections` - Optional connection management configuration
57/// * `tools` - Optional per-toolkit tool filtering
58/// * `tags` - Optional tag-based tool filtering
59/// * `workbench` - Optional workbench configuration
60/// * `experimental` - Optional experimental features configuration
61///
62/// # Example
63///
64/// ```rust
65/// use composio_sdk::models::{SessionConfig, ToolkitFilter};
66/// use std::collections::HashMap;
67///
68/// let config = SessionConfig {
69///     user_id: "user_123".to_string(),
70///     toolkits: Some(ToolkitFilter::Enable(vec!["github".to_string(), "gmail".to_string()])),
71///     auth_configs: {
72///         let mut map = HashMap::new();
73///         map.insert("github".to_string(), "ac_custom_config".to_string());
74///         Some(map)
75///     },
76///     connected_accounts: None,
77///     manage_connections: None,
78///     tools: None,
79///     tags: None,
80///     workbench: None,
81///     experimental: None,
82///     toolkit_versions: None,
83///  };
84/// ```
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct SessionConfig {
87    pub user_id: String,
88    #[serde(skip_serializing_if = "Option::is_none")]
89    pub toolkits: Option<ToolkitFilter>,
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub auth_configs: Option<HashMap<String, String>>,
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub connected_accounts: Option<HashMap<String, String>>,
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub manage_connections: Option<ManageConnectionsConfig>,
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub tools: Option<ToolsConfig>,
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub tags: Option<TagsConfig>,
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub workbench: Option<WorkbenchConfig>,
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub experimental: Option<ExperimentalConfig>,
104    #[serde(skip_serializing_if = "Option::is_none")]
105    pub toolkit_versions: Option<super::versioning::ToolkitVersionParam>,
106}
107
108/// Configuration for connection management
109///
110/// Controls whether the agent automatically prompts users with Connect Links
111/// during chat when authentication is needed (in-chat authentication).
112///
113/// # Variants
114///
115/// * `Bool(bool)` - Simple boolean flag (true = enabled, false = disabled)
116/// * `Detailed` - Detailed configuration with additional options
117///
118/// # Example
119///
120/// ```rust
121/// use composio_sdk::models::ManageConnectionsConfig;
122///
123/// // Simple boolean
124/// let simple = ManageConnectionsConfig::Bool(true);
125///
126/// // Detailed configuration
127/// let detailed = ManageConnectionsConfig::Detailed {
128///     enabled: true,
129///     enable_wait_for_connections: Some(true),
130/// };
131/// ```
132#[derive(Debug, Clone, Serialize, Deserialize)]
133#[serde(untagged)]
134pub enum ManageConnectionsConfig {
135    /// Simple boolean flag
136    Bool(bool),
137    /// Detailed configuration
138    Detailed {
139        enabled: bool,
140        #[serde(skip_serializing_if = "Option::is_none")]
141        enable_wait_for_connections: Option<bool>,
142    },
143}
144
145/// Toolkit filter for enabling or disabling toolkits
146///
147/// Controls which toolkits are accessible in a session. By default, all toolkits
148/// are accessible via COMPOSIO_SEARCH_TOOLS. Use this filter to restrict access.
149///
150/// # Variants
151///
152/// * `Enable(Vec<String>)` - Only allow specified toolkits (allowlist)
153/// * `Disable { disable: Vec<String> }` - Allow all except specified toolkits (denylist)
154///
155/// # Example
156///
157/// ```rust
158/// use composio_sdk::models::ToolkitFilter;
159///
160/// // Enable only GitHub and Gmail
161/// let enable = ToolkitFilter::Enable(vec!["github".to_string(), "gmail".to_string()]);
162///
163/// // Disable Exa and Firecrawl
164/// let disable = ToolkitFilter::Disable {
165///     disable: vec!["exa".to_string(), "firecrawl".to_string()],
166/// };
167/// ```
168#[derive(Debug, Clone, Serialize, Deserialize)]
169#[serde(untagged)]
170pub enum ToolkitFilter {
171    Enable(Vec<String>),
172    Disable { disable: Vec<String> },
173}
174
175/// Configuration for per-toolkit tool filtering
176/// Maps toolkit names to their tool filter configuration
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct ToolsConfig(pub HashMap<String, ToolFilter>);
179
180/// Tool filter for a specific toolkit
181#[derive(Debug, Clone, Serialize, Deserialize)]
182#[serde(untagged)]
183pub enum ToolFilter {
184    /// Enable specific tools
185    Enable { enable: Vec<String> },
186    /// Disable specific tools
187    Disable { disable: Vec<String> },
188    /// Shorthand: array of tool names to enable
189    EnableList(Vec<String>),
190}
191
192/// Configuration for tag-based tool filtering
193/// Tags are MCP annotation hints for filtering tools
194#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct TagsConfig {
196    /// Tags that the tool must have at least one of
197    #[serde(skip_serializing_if = "Option::is_none")]
198    pub enabled: Option<Vec<TagType>>,
199    /// Tags that the tool must NOT have any of
200    #[serde(skip_serializing_if = "Option::is_none")]
201    pub disabled: Option<Vec<TagType>>,
202}
203
204/// Configuration for workbench
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct WorkbenchConfig {
207    #[serde(skip_serializing_if = "Option::is_none")]
208    #[serde(alias = "proxy_execution_enabled")]
209    pub proxy_execution: Option<bool>,
210    #[serde(skip_serializing_if = "Option::is_none")]
211    pub auto_offload_threshold: Option<u32>,
212}
213
214/// Configuration for assistive prompt generation
215///
216/// Experimental feature for generating timezone-aware assistive prompts.
217#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct AssistivePromptConfig {
219    /// IANA timezone identifier (e.g., "America/New_York", "Europe/London")
220    /// for timezone-aware assistive prompts
221    #[serde(skip_serializing_if = "Option::is_none")]
222    pub user_timezone: Option<String>,
223}
224
225/// Experimental configuration for Tool Router sessions
226///
227/// Note: These features are experimental and may be modified or removed in future versions.
228#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct ExperimentalConfig {
230    /// Configuration for assistive prompt generation
231    #[serde(skip_serializing_if = "Option::is_none")]
232    pub assistive_prompt: Option<AssistivePromptConfig>,
233}
234
235/// Request to execute a tool
236///
237/// This struct contains all parameters needed to execute a tool through the Composio API.
238/// It supports various authentication methods and execution modes.
239///
240/// # Fields
241///
242/// * `tool_slug` - The slug of the tool to execute (required)
243/// * `arguments` - Arguments to pass to the tool (optional)
244/// * `connected_account_id` - ID of the connected account to use for authentication (optional)
245/// * `custom_auth_params` - Custom authentication parameters (optional)
246/// * `custom_connection_data` - Custom connection data, takes priority over custom_auth_params (optional)
247/// * `user_id` - User ID to execute the tool for (optional)
248/// * `text` - Natural language text to pass to the tool (optional, mutually exclusive with arguments)
249/// * `version` - Version of the tool to execute (optional, overrides SDK-level toolkit versions)
250/// * `dangerously_skip_version_check` - Skip version check for 'latest' version (optional, dangerous!)
251///
252/// # Example
253///
254/// ```rust
255/// use composio_sdk::models::ToolExecutionRequest;
256/// use serde_json::json;
257///
258/// let request = ToolExecutionRequest {
259///     tool_slug: "GITHUB_CREATE_ISSUE".to_string(),
260///     arguments: Some(json!({
261///         "owner": "composio",
262///         "repo": "composio",
263///         "title": "Test issue"
264///     })),
265///     user_id: Some("user_123".to_string()),
266///     ..Default::default()
267/// };
268/// ```
269#[derive(Debug, Clone, Serialize, Default)]
270pub struct ToolExecutionRequest {
271    /// Tool slug to execute
272    pub tool_slug: String,
273    
274    /// Arguments to pass to the tool (mutually exclusive with text)
275    #[serde(skip_serializing_if = "Option::is_none")]
276    pub arguments: Option<serde_json::Value>,
277    
278    /// Connected account ID to use for authentication
279    #[serde(skip_serializing_if = "Option::is_none")]
280    pub connected_account_id: Option<String>,
281    
282    /// Custom authentication parameters
283    #[serde(skip_serializing_if = "Option::is_none")]
284    pub custom_auth_params: Option<serde_json::Value>,
285    
286    /// Custom connection data (takes priority over custom_auth_params)
287    #[serde(skip_serializing_if = "Option::is_none")]
288    pub custom_connection_data: Option<serde_json::Value>,
289    
290    /// User ID to execute the tool for
291    #[serde(skip_serializing_if = "Option::is_none")]
292    pub user_id: Option<String>,
293    
294    /// Natural language text to pass to the tool (mutually exclusive with arguments)
295    #[serde(skip_serializing_if = "Option::is_none")]
296    pub text: Option<String>,
297    
298    /// Version of the tool to execute (overrides SDK-level toolkit versions)
299    #[serde(skip_serializing_if = "Option::is_none")]
300    pub version: Option<String>,
301    
302    /// Skip version check for 'latest' version (dangerous - may cause unexpected behavior)
303    #[serde(skip_serializing_if = "Option::is_none")]
304    pub dangerously_skip_version_check: Option<bool>,
305}
306
307/// Request to execute a meta tool
308#[derive(Debug, Clone, Serialize)]
309pub struct MetaToolExecutionRequest {
310    pub slug: MetaToolSlug,
311    #[serde(skip_serializing_if = "Option::is_none")]
312    pub arguments: Option<serde_json::Value>,
313}
314
315/// Request to create an authentication link
316#[derive(Debug, Clone, Serialize)]
317pub struct LinkRequest {
318    pub toolkit: String,
319    #[serde(skip_serializing_if = "Option::is_none")]
320    pub callback_url: Option<String>,
321}
322
323// ============================================================================
324// Auth Config Request Types
325// ============================================================================
326
327/// Parameters for creating an authentication configuration
328///
329/// Auth configs define how users authenticate with external services.
330/// They can use Composio's managed auth or custom OAuth apps.
331///
332/// # Example
333///
334/// ```rust
335/// use composio_sdk::models::{AuthConfigCreateParams, AuthConfigData, AuthScheme};
336/// use serde_json::json;
337///
338/// let params = AuthConfigCreateParams {
339///     toolkit: "github".to_string(),
340///     auth_config: AuthConfigData {
341///         auth_type: AuthScheme::Oauth2,
342///         credentials: json!({
343///             "client_id": "your_client_id",
344///             "client_secret": "your_client_secret",
345///             "scopes": ["repo", "user"]
346///         }),
347///         restrict_to_following_tools: None,
348///     },
349/// };
350/// ```
351#[derive(Debug, Clone, Serialize)]
352pub struct AuthConfigCreateParams {
353    /// Toolkit slug (e.g., "github", "gmail")
354    pub toolkit: String,
355    /// Authentication configuration data
356    pub auth_config: AuthConfigData,
357}
358
359/// Authentication configuration data
360#[derive(Debug, Clone, Serialize)]
361pub struct AuthConfigData {
362    /// Type of authentication scheme
363    #[serde(rename = "type")]
364    pub auth_type: AuthScheme,
365    /// Credentials for the authentication (structure varies by auth_type)
366    pub credentials: serde_json::Value,
367    /// Optional list of tool slugs to restrict this auth config to
368    #[serde(skip_serializing_if = "Option::is_none")]
369    pub restrict_to_following_tools: Option<Vec<String>>,
370}
371
372/// Parameters for listing authentication configurations
373///
374/// # Example
375///
376/// ```rust
377/// use composio_sdk::models::AuthConfigListParams;
378///
379/// let params = AuthConfigListParams {
380///     is_composio_managed: Some(false),
381///     toolkit_slug: Some("github".to_string()),
382///     show_disabled: Some(false),
383///     search: None,
384///     limit: Some(20),
385///     cursor: None,
386/// };
387/// ```
388#[derive(Debug, Clone, Serialize, Default)]
389pub struct AuthConfigListParams {
390    /// Filter by Composio-managed auth configs
391    #[serde(skip_serializing_if = "Option::is_none")]
392    pub is_composio_managed: Option<bool>,
393    /// Filter by toolkit slug
394    #[serde(skip_serializing_if = "Option::is_none")]
395    pub toolkit_slug: Option<String>,
396    /// Include disabled auth configs
397    #[serde(skip_serializing_if = "Option::is_none")]
398    pub show_disabled: Option<bool>,
399    /// Search by name or ID
400    #[serde(skip_serializing_if = "Option::is_none")]
401    pub search: Option<String>,
402    /// Maximum number of results to return
403    #[serde(skip_serializing_if = "Option::is_none")]
404    pub limit: Option<u32>,
405    /// Pagination cursor
406    #[serde(skip_serializing_if = "Option::is_none")]
407    pub cursor: Option<String>,
408}
409
410/// Parameters for updating an authentication configuration
411///
412/// # Example
413///
414/// ```rust
415/// use composio_sdk::models::AuthConfigUpdateParams;
416/// use serde_json::json;
417///
418/// let params = AuthConfigUpdateParams {
419///     name: Some("My GitHub App".to_string()),
420///     credentials: Some(json!({
421///         "scopes": ["repo", "user", "admin:org"]
422///     })),
423///     proxy_config: None,
424///     tool_access_config: None,
425/// };
426/// ```
427#[derive(Debug, Clone, Serialize, Default)]
428pub struct AuthConfigUpdateParams {
429    /// New name for the auth config
430    #[serde(skip_serializing_if = "Option::is_none")]
431    pub name: Option<String>,
432    /// Updated credentials
433    #[serde(skip_serializing_if = "Option::is_none")]
434    pub credentials: Option<serde_json::Value>,
435    /// Proxy configuration
436    #[serde(skip_serializing_if = "Option::is_none")]
437    pub proxy_config: Option<serde_json::Value>,
438    /// Tool access configuration
439    #[serde(skip_serializing_if = "Option::is_none")]
440    pub tool_access_config: Option<serde_json::Value>,
441}
442
443// ============================================================================
444// Connected Account Request Types
445// ============================================================================
446
447/// Parameters for creating a connected account
448///
449/// Connected accounts represent user connections to external services.
450///
451/// # Example
452///
453/// ```rust
454/// use composio_sdk::models::{ConnectedAccountCreateParams, AuthConfigReference, ConnectionData};
455///
456/// let params = ConnectedAccountCreateParams {
457///     auth_config: AuthConfigReference {
458///         id: "ac_abc123".to_string(),
459///     },
460///     connection: ConnectionData {
461///         state: None,
462///         data: None,
463///         user_id: "user_123".to_string(),
464///         callback_url: Some("https://myapp.com/callback".to_string()),
465///     },
466///     validate_credentials: Some(true),
467/// };
468/// ```
469#[derive(Debug, Clone, Serialize)]
470pub struct ConnectedAccountCreateParams {
471    /// Reference to the auth config to use
472    pub auth_config: AuthConfigReference,
473    /// Connection data
474    pub connection: ConnectionData,
475    /// Whether to validate credentials immediately
476    #[serde(skip_serializing_if = "Option::is_none")]
477    pub validate_credentials: Option<bool>,
478}
479
480/// Reference to an authentication configuration
481#[derive(Debug, Clone, Serialize)]
482pub struct AuthConfigReference {
483    /// Auth config ID
484    pub id: String,
485}
486
487/// Connection data for creating a connected account
488#[derive(Debug, Clone, Serialize)]
489pub struct ConnectionData {
490    /// Connection state (varies by auth scheme)
491    #[serde(skip_serializing_if = "Option::is_none")]
492    pub state: Option<serde_json::Value>,
493    /// Additional connection data
494    #[serde(skip_serializing_if = "Option::is_none")]
495    pub data: Option<serde_json::Value>,
496    /// User ID this connection belongs to
497    pub user_id: String,
498    /// Callback URL for OAuth flows
499    #[serde(skip_serializing_if = "Option::is_none")]
500    pub callback_url: Option<String>,
501}
502
503/// Parameters for listing connected accounts
504///
505/// # Example
506///
507/// ```rust
508/// use composio_sdk::models::ConnectedAccountListParams;
509///
510/// let params = ConnectedAccountListParams {
511///     toolkit_slugs: Some(vec!["github".to_string(), "gmail".to_string()]),
512///     statuses: Some(vec!["ACTIVE".to_string()]),
513///     user_ids: Some(vec!["user_123".to_string()]),
514///     cursor: None,
515///     limit: Some(50),
516///     auth_config_ids: None,
517/// };
518/// ```
519#[derive(Debug, Clone, Serialize, Default)]
520pub struct ConnectedAccountListParams {
521    /// Filter by toolkit slugs
522    #[serde(skip_serializing_if = "Option::is_none")]
523    pub toolkit_slugs: Option<Vec<String>>,
524    /// Filter by connection statuses (ACTIVE, EXPIRED, FAILED, etc.)
525    #[serde(skip_serializing_if = "Option::is_none")]
526    pub statuses: Option<Vec<String>>,
527    /// Pagination cursor
528    #[serde(skip_serializing_if = "Option::is_none")]
529    pub cursor: Option<String>,
530    /// Maximum number of results to return
531    #[serde(skip_serializing_if = "Option::is_none")]
532    pub limit: Option<u32>,
533    /// Filter by user IDs
534    #[serde(skip_serializing_if = "Option::is_none")]
535    pub user_ids: Option<Vec<String>>,
536    /// Filter by auth config IDs
537    #[serde(skip_serializing_if = "Option::is_none")]
538    pub auth_config_ids: Option<Vec<String>>,
539}
540
541// ============================================================================
542// Tool Proxy Request Types
543// ============================================================================
544
545/// Parameters for executing a proxy request
546///
547/// Proxy requests allow you to make authenticated API calls to external services
548/// without predefined tool schemas.
549///
550/// # Example
551///
552/// ```rust
553/// use composio_sdk::models::ToolProxyParams;
554/// use serde_json::json;
555///
556/// let params = ToolProxyParams {
557///     endpoint: "/repos/owner/repo/issues".to_string(),
558///     method: Some("POST".to_string()),
559///     headers: Some(json!({"Accept": "application/vnd.github.v3+json"})),
560///     body: Some(json!({"title": "Bug report", "body": "Description"})),
561///     query_params: None,
562/// };
563/// ```
564#[derive(Debug, Clone, Serialize)]
565pub struct ToolProxyParams {
566    /// API endpoint (relative or absolute URL)
567    pub endpoint: String,
568    /// HTTP method (GET, POST, PUT, DELETE, PATCH, etc.)
569    #[serde(skip_serializing_if = "Option::is_none")]
570    pub method: Option<String>,
571    /// Custom headers
572    #[serde(skip_serializing_if = "Option::is_none")]
573    pub headers: Option<serde_json::Value>,
574    /// Request body
575    #[serde(skip_serializing_if = "Option::is_none")]
576    pub body: Option<serde_json::Value>,
577    /// Query parameters
578    #[serde(skip_serializing_if = "Option::is_none")]
579    pub query_params: Option<serde_json::Value>,
580}
581
582#[cfg(test)]
583mod tests {
584    use super::*;
585    use serde_json;
586
587    #[test]
588    fn test_session_config_minimal_serialization() {
589        let config = SessionConfig {
590            user_id: "user_123".to_string(),
591            toolkits: None,
592            auth_configs: None,
593            connected_accounts: None,
594            manage_connections: None,
595            tools: None,
596            tags: None,
597            workbench: None,
598            experimental: None,
599            toolkit_versions: None,
600        };
601
602        let json = serde_json::to_string(&config).unwrap();
603        assert!(json.contains("user_123"));
604        assert!(!json.contains("toolkits"));
605        assert!(!json.contains("auth_configs"));
606    }
607
608    #[test]
609    fn test_session_config_with_toolkits_enable() {
610        let config = SessionConfig {
611            user_id: "user_123".to_string(),
612            toolkits: Some(ToolkitFilter::Enable(vec!["github".to_string(), "gmail".to_string()])),
613            auth_configs: None,
614            connected_accounts: None,
615            manage_connections: None,
616            tools: None,
617            tags: None,
618            workbench: None,
619            experimental: None,
620            toolkit_versions: None,
621        };
622
623        let json = serde_json::to_string(&config).unwrap();
624        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
625        
626        assert!(parsed["toolkits"].is_array());
627        let toolkits = parsed["toolkits"].as_array().unwrap();
628        assert_eq!(toolkits.len(), 2);
629    }
630
631    #[test]
632    fn test_session_config_with_toolkits_disable() {
633        let config = SessionConfig {
634            user_id: "user_123".to_string(),
635            toolkits: Some(ToolkitFilter::Disable {
636                disable: vec!["exa".to_string(), "firecrawl".to_string()],
637            }),
638            auth_configs: None,
639            connected_accounts: None,
640            manage_connections: None,
641            tools: None,
642            tags: None,
643            workbench: None,
644            experimental: None,
645            toolkit_versions: None,
646        };
647
648        let json = serde_json::to_string(&config).unwrap();
649        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
650        
651        assert!(parsed["toolkits"].is_object());
652        assert!(parsed["toolkits"]["disable"].is_array());
653    }
654
655    #[test]
656    fn test_session_config_with_auth_configs() {
657        let mut auth_configs = HashMap::new();
658        auth_configs.insert("github".to_string(), "ac_custom".to_string());
659        
660        let config = SessionConfig {
661            user_id: "user_123".to_string(),
662            toolkits: None,
663            auth_configs: Some(auth_configs),
664            connected_accounts: None,
665            manage_connections: None,
666            tools: None,
667            tags: None,
668            workbench: None,
669            experimental: None,
670            toolkit_versions: None,
671        };
672
673        let json = serde_json::to_string(&config).unwrap();
674        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
675        
676        assert_eq!(parsed["auth_configs"]["github"], "ac_custom");
677    }
678
679    #[test]
680    fn test_session_config_with_manage_connections_bool() {
681        let config = SessionConfig {
682            user_id: "user_123".to_string(),
683            toolkits: None,
684            auth_configs: None,
685            connected_accounts: None,
686            manage_connections: Some(ManageConnectionsConfig::Bool(true)),
687            tools: None,
688            tags: None,
689            workbench: None,
690            experimental: None,
691            toolkit_versions: None,
692        };
693
694        let json = serde_json::to_string(&config).unwrap();
695        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
696        
697        assert_eq!(parsed["manage_connections"], true);
698    }
699
700    #[test]
701    fn test_session_config_with_manage_connections_detailed() {
702        let config = SessionConfig {
703            user_id: "user_123".to_string(),
704            toolkits: None,
705            auth_configs: None,
706            connected_accounts: None,
707            manage_connections: Some(ManageConnectionsConfig::Detailed {
708                enabled: true,
709                enable_wait_for_connections: Some(false),
710            }),
711            tools: None,
712            tags: None,
713            workbench: None,
714            experimental: None,
715            toolkit_versions: None,
716        };
717
718        let json = serde_json::to_string(&config).unwrap();
719        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
720        
721        assert_eq!(parsed["manage_connections"]["enabled"], true);
722        assert_eq!(parsed["manage_connections"]["enable_wait_for_connections"], false);
723    }
724
725    #[test]
726    fn test_session_config_with_tools() {
727        let mut tools_map = HashMap::new();
728        tools_map.insert(
729            "github".to_string(),
730            ToolFilter::EnableList(vec!["GITHUB_CREATE_ISSUE".to_string()]),
731        );
732        
733        let config = SessionConfig {
734            user_id: "user_123".to_string(),
735            toolkits: None,
736            auth_configs: None,
737            connected_accounts: None,
738            manage_connections: None,
739            tools: Some(ToolsConfig(tools_map)),
740            tags: None,
741            workbench: None,
742            experimental: None,
743            toolkit_versions: None,
744        };
745
746        let json = serde_json::to_string(&config).unwrap();
747        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
748        
749        assert!(parsed["tools"]["github"].is_array());
750    }
751
752    #[test]
753    fn test_session_config_with_tags() {
754        let config = SessionConfig {
755            user_id: "user_123".to_string(),
756            toolkits: None,
757            auth_configs: None,
758            connected_accounts: None,
759            manage_connections: None,
760            tools: None,
761            tags: Some(TagsConfig {
762                enabled: Some(vec![TagType::ReadOnlyHint]),
763                disabled: Some(vec![TagType::DestructiveHint]),
764            }),
765            workbench: None,
766            experimental: None,
767            toolkit_versions: None,
768        };
769
770        let json = serde_json::to_string(&config).unwrap();
771        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
772        
773        assert!(parsed["tags"]["enabled"].is_array());
774        assert!(parsed["tags"]["disabled"].is_array());
775    }
776
777    #[test]
778    fn test_session_config_with_workbench() {
779        let config = SessionConfig {
780            user_id: "user_123".to_string(),
781            toolkits: None,
782            auth_configs: None,
783            connected_accounts: None,
784            manage_connections: None,
785            tools: None,
786            tags: None,
787            workbench: Some(WorkbenchConfig {
788                proxy_execution: Some(true),
789                auto_offload_threshold: Some(1000),
790            }),
791            experimental: None,
792            toolkit_versions: None,
793        };
794
795        let json = serde_json::to_string(&config).unwrap();
796        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
797        
798        assert_eq!(parsed["workbench"]["proxy_execution"], true);
799        assert_eq!(parsed["workbench"]["auto_offload_threshold"], 1000);
800    }
801
802    #[test]
803    fn test_toolkit_filter_enable_serialization() {
804        let filter = ToolkitFilter::Enable(vec!["github".to_string(), "gmail".to_string()]);
805        let json = serde_json::to_string(&filter).unwrap();
806        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
807        
808        assert!(parsed.is_array());
809        assert_eq!(parsed.as_array().unwrap().len(), 2);
810    }
811
812    #[test]
813    fn test_toolkit_filter_disable_serialization() {
814        let filter = ToolkitFilter::Disable {
815            disable: vec!["exa".to_string()],
816        };
817        let json = serde_json::to_string(&filter).unwrap();
818        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
819        
820        assert!(parsed.is_object());
821        assert!(parsed["disable"].is_array());
822    }
823
824    #[test]
825    fn test_tool_filter_enable_serialization() {
826        let filter = ToolFilter::Enable {
827            enable: vec!["GITHUB_CREATE_ISSUE".to_string()],
828        };
829        let json = serde_json::to_string(&filter).unwrap();
830        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
831        
832        assert!(parsed.is_object());
833        assert!(parsed["enable"].is_array());
834    }
835
836    #[test]
837    fn test_tool_filter_disable_serialization() {
838        let filter = ToolFilter::Disable {
839            disable: vec!["GITHUB_DELETE_REPO".to_string()],
840        };
841        let json = serde_json::to_string(&filter).unwrap();
842        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
843        
844        assert!(parsed.is_object());
845        assert!(parsed["disable"].is_array());
846    }
847
848    #[test]
849    fn test_tool_filter_enable_list_serialization() {
850        let filter = ToolFilter::EnableList(vec!["GITHUB_CREATE_ISSUE".to_string()]);
851        let json = serde_json::to_string(&filter).unwrap();
852        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
853        
854        assert!(parsed.is_array());
855    }
856
857    #[test]
858    fn test_tool_execution_request_serialization() {
859        let request = ToolExecutionRequest {
860            tool_slug: "GITHUB_CREATE_ISSUE".to_string(),
861            arguments: Some(serde_json::json!({
862                "owner": "composio",
863                "repo": "composio",
864                "title": "Test issue"
865            })),
866            ..Default::default()
867        };
868
869        let json = serde_json::to_string(&request).unwrap();
870        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
871        
872        assert_eq!(parsed["tool_slug"], "GITHUB_CREATE_ISSUE");
873        assert!(parsed["arguments"].is_object());
874        assert_eq!(parsed["arguments"]["owner"], "composio");
875    }
876
877    #[test]
878    fn test_tool_execution_request_without_arguments() {
879        let request = ToolExecutionRequest {
880            tool_slug: "GITHUB_GET_USER".to_string(),
881            arguments: None,
882            ..Default::default()
883        };
884
885        let json = serde_json::to_string(&request).unwrap();
886        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
887        
888        assert_eq!(parsed["tool_slug"], "GITHUB_GET_USER");
889        assert!(parsed.get("arguments").is_none());
890    }
891
892    #[test]
893    fn test_meta_tool_execution_request_serialization() {
894        let request = MetaToolExecutionRequest {
895            slug: MetaToolSlug::ComposioSearchTools,
896            arguments: Some(serde_json::json!({
897                "query": "create a GitHub issue"
898            })),
899        };
900
901        let json = serde_json::to_string(&request).unwrap();
902        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
903        
904        assert_eq!(parsed["slug"], "COMPOSIO_SEARCH_TOOLS");
905        assert!(parsed["arguments"].is_object());
906    }
907
908    #[test]
909    fn test_link_request_serialization() {
910        let request = LinkRequest {
911            toolkit: "github".to_string(),
912            callback_url: Some("https://example.com/callback".to_string()),
913        };
914
915        let json = serde_json::to_string(&request).unwrap();
916        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
917        
918        assert_eq!(parsed["toolkit"], "github");
919        assert_eq!(parsed["callback_url"], "https://example.com/callback");
920    }
921
922    #[test]
923    fn test_link_request_without_callback() {
924        let request = LinkRequest {
925            toolkit: "gmail".to_string(),
926            callback_url: None,
927        };
928
929        let json = serde_json::to_string(&request).unwrap();
930        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
931        
932        assert_eq!(parsed["toolkit"], "gmail");
933        assert!(parsed.get("callback_url").is_none());
934    }
935
936    #[test]
937    fn test_tags_config_serialization() {
938        let config = TagsConfig {
939            enabled: Some(vec![TagType::ReadOnlyHint, TagType::IdempotentHint]),
940            disabled: Some(vec![TagType::DestructiveHint]),
941        };
942
943        let json = serde_json::to_string(&config).unwrap();
944        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
945        
946        assert!(parsed["enabled"].is_array());
947        assert!(parsed["disabled"].is_array());
948        assert_eq!(parsed["enabled"].as_array().unwrap().len(), 2);
949        assert_eq!(parsed["disabled"].as_array().unwrap().len(), 1);
950    }
951
952    #[test]
953    fn test_workbench_config_serialization() {
954        let config = WorkbenchConfig {
955            proxy_execution: Some(true),
956            auto_offload_threshold: Some(500),
957        };
958
959        let json = serde_json::to_string(&config).unwrap();
960        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
961        
962        assert_eq!(parsed["proxy_execution"], true);
963        assert_eq!(parsed["auto_offload_threshold"], 500);
964    }
965
966    #[test]
967    fn test_workbench_config_partial_serialization() {
968        let config = WorkbenchConfig {
969            proxy_execution: Some(false),
970            auto_offload_threshold: None,
971        };
972
973        let json = serde_json::to_string(&config).unwrap();
974        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
975        
976        assert_eq!(parsed["proxy_execution"], false);
977        assert!(parsed.get("auto_offload_threshold").is_none());
978    }
979}