codex_tools/
tool_discovery.rs1use 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;