1use serde::{Deserialize, Serialize};
47use std::collections::HashMap;
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct MCPToolkitConfig {
58 pub toolkit: String,
60
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub auth_config_id: Option<String>,
64}
65
66impl MCPToolkitConfig {
67 pub fn new(toolkit: impl Into<String>) -> Self {
69 Self {
70 toolkit: toolkit.into(),
71 auth_config_id: None,
72 }
73 }
74
75 pub fn with_auth_config(mut self, auth_config_id: impl Into<String>) -> Self {
77 self.auth_config_id = Some(auth_config_id.into());
78 self
79 }
80}
81
82impl From<String> for MCPToolkitConfig {
83 fn from(toolkit: String) -> Self {
84 Self::new(toolkit)
85 }
86}
87
88impl From<&str> for MCPToolkitConfig {
89 fn from(toolkit: &str) -> Self {
90 Self::new(toolkit)
91 }
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct MCPServerInstance {
99 pub id: String,
101
102 pub name: String,
104
105 #[serde(rename = "type")]
107 pub server_type: String,
108
109 pub url: String,
111
112 pub user_id: String,
114
115 pub allowed_tools: Vec<String>,
117
118 pub auth_configs: Vec<String>,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct MCPItem {
125 pub id: String,
127
128 pub name: String,
130
131 pub allowed_tools: Vec<String>,
133
134 pub auth_config_ids: Vec<String>,
136
137 pub toolkits: Vec<String>,
139
140 pub commands: HashMap<String, String>,
142
143 pub mcp_url: String,
145
146 pub toolkit_icons: HashMap<String, String>,
148
149 pub server_instance_count: i32,
151
152 #[serde(skip_serializing_if = "Option::is_none")]
154 pub created_at: Option<String>,
155
156 #[serde(skip_serializing_if = "Option::is_none")]
158 pub updated_at: Option<String>,
159
160 #[serde(skip_serializing_if = "Option::is_none")]
162 pub deleted: Option<bool>,
163
164 #[serde(skip_serializing_if = "Option::is_none")]
166 pub managed_auth_via_composio: Option<bool>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct MCPListResponse {
172 pub items: Vec<MCPItem>,
174
175 pub current_page: i32,
177
178 pub total_pages: i32,
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct MCPCreateResponse {
185 pub id: String,
187
188 pub name: String,
190
191 pub allowed_tools: Vec<String>,
193
194 pub auth_config_ids: Vec<String>,
196
197 pub toolkits: Vec<String>,
199
200 pub mcp_url: String,
202
203 #[serde(skip_serializing_if = "Option::is_none")]
205 pub created_at: Option<String>,
206}
207
208pub type MCPUpdateResponse = MCPCreateResponse;
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct MCPDeleteResponse {
214 pub id: String,
216
217 pub deleted: bool,
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
223pub struct MCPGenerateUrlResponse {
224 #[serde(skip_serializing_if = "Option::is_none")]
226 pub mcp_url: Option<String>,
227
228 #[serde(skip_serializing_if = "Option::is_none")]
230 pub connected_account_urls: Option<Vec<String>>,
231
232 pub user_ids_url: Vec<String>,
234}
235
236pub type MCPRetrieveAppResponse = MCPListResponse;
238
239pub type MCPCustomCreateResponse = MCPCreateResponse;
241
242#[derive(Debug, Clone, Serialize)]
248pub struct MCPCreateParams {
249 pub name: String,
251
252 pub auth_config_ids: Vec<String>,
254
255 #[serde(skip_serializing_if = "Option::is_none")]
257 pub allowed_tools: Option<Vec<String>>,
258
259 #[serde(skip_serializing_if = "Option::is_none")]
261 pub no_auth_apps: Option<Vec<String>>,
262
263 #[serde(skip_serializing_if = "Option::is_none")]
265 #[deprecated(
266 note = "Prefer no_auth_apps/auth_config_ids to match MCP create endpoint contract"
267 )]
268 pub toolkits: Option<Vec<String>>,
269
270 #[serde(skip_serializing_if = "Option::is_none")]
272 #[deprecated(note = "Prefer allowed_tools to match MCP create endpoint contract")]
273 pub custom_tools: Option<Vec<String>>,
274
275 #[serde(skip_serializing_if = "Option::is_none")]
277 pub managed_auth_via_composio: Option<bool>,
278}
279
280#[derive(Debug, Clone, Serialize)]
282pub struct MCPUpdateParams {
283 #[serde(skip_serializing_if = "Option::is_none")]
285 pub name: Option<String>,
286
287 #[serde(skip_serializing_if = "Option::is_none")]
289 pub toolkits: Option<Vec<String>>,
290
291 #[serde(skip_serializing_if = "Option::is_none")]
293 pub auth_config_ids: Option<Vec<String>>,
294
295 #[serde(skip_serializing_if = "Option::is_none")]
297 pub allowed_tools: Option<Vec<String>>,
298
299 #[serde(skip_serializing_if = "Option::is_none")]
301 pub custom_tools: Option<Vec<String>>,
302
303 #[serde(skip_serializing_if = "Option::is_none")]
305 pub managed_auth_via_composio: Option<bool>,
306}
307
308#[derive(Debug, Clone, Default, Serialize)]
310pub struct MCPListParams {
311 #[serde(skip_serializing_if = "Option::is_none")]
313 pub page_no: Option<i32>,
314
315 #[serde(skip_serializing_if = "Option::is_none")]
317 pub limit: Option<i32>,
318
319 #[serde(skip_serializing_if = "Option::is_none")]
321 pub toolkits: Option<String>,
322
323 #[serde(skip_serializing_if = "Option::is_none")]
325 pub auth_config_ids: Option<String>,
326
327 #[serde(skip_serializing_if = "Option::is_none")]
329 pub name: Option<String>,
330
331 #[serde(skip_serializing_if = "Option::is_none")]
333 pub order_by: Option<String>,
334
335 #[serde(skip_serializing_if = "Option::is_none")]
337 pub order_direction: Option<String>,
338}
339
340#[derive(Debug, Clone, Serialize)]
342pub struct MCPGenerateUrlParams {
343 pub mcp_server_id: String,
345
346 #[serde(skip_serializing_if = "Option::is_none")]
348 pub connected_account_ids: Option<Vec<String>>,
349
350 #[serde(skip_serializing_if = "Option::is_none")]
352 pub user_ids: Option<Vec<String>>,
353
354 #[serde(skip_serializing_if = "Option::is_none")]
356 pub managed_auth_by_composio: Option<bool>,
357}
358
359#[derive(Debug, Clone, Serialize)]
361pub struct MCPCustomCreateParams {
362 pub name: String,
364
365 #[serde(skip_serializing_if = "Option::is_none")]
367 pub allowed_tools: Option<Vec<String>>,
368
369 #[serde(skip_serializing_if = "Option::is_none")]
371 pub auth_config_ids: Option<Vec<String>>,
372
373 #[serde(skip_serializing_if = "Option::is_none")]
375 pub custom_tools: Option<Vec<String>>,
376
377 #[serde(skip_serializing_if = "Option::is_none")]
379 pub managed_auth_via_composio: Option<bool>,
380
381 #[serde(skip_serializing_if = "Option::is_none")]
383 pub toolkits: Option<Vec<String>>,
384}
385
386pub type MCPRetrieveAppParams = MCPListParams;
388
389#[cfg(test)]
390mod tests {
391 use super::*;
392
393 #[test]
394 fn test_mcp_toolkit_config_new() {
395 let config = MCPToolkitConfig::new("github");
396 assert_eq!(config.toolkit, "github");
397 assert!(config.auth_config_id.is_none());
398 }
399
400 #[test]
401 fn test_mcp_toolkit_config_with_auth() {
402 let config = MCPToolkitConfig::new("github").with_auth_config("ac_123");
403
404 assert_eq!(config.toolkit, "github");
405 assert_eq!(config.auth_config_id, Some("ac_123".to_string()));
406 }
407
408 #[test]
409 fn test_mcp_toolkit_config_from_string() {
410 let config: MCPToolkitConfig = "slack".into();
411 assert_eq!(config.toolkit, "slack");
412 assert!(config.auth_config_id.is_none());
413 }
414
415 #[test]
416 fn test_mcp_server_instance_serialization() {
417 let instance = MCPServerInstance {
418 id: "mcp_123".to_string(),
419 name: "Test Server".to_string(),
420 server_type: "streamable_http".to_string(),
421 url: "https://mcp.composio.dev/test".to_string(),
422 user_id: "user_123".to_string(),
423 allowed_tools: vec!["GITHUB_CREATE_ISSUE".to_string()],
424 auth_configs: vec!["ac_123".to_string()],
425 };
426
427 let json = serde_json::to_string(&instance).unwrap();
428 assert!(json.contains("mcp_123"));
429 assert!(json.contains("Test Server"));
430 assert!(json.contains("streamable_http"));
431 }
432
433 #[test]
434 fn test_mcp_server_instance_deserialization() {
435 let json = r#"{
436 "id": "mcp_456",
437 "name": "My Server",
438 "type": "streamable_http",
439 "url": "https://mcp.url",
440 "user_id": "user_456",
441 "allowed_tools": ["SLACK_SEND_MESSAGE"],
442 "auth_configs": ["ac_456"]
443 }"#;
444
445 let instance: MCPServerInstance = serde_json::from_str(json).unwrap();
446 assert_eq!(instance.id, "mcp_456");
447 assert_eq!(instance.name, "My Server");
448 assert_eq!(instance.server_type, "streamable_http");
449 assert_eq!(instance.url, "https://mcp.url");
450 assert_eq!(instance.user_id, "user_456");
451 assert_eq!(instance.allowed_tools.len(), 1);
452 assert_eq!(instance.auth_configs.len(), 1);
453 }
454
455 #[test]
456 fn test_mcp_list_params_default() {
457 let params = MCPListParams::default();
458 assert!(params.page_no.is_none());
459 assert!(params.limit.is_none());
460 assert!(params.toolkits.is_none());
461 assert!(params.auth_config_ids.is_none());
462 assert!(params.name.is_none());
463 }
464
465 #[test]
466 fn test_mcp_create_params_serialization() {
467 let params = MCPCreateParams {
468 name: "Test Server".to_string(),
469 auth_config_ids: vec!["ac_123".to_string()],
470 allowed_tools: Some(vec!["GITHUB_CREATE_ISSUE".to_string()]),
471 no_auth_apps: Some(vec!["notion".to_string()]),
472 toolkits: Some(vec!["github".to_string()]),
473 custom_tools: Some(vec!["GITHUB_CREATE_ISSUE".to_string()]),
474 managed_auth_via_composio: Some(true),
475 };
476
477 let json = serde_json::to_string(¶ms).unwrap();
478 assert!(json.contains("Test Server"));
479 assert!(json.contains("github"));
480 assert!(json.contains("ac_123"));
481 assert!(json.contains("allowed_tools"));
482 assert!(json.contains("no_auth_apps"));
483 }
484
485 #[test]
486 fn test_mcp_generate_params_serialization() {
487 let params = MCPGenerateUrlParams {
488 mcp_server_id: "mcp_123".to_string(),
489 connected_account_ids: Some(vec!["ca_1".to_string()]),
490 user_ids: Some(vec!["user_123".to_string()]),
491 managed_auth_by_composio: Some(true),
492 };
493
494 let json = serde_json::to_string(¶ms).unwrap();
495 assert!(json.contains("connected_account_ids"));
496 assert!(json.contains("user_ids"));
497 }
498
499 #[test]
500 fn test_mcp_generate_response_deserialization() {
501 let json = r#"{
502 "mcp_url": "https://mcp.example.com/base",
503 "connected_account_urls": ["https://mcp.example.com?connected_account_id=ca_1"],
504 "user_ids_url": ["https://mcp.example.com?user_id=user_123"]
505 }"#;
506
507 let response: MCPGenerateUrlResponse = serde_json::from_str(json).unwrap();
508 assert_eq!(
509 response.mcp_url.as_deref(),
510 Some("https://mcp.example.com/base")
511 );
512 assert_eq!(response.connected_account_urls.unwrap().len(), 1);
513 assert_eq!(response.user_ids_url.len(), 1);
514 }
515
516 #[test]
517 fn test_mcp_delete_response_deserialization() {
518 let json = r#"{
519 "id": "mcp_789",
520 "deleted": true
521 }"#;
522
523 let response: MCPDeleteResponse = serde_json::from_str(json).unwrap();
524 assert_eq!(response.id, "mcp_789");
525 assert!(response.deleted);
526 }
527}