Skip to main content

codex_tools/
tool_discovery.rs

1use codex_connectors::AppInfo;
2use serde::Deserialize;
3use serde::Serialize;
4
5const TUI_CLIENT_NAME: &str = "codex-tui";
6pub const TOOL_SEARCH_TOOL_NAME: &str = "tool_search";
7pub const TOOL_SEARCH_DEFAULT_LIMIT: usize = 8;
8pub const LIST_AVAILABLE_PLUGINS_TO_INSTALL_TOOL_NAME: &str = "list_available_plugins_to_install";
9pub const REQUEST_PLUGIN_INSTALL_TOOL_NAME: &str = "request_plugin_install";
10
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct ToolSearchSourceInfo {
13    pub name: String,
14    pub description: Option<String>,
15}
16
17#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
18#[serde(rename_all = "snake_case")]
19pub enum DiscoverableToolType {
20    Connector,
21    Plugin,
22}
23
24#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
25#[serde(rename_all = "snake_case")]
26pub enum DiscoverableToolAction {
27    Install,
28    Enable,
29}
30
31#[derive(Clone, Debug, PartialEq)]
32pub enum DiscoverableTool {
33    Connector(Box<AppInfo>),
34    Plugin(Box<DiscoverablePluginInfo>),
35}
36
37impl DiscoverableTool {
38    pub fn tool_type(&self) -> DiscoverableToolType {
39        match self {
40            Self::Connector(_) => DiscoverableToolType::Connector,
41            Self::Plugin(_) => DiscoverableToolType::Plugin,
42        }
43    }
44
45    pub fn id(&self) -> &str {
46        match self {
47            Self::Connector(connector) => connector.id.as_str(),
48            Self::Plugin(plugin) => plugin.id.as_str(),
49        }
50    }
51
52    pub fn name(&self) -> &str {
53        match self {
54            Self::Connector(connector) => connector.name.as_str(),
55            Self::Plugin(plugin) => plugin.name.as_str(),
56        }
57    }
58
59    pub fn install_url(&self) -> Option<&str> {
60        match self {
61            Self::Connector(connector) => connector.install_url.as_deref(),
62            Self::Plugin(_) => None,
63        }
64    }
65}
66
67impl From<AppInfo> for DiscoverableTool {
68    fn from(value: AppInfo) -> Self {
69        Self::Connector(Box::new(value))
70    }
71}
72
73impl From<DiscoverablePluginInfo> for DiscoverableTool {
74    fn from(value: DiscoverablePluginInfo) -> Self {
75        Self::Plugin(Box::new(value))
76    }
77}
78
79pub fn filter_request_plugin_install_discoverable_tools_for_client(
80    discoverable_tools: Vec<DiscoverableTool>,
81    app_server_client_name: Option<&str>,
82) -> Vec<DiscoverableTool> {
83    if app_server_client_name != Some(TUI_CLIENT_NAME) {
84        return discoverable_tools;
85    }
86
87    discoverable_tools
88        .into_iter()
89        .filter(|tool| !matches!(tool, DiscoverableTool::Plugin(_)))
90        .collect()
91}
92
93#[derive(Clone, Debug, PartialEq, Eq)]
94pub struct DiscoverablePluginInfo {
95    pub id: String,
96    pub remote_plugin_id: Option<String>,
97    pub name: String,
98    pub description: Option<String>,
99    pub has_skills: bool,
100    pub mcp_server_names: Vec<String>,
101    pub app_connector_ids: Vec<String>,
102}
103
104#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
105pub struct RequestPluginInstallEntry {
106    pub id: String,
107    pub name: String,
108    pub description: Option<String>,
109    pub tool_type: DiscoverableToolType,
110    pub has_skills: bool,
111    pub mcp_server_names: Vec<String>,
112    pub app_connector_ids: Vec<String>,
113}
114
115#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
116pub struct ListAvailablePluginsToInstallResult {
117    pub tools: Vec<RequestPluginInstallEntry>,
118}
119
120pub fn collect_request_plugin_install_entries(
121    discoverable_tools: &[DiscoverableTool],
122) -> Vec<RequestPluginInstallEntry> {
123    discoverable_tools
124        .iter()
125        .map(|tool| match tool {
126            DiscoverableTool::Connector(connector) => RequestPluginInstallEntry {
127                id: connector.id.clone(),
128                name: connector.name.clone(),
129                description: connector.description.clone(),
130                tool_type: DiscoverableToolType::Connector,
131                has_skills: false,
132                mcp_server_names: Vec::new(),
133                app_connector_ids: Vec::new(),
134            },
135            DiscoverableTool::Plugin(plugin) => RequestPluginInstallEntry {
136                id: plugin.id.clone(),
137                name: plugin.name.clone(),
138                description: plugin.description.clone(),
139                tool_type: DiscoverableToolType::Plugin,
140                has_skills: plugin.has_skills,
141                mcp_server_names: plugin.mcp_server_names.clone(),
142                app_connector_ids: plugin.app_connector_ids.clone(),
143            },
144        })
145        .collect()
146}
147
148#[cfg(test)]
149#[path = "tool_discovery_tests.rs"]
150mod tests;