Skip to main content

composio_sdk/models/
response.rs

1//! Response models from Composio API
2
3use serde::Deserialize;
4
5use super::enums::AuthScheme;
6use super::request::SessionConfig;
7
8/// Response from session creation
9#[derive(Debug, Clone, Deserialize)]
10pub struct SessionResponse {
11    pub session_id: String,
12    pub mcp: McpInfo,
13    pub tool_router_tools: Vec<String>,
14    pub config: SessionConfig,
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub assistive_prompt: Option<String>,
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub toolkit_versions: Option<super::versioning::ToolkitVersionParam>,
19}
20
21/// MCP server information
22#[derive(Debug, Clone, Deserialize)]
23pub struct McpInfo {
24    pub url: String,
25}
26
27/// Tool schema information
28#[derive(Debug, Clone, Deserialize)]
29pub struct ToolSchema {
30    pub slug: String,
31    pub name: String,
32    pub description: String,
33    pub toolkit: String,
34    pub input_parameters: serde_json::Value,
35    pub output_parameters: serde_json::Value,
36    #[serde(default)]
37    pub scopes: Vec<String>,
38    #[serde(default)]
39    pub tags: Vec<String>,
40    pub version: String,
41    #[serde(default)]
42    pub available_versions: Vec<String>,
43    #[serde(default)]
44    pub is_deprecated: bool,
45    #[serde(default)]
46    pub no_auth: bool,
47}
48
49/// Response from tool execution
50///
51/// Contains the result of a tool execution, including data, error information,
52/// and execution metadata.
53///
54/// # Fields
55///
56/// * `data` - The execution result data
57/// * `error` - Error message if execution failed
58/// * `log_id` - Log ID for debugging and tracing
59/// * `successful` - Whether the execution was successful (derived from error field)
60///
61/// # Example
62///
63/// ```rust
64/// use composio_sdk::models::ToolExecutionResponse;
65///
66/// # fn example(response: ToolExecutionResponse) {
67/// if response.is_successful() {
68///     println!("Success: {:?}", response.data);
69/// } else {
70///     eprintln!("Error: {}", response.error.unwrap());
71/// }
72/// # }
73/// ```
74#[derive(Debug, Clone, Deserialize)]
75pub struct ToolExecutionResponse {
76    /// Execution result data
77    pub data: serde_json::Value,
78    
79    /// Error message if execution failed
80    pub error: Option<String>,
81    
82    /// Log ID for debugging and tracing
83    pub log_id: String,
84    
85    /// Whether the execution was successful
86    /// This field is computed from the error field during deserialization
87    #[serde(default)]
88    pub successful: bool,
89}
90
91impl ToolExecutionResponse {
92    /// Check if the execution was successful
93    ///
94    /// Returns `true` if there is no error, `false` otherwise.
95    ///
96    /// # Example
97    ///
98    /// ```rust
99    /// # use composio_sdk::models::ToolExecutionResponse;
100    /// # fn example(response: ToolExecutionResponse) {
101    /// if response.is_successful() {
102    ///     println!("Tool executed successfully!");
103    /// }
104    /// # }
105    /// ```
106    pub fn is_successful(&self) -> bool {
107        self.error.is_none()
108    }
109}
110
111/// Response from meta tool execution
112pub type MetaToolExecutionResponse = ToolExecutionResponse;
113
114/// Response from listing toolkits
115#[derive(Debug, Clone, Deserialize)]
116pub struct ToolkitListResponse {
117    pub items: Vec<ToolkitInfo>,
118    pub next_cursor: Option<String>,
119    pub total_pages: u32,
120    pub current_page: u32,
121    pub total_items: u32,
122}
123
124/// Information about a toolkit
125#[derive(Debug, Clone, Deserialize)]
126pub struct ToolkitInfo {
127    pub name: String,
128    pub slug: String,
129    pub enabled: bool,
130    pub is_no_auth: bool,
131    pub composio_managed_auth_schemes: Vec<AuthScheme>,
132    pub meta: ToolkitMeta,
133    pub connected_account: Option<ConnectedAccountInfo>,
134}
135
136/// Metadata about a toolkit
137#[derive(Debug, Clone, Deserialize)]
138pub struct ToolkitMeta {
139    pub logo: String,
140    pub description: String,
141    #[serde(default)]
142    pub categories: Vec<String>,
143    #[serde(default)]
144    pub tools_count: u32,
145    #[serde(default)]
146    pub triggers_count: u32,
147    #[serde(default)]
148    pub version: String,
149}
150
151/// Information about a connected account
152#[derive(Debug, Clone, Deserialize)]
153pub struct ConnectedAccountInfo {
154    pub id: String,
155    pub status: String,
156    pub created_at: String,
157}
158
159/// Response from creating an auth link
160#[derive(Debug, Clone, Deserialize)]
161pub struct LinkResponse {
162    pub link_token: String,
163    pub redirect_url: String,
164    pub connected_account_id: Option<String>,
165}
166
167// ============================================================================
168// Tool Router Session Response Types
169// ============================================================================
170
171/// MCP server type for Tool Router sessions
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
173#[serde(rename_all = "lowercase")]
174pub enum ToolRouterMcpServerType {
175    /// HTTP-based MCP server
176    Http,
177    /// Server-Sent Events (SSE) based MCP server
178    Sse,
179}
180
181/// MCP server configuration for Tool Router sessions
182///
183/// Contains the connection details for accessing the MCP server
184/// associated with a Tool Router session.
185#[derive(Debug, Clone, Deserialize)]
186pub struct ToolRouterMcpServerConfig {
187    /// Type of MCP server (HTTP or SSE)
188    #[serde(rename = "type")]
189    pub server_type: ToolRouterMcpServerType,
190    /// MCP server URL
191    pub url: String,
192    /// Optional authentication headers (includes x-api-key)
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub headers: Option<std::collections::HashMap<String, Option<String>>>,
195}
196
197/// Experimental features in Tool Router session response
198///
199/// Note: These features are experimental and may be modified or removed in future versions.
200#[derive(Debug, Clone, Deserialize)]
201pub struct ToolRouterSessionExperimental {
202    /// Generated assistive system prompt based on experimental config
203    #[serde(skip_serializing_if = "Option::is_none")]
204    pub assistive_prompt: Option<String>,
205}
206
207/// Auth config information for a toolkit connection
208#[derive(Debug, Clone, Deserialize)]
209pub struct ToolkitConnectionAuthConfig {
210    /// Auth config ID
211    pub id: String,
212    /// Authentication scheme/mode
213    pub mode: String,
214    /// Whether this auth config is managed by Composio
215    pub is_composio_managed: bool,
216}
217
218/// Connected account information for a toolkit
219#[derive(Debug, Clone, Deserialize)]
220pub struct ToolkitConnectedAccount {
221    /// Connected account ID
222    pub id: String,
223    /// Connection status (ACTIVE, EXPIRED, FAILED, etc.)
224    pub status: String,
225}
226
227/// Connection information for a toolkit
228#[derive(Debug, Clone, Deserialize)]
229pub struct ToolkitConnection {
230    /// Whether the connection is active
231    pub is_active: bool,
232    /// Auth config information (None for no-auth toolkits)
233    #[serde(skip_serializing_if = "Option::is_none")]
234    pub auth_config: Option<ToolkitConnectionAuthConfig>,
235    /// Connected account information (None if not connected)
236    #[serde(skip_serializing_if = "Option::is_none")]
237    pub connected_account: Option<ToolkitConnectedAccount>,
238}
239
240/// Connection state of a toolkit in a Tool Router session
241///
242/// Provides detailed information about a toolkit's availability,
243/// authentication status, and connection details.
244#[derive(Debug, Clone, Deserialize)]
245pub struct ToolkitConnectionState {
246    /// Toolkit slug (e.g., "github", "gmail")
247    pub slug: String,
248    /// Human-readable toolkit name
249    pub name: String,
250    /// Whether this toolkit requires no authentication
251    pub is_no_auth: bool,
252    /// Connection information (None for no-auth toolkits)
253    #[serde(skip_serializing_if = "Option::is_none")]
254    pub connection: Option<ToolkitConnection>,
255    /// Toolkit logo URL
256    #[serde(skip_serializing_if = "Option::is_none")]
257    pub logo: Option<String>,
258}
259
260/// Details of toolkit connections in a Tool Router session
261///
262/// Contains a paginated list of toolkit connection states with
263/// information about authentication and connection status.
264#[derive(Debug, Clone, Deserialize)]
265pub struct ToolkitConnectionsDetails {
266    /// List of toolkit connection states
267    pub items: Vec<ToolkitConnectionState>,
268    /// Total number of pages available
269    pub total_pages: u32,
270    /// Cursor for fetching the next page (None if no more pages)
271    #[serde(skip_serializing_if = "Option::is_none")]
272    pub next_cursor: Option<String>,
273}
274
275/// Error response from API
276#[derive(Debug, Clone, Deserialize)]
277pub struct ErrorResponse {
278    pub message: String,
279    pub code: Option<String>,
280    pub slug: Option<String>,
281    pub status: u16,
282    pub request_id: Option<String>,
283    pub suggested_fix: Option<String>,
284    pub errors: Option<Vec<crate::error::ErrorDetail>>,
285}
286
287// ============================================================================
288// Auth Config Response Types
289// ============================================================================
290
291/// Response from creating an authentication configuration
292#[derive(Debug, Clone, Deserialize)]
293pub struct AuthConfigCreateResponse {
294    /// Toolkit information
295    pub toolkit: ToolkitInfo,
296    /// Created auth config information
297    pub auth_config: AuthConfigInfo,
298}
299
300/// Response from listing authentication configurations
301#[derive(Debug, Clone, Deserialize)]
302pub struct AuthConfigListResponse {
303    /// List of auth configs
304    pub items: Vec<AuthConfigDetail>,
305    /// Pagination cursor for next page
306    pub next_cursor: Option<String>,
307    /// Total number of pages
308    pub total_pages: u32,
309    /// Current page number
310    pub current_page: u32,
311    /// Total number of items
312    pub total_items: u32,
313}
314
315/// Response from retrieving a single authentication configuration
316#[derive(Debug, Clone, Deserialize)]
317pub struct AuthConfigRetrieveResponse {
318    /// Auth config ID (nano ID)
319    pub id: String,
320    /// Auth config UUID (deprecated, use id)
321    pub uuid: String,
322    /// Type of auth config
323    #[serde(rename = "type")]
324    pub config_type: String,
325    /// Toolkit slug
326    pub toolkit: String,
327    /// Auth config name
328    pub name: String,
329    /// Authentication scheme
330    pub auth_scheme: AuthScheme,
331    /// Credentials (may be masked)
332    pub credentials: serde_json::Value,
333    /// Status (ENABLED, DISABLED)
334    pub status: String,
335    /// Creation timestamp
336    pub created_at: String,
337    /// Number of connected accounts using this auth config
338    pub no_of_connections: u32,
339    /// Tool access configuration
340    #[serde(skip_serializing_if = "Option::is_none")]
341    pub tool_access_config: Option<serde_json::Value>,
342}
343
344/// Detailed auth config information
345#[derive(Debug, Clone, Deserialize)]
346pub struct AuthConfigDetail {
347    /// Auth config ID (nano ID)
348    pub id: String,
349    /// Auth config UUID (deprecated)
350    pub uuid: String,
351    /// Type of auth config
352    #[serde(rename = "type")]
353    pub config_type: String,
354    /// Toolkit slug
355    pub toolkit: String,
356    /// Auth config name
357    pub name: String,
358    /// Authentication scheme
359    pub auth_scheme: AuthScheme,
360    /// Credentials (may be masked)
361    pub credentials: serde_json::Value,
362    /// Status (ENABLED, DISABLED)
363    pub status: String,
364    /// Creation timestamp
365    pub created_at: String,
366    /// Number of connected accounts
367    pub no_of_connections: u32,
368    /// Tool access configuration
369    #[serde(skip_serializing_if = "Option::is_none")]
370    pub tool_access_config: Option<serde_json::Value>,
371}
372
373/// Information about an authentication configuration
374#[derive(Debug, Clone, Deserialize)]
375pub struct AuthConfigInfo {
376    /// Auth config ID
377    pub id: String,
378    /// Authentication scheme
379    pub auth_scheme: AuthScheme,
380    /// Whether this is managed by Composio
381    pub is_composio_managed: bool,
382    /// Optional list of tools this auth config is restricted to
383    #[serde(skip_serializing_if = "Option::is_none")]
384    pub restrict_to_following_tools: Option<Vec<String>>,
385}
386
387// ============================================================================
388// Connected Account Response Types
389// ============================================================================
390
391/// Response from creating a connected account
392#[derive(Debug, Clone, Deserialize)]
393pub struct ConnectedAccountCreateResponse {
394    /// Connected account ID
395    pub id: String,
396    /// Connection data (varies by auth scheme)
397    pub connection_data: serde_json::Value,
398    /// Connection status
399    pub status: String,
400    /// Redirect URL for OAuth flows
401    #[serde(skip_serializing_if = "Option::is_none")]
402    pub redirect_url: Option<String>,
403}
404
405/// Response from listing connected accounts
406#[derive(Debug, Clone, Deserialize)]
407pub struct ConnectedAccountListResponse {
408    /// List of connected accounts
409    pub items: Vec<ConnectedAccountDetail>,
410    /// Pagination cursor for next page
411    pub next_cursor: Option<String>,
412    /// Total number of pages
413    pub total_pages: u32,
414    /// Current page number
415    pub current_page: u32,
416    /// Total number of items
417    pub total_items: u32,
418}
419
420/// Detailed information about a connected account
421#[derive(Debug, Clone, Deserialize)]
422pub struct ConnectedAccountDetail {
423    /// Toolkit slug
424    pub toolkit: String,
425    /// Auth config ID
426    pub auth_config: String,
427    /// Connected account ID
428    pub id: String,
429    /// User ID
430    pub user_id: String,
431    /// Connection status (ACTIVE, EXPIRED, FAILED, etc.)
432    pub status: String,
433    /// Creation timestamp
434    pub created_at: String,
435    /// Last update timestamp
436    pub updated_at: String,
437    /// Connection state
438    pub state: serde_json::Value,
439    /// Additional connection data
440    #[serde(skip_serializing_if = "Option::is_none")]
441    pub data: Option<serde_json::Value>,
442    /// Status reason (if failed or expired)
443    #[serde(skip_serializing_if = "Option::is_none")]
444    pub status_reason: Option<String>,
445    /// Whether the account is disabled
446    pub is_disabled: bool,
447}
448
449/// Response from retrieving a single connected account
450pub type ConnectedAccountRetrieveResponse = ConnectedAccountDetail;
451
452/// Response from updating connected account status
453#[derive(Debug, Clone, Deserialize)]
454pub struct ConnectedAccountUpdateStatusResponse {
455    /// Whether the operation was successful
456    pub success: bool,
457}
458
459// ============================================================================
460// Tool Proxy Response Types
461// ============================================================================
462
463/// Response from a proxy request
464#[derive(Debug, Clone, Deserialize)]
465pub struct ToolProxyResponse {
466    /// Response data from the external API
467    pub data: serde_json::Value,
468    /// HTTP status code
469    pub status: u16,
470    /// Response headers
471    #[serde(skip_serializing_if = "Option::is_none")]
472    pub headers: Option<serde_json::Value>,
473    /// Error message if request failed
474    #[serde(skip_serializing_if = "Option::is_none")]
475    pub error: Option<String>,
476}
477
478// ============================================================================
479// Trigger Response Types
480// ============================================================================
481
482/// Response from creating or updating a trigger instance
483#[derive(Debug, Clone, Deserialize)]
484pub struct TriggerInstanceUpsertResponse {
485    /// Trigger instance ID
486    pub id: String,
487    /// Trigger name/slug
488    pub trigger_name: String,
489    /// Connected account ID
490    pub connected_account_id: String,
491    /// User ID
492    pub user_id: String,
493    /// Trigger configuration
494    pub trigger_config: serde_json::Value,
495    /// Trigger state
496    pub state: String,
497    /// Creation timestamp
498    pub created_at: String,
499    /// Last update timestamp
500    pub updated_at: String,
501}
502
503#[cfg(test)]
504mod tests {
505    use super::*;
506    use serde_json;
507
508    #[test]
509    fn test_session_response_deserialization() {
510        let json = r#"{
511            "session_id": "sess_abc123",
512            "mcp": {
513                "url": "https://mcp.composio.dev/sess_abc123"
514            },
515            "tool_router_tools": [
516                "COMPOSIO_SEARCH_TOOLS",
517                "COMPOSIO_MULTI_EXECUTE_TOOL"
518            ],
519            "config": {
520                "user_id": "user_123"
521            }
522        }"#;
523
524        let response: SessionResponse = serde_json::from_str(json).unwrap();
525        assert_eq!(response.session_id, "sess_abc123");
526        assert_eq!(response.mcp.url, "https://mcp.composio.dev/sess_abc123");
527        assert_eq!(response.tool_router_tools.len(), 2);
528        assert_eq!(response.config.user_id, "user_123");
529        assert!(response.assistive_prompt.is_none());
530    }
531
532    #[test]
533    fn test_session_response_with_assistive_prompt() {
534        let json = r#"{
535            "session_id": "sess_abc123",
536            "mcp": {
537                "url": "https://mcp.composio.dev/sess_abc123"
538            },
539            "tool_router_tools": [],
540            "config": {
541                "user_id": "user_123"
542            },
543            "assistive_prompt": "Use COMPOSIO_SEARCH_TOOLS to discover tools"
544        }"#;
545
546        let response: SessionResponse = serde_json::from_str(json).unwrap();
547        assert_eq!(
548            response.assistive_prompt,
549            Some("Use COMPOSIO_SEARCH_TOOLS to discover tools".to_string())
550        );
551    }
552
553    #[test]
554    fn test_mcp_info_deserialization() {
555        let json = r#"{
556            "url": "https://mcp.composio.dev/session_123"
557        }"#;
558
559        let mcp: McpInfo = serde_json::from_str(json).unwrap();
560        assert_eq!(mcp.url, "https://mcp.composio.dev/session_123");
561    }
562
563    #[test]
564    fn test_tool_schema_deserialization() {
565        let json = r#"{
566            "slug": "GITHUB_CREATE_ISSUE",
567            "name": "Create Issue",
568            "description": "Create a new issue in a GitHub repository",
569            "toolkit": "github",
570            "input_parameters": {
571                "type": "object",
572                "properties": {
573                    "owner": {"type": "string"},
574                    "repo": {"type": "string"},
575                    "title": {"type": "string"}
576                }
577            },
578            "output_parameters": {
579                "type": "object",
580                "properties": {
581                    "id": {"type": "number"}
582                }
583            },
584            "scopes": ["repo"],
585            "tags": ["write"],
586            "version": "1.0.0",
587            "available_versions": ["1.0.0", "0.9.0"],
588            "is_deprecated": false,
589            "no_auth": false
590        }"#;
591
592        let schema: ToolSchema = serde_json::from_str(json).unwrap();
593        assert_eq!(schema.slug, "GITHUB_CREATE_ISSUE");
594        assert_eq!(schema.name, "Create Issue");
595        assert_eq!(schema.toolkit, "github");
596        assert_eq!(schema.scopes.len(), 1);
597        assert_eq!(schema.tags.len(), 1);
598        assert_eq!(schema.version, "1.0.0");
599        assert_eq!(schema.available_versions.len(), 2);
600        assert!(!schema.is_deprecated);
601        assert!(!schema.no_auth);
602    }
603
604    #[test]
605    fn test_tool_schema_minimal_deserialization() {
606        let json = r#"{
607            "slug": "SIMPLE_TOOL",
608            "name": "Simple Tool",
609            "description": "A simple tool",
610            "toolkit": "simple",
611            "input_parameters": {},
612            "output_parameters": {},
613            "version": "1.0.0"
614        }"#;
615
616        let schema: ToolSchema = serde_json::from_str(json).unwrap();
617        assert_eq!(schema.slug, "SIMPLE_TOOL");
618        assert!(schema.scopes.is_empty());
619        assert!(schema.tags.is_empty());
620        assert!(schema.available_versions.is_empty());
621        assert!(!schema.is_deprecated);
622        assert!(!schema.no_auth);
623    }
624
625    #[test]
626    fn test_tool_execution_response_deserialization() {
627        let json = r#"{
628            "data": {
629                "issue_id": 123,
630                "url": "https://github.com/owner/repo/issues/123"
631            },
632            "error": null,
633            "log_id": "log_xyz789"
634        }"#;
635
636        let response: ToolExecutionResponse = serde_json::from_str(json).unwrap();
637        assert!(response.data.is_object());
638        assert_eq!(response.data["issue_id"], 123);
639        assert!(response.error.is_none());
640        assert_eq!(response.log_id, "log_xyz789");
641    }
642
643    #[test]
644    fn test_tool_execution_response_with_error() {
645        let json = r#"{
646            "data": null,
647            "error": "Failed to create issue: Invalid repository",
648            "log_id": "log_error123"
649        }"#;
650
651        let response: ToolExecutionResponse = serde_json::from_str(json).unwrap();
652        assert!(response.data.is_null());
653        assert_eq!(
654            response.error,
655            Some("Failed to create issue: Invalid repository".to_string())
656        );
657        assert_eq!(response.log_id, "log_error123");
658    }
659
660    #[test]
661    fn test_toolkit_list_response_deserialization() {
662        let json = r#"{
663            "items": [
664                {
665                    "name": "GitHub",
666                    "slug": "github",
667                    "enabled": true,
668                    "is_no_auth": false,
669                    "composio_managed_auth_schemes": ["OAUTH2"],
670                    "meta": {
671                        "logo": "https://logo.url",
672                        "description": "GitHub integration",
673                        "categories": ["development"],
674                        "tools_count": 50,
675                        "triggers_count": 10,
676                        "version": "1.0.0"
677                    },
678                    "connected_account": {
679                        "id": "ca_123",
680                        "status": "ACTIVE",
681                        "created_at": "2024-01-01T00:00:00Z"
682                    }
683                }
684            ],
685            "next_cursor": "cursor_abc",
686            "total_pages": 5,
687            "current_page": 1,
688            "total_items": 100
689        }"#;
690
691        let response: ToolkitListResponse = serde_json::from_str(json).unwrap();
692        assert_eq!(response.items.len(), 1);
693        assert_eq!(response.next_cursor, Some("cursor_abc".to_string()));
694        assert_eq!(response.total_pages, 5);
695        assert_eq!(response.current_page, 1);
696        assert_eq!(response.total_items, 100);
697    }
698
699    #[test]
700    fn test_toolkit_info_deserialization() {
701        let json = r#"{
702            "name": "Gmail",
703            "slug": "gmail",
704            "enabled": true,
705            "is_no_auth": false,
706            "composio_managed_auth_schemes": ["OAUTH2"],
707            "meta": {
708                "logo": "https://gmail.logo",
709                "description": "Gmail integration",
710                "categories": ["communication"],
711                "tools_count": 30,
712                "triggers_count": 5,
713                "version": "2.0.0"
714            },
715            "connected_account": null
716        }"#;
717
718        let info: ToolkitInfo = serde_json::from_str(json).unwrap();
719        assert_eq!(info.name, "Gmail");
720        assert_eq!(info.slug, "gmail");
721        assert!(info.enabled);
722        assert!(!info.is_no_auth);
723        assert_eq!(info.composio_managed_auth_schemes.len(), 1);
724        assert!(info.connected_account.is_none());
725    }
726
727    #[test]
728    fn test_toolkit_meta_deserialization() {
729        let json = r#"{
730            "logo": "https://logo.url",
731            "description": "Test toolkit",
732            "categories": ["test", "development"],
733            "tools_count": 25,
734            "triggers_count": 3,
735            "version": "1.5.0"
736        }"#;
737
738        let meta: ToolkitMeta = serde_json::from_str(json).unwrap();
739        assert_eq!(meta.logo, "https://logo.url");
740        assert_eq!(meta.description, "Test toolkit");
741        assert_eq!(meta.categories.len(), 2);
742        assert_eq!(meta.tools_count, 25);
743        assert_eq!(meta.triggers_count, 3);
744        assert_eq!(meta.version, "1.5.0");
745    }
746
747    #[test]
748    fn test_toolkit_meta_minimal_deserialization() {
749        let json = r#"{
750            "logo": "https://logo.url",
751            "description": "Minimal toolkit"
752        }"#;
753
754        let meta: ToolkitMeta = serde_json::from_str(json).unwrap();
755        assert_eq!(meta.logo, "https://logo.url");
756        assert_eq!(meta.description, "Minimal toolkit");
757        assert!(meta.categories.is_empty());
758        assert_eq!(meta.tools_count, 0);
759        assert_eq!(meta.triggers_count, 0);
760        assert_eq!(meta.version, "");
761    }
762
763    #[test]
764    fn test_connected_account_info_deserialization() {
765        let json = r#"{
766            "id": "ca_abc123",
767            "status": "ACTIVE",
768            "created_at": "2024-01-15T10:30:00Z"
769        }"#;
770
771        let info: ConnectedAccountInfo = serde_json::from_str(json).unwrap();
772        assert_eq!(info.id, "ca_abc123");
773        assert_eq!(info.status, "ACTIVE");
774        assert_eq!(info.created_at, "2024-01-15T10:30:00Z");
775    }
776
777    #[test]
778    fn test_link_response_deserialization() {
779        let json = r#"{
780            "link_token": "lt_xyz789",
781            "redirect_url": "https://auth.composio.dev/link?token=lt_xyz789",
782            "connected_account_id": "ca_existing123"
783        }"#;
784
785        let response: LinkResponse = serde_json::from_str(json).unwrap();
786        assert_eq!(response.link_token, "lt_xyz789");
787        assert_eq!(response.redirect_url, "https://auth.composio.dev/link?token=lt_xyz789");
788        assert_eq!(response.connected_account_id, Some("ca_existing123".to_string()));
789    }
790
791    #[test]
792    fn test_link_response_without_connected_account() {
793        let json = r#"{
794            "link_token": "lt_new456",
795            "redirect_url": "https://auth.composio.dev/link?token=lt_new456",
796            "connected_account_id": null
797        }"#;
798
799        let response: LinkResponse = serde_json::from_str(json).unwrap();
800        assert_eq!(response.link_token, "lt_new456");
801        assert!(response.connected_account_id.is_none());
802    }
803
804    #[test]
805    fn test_error_response_deserialization() {
806        let json = r#"{
807            "message": "Validation failed",
808            "code": "VALIDATION_ERROR",
809            "slug": "validation-failed",
810            "status": 400,
811            "request_id": "req_abc123",
812            "suggested_fix": "Check your input parameters",
813            "errors": [
814                {
815                    "field": "user_id",
816                    "message": "User ID is required"
817                }
818            ]
819        }"#;
820
821        let response: ErrorResponse = serde_json::from_str(json).unwrap();
822        assert_eq!(response.message, "Validation failed");
823        assert_eq!(response.code, Some("VALIDATION_ERROR".to_string()));
824        assert_eq!(response.slug, Some("validation-failed".to_string()));
825        assert_eq!(response.status, 400);
826        assert_eq!(response.request_id, Some("req_abc123".to_string()));
827        assert_eq!(response.suggested_fix, Some("Check your input parameters".to_string()));
828        assert!(response.errors.is_some());
829        assert_eq!(response.errors.as_ref().unwrap().len(), 1);
830    }
831
832    #[test]
833    fn test_error_response_minimal_deserialization() {
834        let json = r#"{
835            "message": "Internal server error",
836            "status": 500
837        }"#;
838
839        let response: ErrorResponse = serde_json::from_str(json).unwrap();
840        assert_eq!(response.message, "Internal server error");
841        assert_eq!(response.status, 500);
842        assert!(response.code.is_none());
843        assert!(response.slug.is_none());
844        assert!(response.request_id.is_none());
845        assert!(response.suggested_fix.is_none());
846        assert!(response.errors.is_none());
847    }
848
849    #[test]
850    fn test_auth_scheme_deserialization() {
851        let json = r#"["OAUTH2", "API_KEY", "BEARER_TOKEN"]"#;
852        let schemes: Vec<AuthScheme> = serde_json::from_str(json).unwrap();
853        
854        assert_eq!(schemes.len(), 3);
855        assert!(matches!(schemes[0], AuthScheme::Oauth2));
856        assert!(matches!(schemes[1], AuthScheme::ApiKey));
857        assert!(matches!(schemes[2], AuthScheme::BearerToken));
858    }
859
860    #[test]
861    fn test_meta_tool_execution_response_alias() {
862        let json = r#"{
863            "data": {"result": "success"},
864            "error": null,
865            "log_id": "log_meta123"
866        }"#;
867
868        let response: MetaToolExecutionResponse = serde_json::from_str(json).unwrap();
869        assert!(response.data.is_object());
870        assert!(response.error.is_none());
871        assert_eq!(response.log_id, "log_meta123");
872    }
873
874    #[test]
875    fn test_toolkit_list_response_empty_items() {
876        let json = r#"{
877            "items": [],
878            "next_cursor": null,
879            "total_pages": 0,
880            "current_page": 0,
881            "total_items": 0
882        }"#;
883
884        let response: ToolkitListResponse = serde_json::from_str(json).unwrap();
885        assert!(response.items.is_empty());
886        assert!(response.next_cursor.is_none());
887        assert_eq!(response.total_pages, 0);
888        assert_eq!(response.current_page, 0);
889        assert_eq!(response.total_items, 0);
890    }
891
892    #[test]
893    fn test_session_response_clone() {
894        let json = r#"{
895            "session_id": "sess_123",
896            "mcp": {"url": "https://mcp.url"},
897            "tool_router_tools": [],
898            "config": {"user_id": "user_123"}
899        }"#;
900
901        let response: SessionResponse = serde_json::from_str(json).unwrap();
902        let cloned = response.clone();
903        
904        assert_eq!(response.session_id, cloned.session_id);
905        assert_eq!(response.mcp.url, cloned.mcp.url);
906    }
907
908    #[test]
909    fn test_tool_schema_debug() {
910        let json = r#"{
911            "slug": "TEST_TOOL",
912            "name": "Test",
913            "description": "Test tool",
914            "toolkit": "test",
915            "input_parameters": {},
916            "output_parameters": {},
917            "version": "1.0.0"
918        }"#;
919
920        let schema: ToolSchema = serde_json::from_str(json).unwrap();
921        let debug_str = format!("{:?}", schema);
922        
923        assert!(debug_str.contains("TEST_TOOL"));
924        assert!(debug_str.contains("Test"));
925    }
926}