[plugins] Additional gating for tool suggest and apps. (#15573)

- [x] Additional gating for tool suggest and apps.
This commit is contained in:
Matthew Zeng
2026-03-24 15:10:00 -07:00
committed by GitHub
Unverified
parent 4b91a7b391
commit b32d921cd9
5 changed files with 139 additions and 4 deletions
+64 -2
View File
@@ -1,10 +1,72 @@
use crate::mcp::CODEX_APPS_MCP_SERVER_NAME;
use codex_app_server_protocol::AppInfo;
use codex_protocol::protocol::APPS_INSTRUCTIONS_CLOSE_TAG;
use codex_protocol::protocol::APPS_INSTRUCTIONS_OPEN_TAG;
pub(crate) fn render_apps_section() -> String {
pub(crate) fn render_apps_section(connectors: &[AppInfo]) -> Option<String> {
if !connectors
.iter()
.any(|connector| connector.is_accessible && connector.is_enabled)
{
return None;
}
let body = format!(
"## Apps (Connectors)\nApps (Connectors) can be explicitly triggered in user messages in the format `[$app-name](app://{{connector_id}})`. Apps can also be implicitly triggered as long as the context suggests usage of available apps, the available apps will be listed by the `tool_search` tool.\nAn app is equivalent to a set of MCP tools within the `{CODEX_APPS_MCP_SERVER_NAME}` MCP.\nAn installed app's MCP tools are either provided to you already, or can be lazy-loaded through the `tool_search` tool.\nDo not additionally call list_mcp_resources or list_mcp_resource_templates for apps."
);
format!("{APPS_INSTRUCTIONS_OPEN_TAG}\n{body}\n{APPS_INSTRUCTIONS_CLOSE_TAG}")
Some(format!(
"{APPS_INSTRUCTIONS_OPEN_TAG}\n{body}\n{APPS_INSTRUCTIONS_CLOSE_TAG}"
))
}
#[cfg(test)]
mod tests {
use super::*;
fn connector(id: &str, is_accessible: bool, is_enabled: bool) -> AppInfo {
AppInfo {
id: id.to_string(),
name: id.to_string(),
description: None,
logo_url: None,
logo_url_dark: None,
distribution_channel: None,
branding: None,
app_metadata: None,
labels: None,
install_url: None,
is_accessible,
is_enabled,
plugin_display_names: Vec::new(),
}
}
#[test]
fn omits_apps_section_without_accessible_and_enabled_apps() {
assert_eq!(render_apps_section(&[]), None);
assert_eq!(
render_apps_section(&[connector(
"calendar", /*is_accessible*/ true, /*is_enabled*/ false
)]),
None
);
assert_eq!(
render_apps_section(&[connector(
"calendar", /*is_accessible*/ false, /*is_enabled*/ true
)]),
None
);
}
#[test]
fn renders_apps_section_with_an_accessible_and_enabled_app() {
let rendered = render_apps_section(&[connector(
"calendar", /*is_accessible*/ true, /*is_enabled*/ true,
)])
.expect("expected apps section");
assert!(rendered.starts_with(APPS_INSTRUCTIONS_OPEN_TAG));
assert!(rendered.contains("## Apps (Connectors)"));
assert!(rendered.ends_with(APPS_INSTRUCTIONS_CLOSE_TAG));
}
}