use codex_app_server_protocol::AppInfo; use serde::Deserialize; use serde::Serialize; const TUI_CLIENT_NAME: &str = "codex-tui"; pub const TOOL_SEARCH_TOOL_NAME: &str = "tool_search"; pub const TOOL_SEARCH_DEFAULT_LIMIT: usize = 8; pub const LIST_AVAILABLE_PLUGINS_TO_INSTALL_TOOL_NAME: &str = "list_available_plugins_to_install"; pub const REQUEST_PLUGIN_INSTALL_TOOL_NAME: &str = "request_plugin_install"; #[derive(Clone, Debug, PartialEq, Eq)] pub struct ToolSearchSourceInfo { pub name: String, pub description: Option, } #[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum DiscoverableToolType { Connector, Plugin, } #[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum DiscoverableToolAction { Install, Enable, } #[derive(Clone, Debug, PartialEq)] pub enum DiscoverableTool { Connector(Box), Plugin(Box), } impl DiscoverableTool { pub fn tool_type(&self) -> DiscoverableToolType { match self { Self::Connector(_) => DiscoverableToolType::Connector, Self::Plugin(_) => DiscoverableToolType::Plugin, } } pub fn id(&self) -> &str { match self { Self::Connector(connector) => connector.id.as_str(), Self::Plugin(plugin) => plugin.id.as_str(), } } pub fn name(&self) -> &str { match self { Self::Connector(connector) => connector.name.as_str(), Self::Plugin(plugin) => plugin.name.as_str(), } } pub fn install_url(&self) -> Option<&str> { match self { Self::Connector(connector) => connector.install_url.as_deref(), Self::Plugin(_) => None, } } } impl From for DiscoverableTool { fn from(value: AppInfo) -> Self { Self::Connector(Box::new(value)) } } impl From for DiscoverableTool { fn from(value: DiscoverablePluginInfo) -> Self { Self::Plugin(Box::new(value)) } } pub fn filter_request_plugin_install_discoverable_tools_for_client( discoverable_tools: Vec, app_server_client_name: Option<&str>, ) -> Vec { if app_server_client_name != Some(TUI_CLIENT_NAME) { return discoverable_tools; } discoverable_tools .into_iter() .filter(|tool| !matches!(tool, DiscoverableTool::Plugin(_))) .collect() } #[derive(Clone, Debug, PartialEq, Eq)] pub struct DiscoverablePluginInfo { pub id: String, pub name: String, pub description: Option, pub has_skills: bool, pub mcp_server_names: Vec, pub app_connector_ids: Vec, } #[derive(Clone, Debug, Serialize, PartialEq, Eq)] pub struct RequestPluginInstallEntry { pub id: String, pub name: String, pub description: Option, pub tool_type: DiscoverableToolType, pub has_skills: bool, pub mcp_server_names: Vec, pub app_connector_ids: Vec, } #[derive(Clone, Debug, Serialize, PartialEq, Eq)] pub struct ListAvailablePluginsToInstallResult { pub tools: Vec, } pub fn collect_request_plugin_install_entries( discoverable_tools: &[DiscoverableTool], ) -> Vec { discoverable_tools .iter() .map(|tool| match tool { DiscoverableTool::Connector(connector) => RequestPluginInstallEntry { id: connector.id.clone(), name: connector.name.clone(), description: connector.description.clone(), tool_type: DiscoverableToolType::Connector, has_skills: false, mcp_server_names: Vec::new(), app_connector_ids: Vec::new(), }, DiscoverableTool::Plugin(plugin) => RequestPluginInstallEntry { id: plugin.id.clone(), name: plugin.name.clone(), description: plugin.description.clone(), tool_type: DiscoverableToolType::Plugin, has_skills: plugin.has_skills, mcp_server_names: plugin.mcp_server_names.clone(), app_connector_ids: plugin.app_connector_ids.clone(), }, }) .collect() } #[cfg(test)] #[path = "tool_discovery_tests.rs"] mod tests;