use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MCPToolkitConfig {
pub toolkit: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth_config_id: Option<String>,
}
impl MCPToolkitConfig {
pub fn new(toolkit: impl Into<String>) -> Self {
Self {
toolkit: toolkit.into(),
auth_config_id: None,
}
}
pub fn with_auth_config(mut self, auth_config_id: impl Into<String>) -> Self {
self.auth_config_id = Some(auth_config_id.into());
self
}
}
impl From<String> for MCPToolkitConfig {
fn from(toolkit: String) -> Self {
Self::new(toolkit)
}
}
impl From<&str> for MCPToolkitConfig {
fn from(toolkit: &str) -> Self {
Self::new(toolkit)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MCPServerInstance {
pub id: String,
pub name: String,
#[serde(rename = "type")]
pub server_type: String,
pub url: String,
pub user_id: String,
pub allowed_tools: Vec<String>,
pub auth_configs: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MCPItem {
pub id: String,
pub name: String,
pub allowed_tools: Vec<String>,
pub auth_config_ids: Vec<String>,
pub toolkits: Vec<String>,
pub commands: HashMap<String, String>,
pub mcp_url: String,
pub toolkit_icons: HashMap<String, String>,
pub server_instance_count: i32,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_at: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MCPListResponse {
pub items: Vec<MCPItem>,
pub current_page: i32,
pub total_pages: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MCPCreateResponse {
pub id: String,
pub name: String,
pub allowed_tools: Vec<String>,
pub auth_config_ids: Vec<String>,
pub toolkits: Vec<String>,
pub mcp_url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<String>,
}
pub type MCPUpdateResponse = MCPCreateResponse;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MCPDeleteResponse {
pub id: String,
pub deleted: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MCPGenerateUrlResponse {
pub user_ids_url: Vec<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct MCPCreateParams {
pub name: String,
pub toolkits: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth_config_ids: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_tools: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub managed_auth_via_composio: Option<bool>,
}
#[derive(Debug, Clone, Serialize)]
pub struct MCPUpdateParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub toolkits: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth_config_ids: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_tools: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub managed_auth_via_composio: Option<bool>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct MCPListParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub page_no: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub toolkits: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth_config_ids: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_by: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_direction: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct MCPGenerateUrlParams {
pub mcp_server_id: String,
pub user_ids: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub managed_auth_by_composio: Option<bool>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mcp_toolkit_config_new() {
let config = MCPToolkitConfig::new("github");
assert_eq!(config.toolkit, "github");
assert!(config.auth_config_id.is_none());
}
#[test]
fn test_mcp_toolkit_config_with_auth() {
let config = MCPToolkitConfig::new("github")
.with_auth_config("ac_123");
assert_eq!(config.toolkit, "github");
assert_eq!(config.auth_config_id, Some("ac_123".to_string()));
}
#[test]
fn test_mcp_toolkit_config_from_string() {
let config: MCPToolkitConfig = "slack".into();
assert_eq!(config.toolkit, "slack");
assert!(config.auth_config_id.is_none());
}
#[test]
fn test_mcp_server_instance_serialization() {
let instance = MCPServerInstance {
id: "mcp_123".to_string(),
name: "Test Server".to_string(),
server_type: "streamable_http".to_string(),
url: "https://mcp.composio.dev/test".to_string(),
user_id: "user_123".to_string(),
allowed_tools: vec!["GITHUB_CREATE_ISSUE".to_string()],
auth_configs: vec!["ac_123".to_string()],
};
let json = serde_json::to_string(&instance).unwrap();
assert!(json.contains("mcp_123"));
assert!(json.contains("Test Server"));
assert!(json.contains("streamable_http"));
}
#[test]
fn test_mcp_server_instance_deserialization() {
let json = r#"{
"id": "mcp_456",
"name": "My Server",
"type": "streamable_http",
"url": "https://mcp.url",
"user_id": "user_456",
"allowed_tools": ["SLACK_SEND_MESSAGE"],
"auth_configs": ["ac_456"]
}"#;
let instance: MCPServerInstance = serde_json::from_str(json).unwrap();
assert_eq!(instance.id, "mcp_456");
assert_eq!(instance.name, "My Server");
assert_eq!(instance.server_type, "streamable_http");
assert_eq!(instance.url, "https://mcp.url");
assert_eq!(instance.user_id, "user_456");
assert_eq!(instance.allowed_tools.len(), 1);
assert_eq!(instance.auth_configs.len(), 1);
}
#[test]
fn test_mcp_list_params_default() {
let params = MCPListParams::default();
assert!(params.page_no.is_none());
assert!(params.limit.is_none());
assert!(params.toolkits.is_none());
assert!(params.auth_config_ids.is_none());
assert!(params.name.is_none());
}
#[test]
fn test_mcp_create_params_serialization() {
let params = MCPCreateParams {
name: "Test Server".to_string(),
toolkits: vec!["github".to_string()],
auth_config_ids: Some(vec!["ac_123".to_string()]),
custom_tools: Some(vec!["GITHUB_CREATE_ISSUE".to_string()]),
managed_auth_via_composio: Some(true),
};
let json = serde_json::to_string(¶ms).unwrap();
assert!(json.contains("Test Server"));
assert!(json.contains("github"));
assert!(json.contains("ac_123"));
}
#[test]
fn test_mcp_delete_response_deserialization() {
let json = r#"{
"id": "mcp_789",
"deleted": true
}"#;
let response: MCPDeleteResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.id, "mcp_789");
assert!(response.deleted);
}
}