1use serde::Deserialize;
4
5use super::enums::AuthScheme;
6use super::request::SessionConfig;
7
8#[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#[derive(Debug, Clone, Deserialize)]
23pub struct McpInfo {
24 pub url: String,
25}
26
27#[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#[derive(Debug, Clone, Deserialize)]
75pub struct ToolExecutionResponse {
76 pub data: serde_json::Value,
78
79 pub error: Option<String>,
81
82 pub log_id: String,
84
85 #[serde(default)]
88 pub successful: bool,
89}
90
91impl ToolExecutionResponse {
92 pub fn is_successful(&self) -> bool {
107 self.error.is_none()
108 }
109}
110
111pub type MetaToolExecutionResponse = ToolExecutionResponse;
113
114#[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#[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#[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#[derive(Debug, Clone, Deserialize)]
153pub struct ConnectedAccountInfo {
154 pub id: String,
155 pub status: String,
156 pub created_at: String,
157}
158
159#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
173#[serde(rename_all = "lowercase")]
174pub enum ToolRouterMcpServerType {
175 Http,
177 Sse,
179}
180
181#[derive(Debug, Clone, Deserialize)]
186pub struct ToolRouterMcpServerConfig {
187 #[serde(rename = "type")]
189 pub server_type: ToolRouterMcpServerType,
190 pub url: String,
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub headers: Option<std::collections::HashMap<String, Option<String>>>,
195}
196
197#[derive(Debug, Clone, Deserialize)]
201pub struct ToolRouterSessionExperimental {
202 #[serde(skip_serializing_if = "Option::is_none")]
204 pub assistive_prompt: Option<String>,
205}
206
207#[derive(Debug, Clone, Deserialize)]
209pub struct ToolkitConnectionAuthConfig {
210 pub id: String,
212 pub mode: String,
214 pub is_composio_managed: bool,
216}
217
218#[derive(Debug, Clone, Deserialize)]
220pub struct ToolkitConnectedAccount {
221 pub id: String,
223 pub status: String,
225}
226
227#[derive(Debug, Clone, Deserialize)]
229pub struct ToolkitConnection {
230 pub is_active: bool,
232 #[serde(skip_serializing_if = "Option::is_none")]
234 pub auth_config: Option<ToolkitConnectionAuthConfig>,
235 #[serde(skip_serializing_if = "Option::is_none")]
237 pub connected_account: Option<ToolkitConnectedAccount>,
238}
239
240#[derive(Debug, Clone, Deserialize)]
245pub struct ToolkitConnectionState {
246 pub slug: String,
248 pub name: String,
250 pub is_no_auth: bool,
252 #[serde(skip_serializing_if = "Option::is_none")]
254 pub connection: Option<ToolkitConnection>,
255 #[serde(skip_serializing_if = "Option::is_none")]
257 pub logo: Option<String>,
258}
259
260#[derive(Debug, Clone, Deserialize)]
265pub struct ToolkitConnectionsDetails {
266 pub items: Vec<ToolkitConnectionState>,
268 pub total_pages: u32,
270 #[serde(skip_serializing_if = "Option::is_none")]
272 pub next_cursor: Option<String>,
273}
274
275#[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#[derive(Debug, Clone, Deserialize)]
293pub struct AuthConfigCreateResponse {
294 pub toolkit: ToolkitInfo,
296 pub auth_config: AuthConfigInfo,
298}
299
300#[derive(Debug, Clone, Deserialize)]
302pub struct AuthConfigListResponse {
303 pub items: Vec<AuthConfigDetail>,
305 pub next_cursor: Option<String>,
307 pub total_pages: u32,
309 pub current_page: u32,
311 pub total_items: u32,
313}
314
315#[derive(Debug, Clone, Deserialize)]
317pub struct AuthConfigRetrieveResponse {
318 pub id: String,
320 pub uuid: String,
322 #[serde(rename = "type")]
324 pub config_type: String,
325 pub toolkit: String,
327 pub name: String,
329 pub auth_scheme: AuthScheme,
331 pub credentials: serde_json::Value,
333 pub status: String,
335 pub created_at: String,
337 pub no_of_connections: u32,
339 #[serde(skip_serializing_if = "Option::is_none")]
341 pub tool_access_config: Option<serde_json::Value>,
342}
343
344#[derive(Debug, Clone, Deserialize)]
346pub struct AuthConfigDetail {
347 pub id: String,
349 pub uuid: String,
351 #[serde(rename = "type")]
353 pub config_type: String,
354 pub toolkit: String,
356 pub name: String,
358 pub auth_scheme: AuthScheme,
360 pub credentials: serde_json::Value,
362 pub status: String,
364 pub created_at: String,
366 pub no_of_connections: u32,
368 #[serde(skip_serializing_if = "Option::is_none")]
370 pub tool_access_config: Option<serde_json::Value>,
371}
372
373#[derive(Debug, Clone, Deserialize)]
375pub struct AuthConfigInfo {
376 pub id: String,
378 pub auth_scheme: AuthScheme,
380 pub is_composio_managed: bool,
382 #[serde(skip_serializing_if = "Option::is_none")]
384 pub restrict_to_following_tools: Option<Vec<String>>,
385}
386
387#[derive(Debug, Clone, Deserialize)]
393pub struct ConnectedAccountCreateResponse {
394 pub id: String,
396 pub connection_data: serde_json::Value,
398 pub status: String,
400 #[serde(skip_serializing_if = "Option::is_none")]
402 pub redirect_url: Option<String>,
403}
404
405#[derive(Debug, Clone, Deserialize)]
407pub struct ConnectedAccountListResponse {
408 pub items: Vec<ConnectedAccountDetail>,
410 pub next_cursor: Option<String>,
412 pub total_pages: u32,
414 pub current_page: u32,
416 pub total_items: u32,
418}
419
420#[derive(Debug, Clone, Deserialize)]
422pub struct ConnectedAccountDetail {
423 pub toolkit: String,
425 pub auth_config: String,
427 pub id: String,
429 pub user_id: String,
431 pub status: String,
433 pub created_at: String,
435 pub updated_at: String,
437 pub state: serde_json::Value,
439 #[serde(skip_serializing_if = "Option::is_none")]
441 pub data: Option<serde_json::Value>,
442 #[serde(skip_serializing_if = "Option::is_none")]
444 pub status_reason: Option<String>,
445 pub is_disabled: bool,
447}
448
449pub type ConnectedAccountRetrieveResponse = ConnectedAccountDetail;
451
452#[derive(Debug, Clone, Deserialize)]
454pub struct ConnectedAccountUpdateStatusResponse {
455 pub success: bool,
457}
458
459#[derive(Debug, Clone, Deserialize)]
465pub struct ToolProxyResponse {
466 pub data: serde_json::Value,
468 pub status: u16,
470 #[serde(skip_serializing_if = "Option::is_none")]
472 pub headers: Option<serde_json::Value>,
473 #[serde(skip_serializing_if = "Option::is_none")]
475 pub error: Option<String>,
476}
477
478#[derive(Debug, Clone, Deserialize)]
484pub struct TriggerInstanceUpsertResponse {
485 pub id: String,
487 pub trigger_name: String,
489 pub connected_account_id: String,
491 pub user_id: String,
493 pub trigger_config: serde_json::Value,
495 pub state: String,
497 pub created_at: String,
499 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}