core: limit search_tool_bm25 to Apps and clarify discovery guidance (#11669)

## Summary
- Limit `search_tool_bm25` indexing to `codex_apps` tools only, so
non-Apps MCP servers are no longer discoverable through this search
path.
- Move search-tool discovery guidance into the `search_tool_bm25` tool
description (via template include) instead of injecting it as a separate
developer message.
- Update Apps discovery guidance wording to clarify when to use
`search_tool_bm25` for Apps-backed systems (for example Slack, Google
Drive, Jira, Notion) and when to call tools directly.
- Remove dead `core` helper code (`filter_codex_apps_mcp_tools` and
`codex_apps_connector_id`) that is no longer used after the
tool-selection refactor.
- Update `core` search-tool tests to assert codex-apps-only behavior and
to validate guidance from the tool description.

## Validation
-  `just fmt`
-  `cargo test -p codex-core search_tool`
- ⚠️ `cargo test -p codex-core` was attempted, but the run repeatedly
stalled on
`tools::js_repl::tests::js_repl_can_attach_image_via_view_image_tool`.

## Tickets
- None
This commit is contained in:
Anton Panasenko
2026-02-13 09:32:46 -08:00
committed by GitHub
parent c0749c349f
commit 38c442ca7f
9 changed files with 287 additions and 176 deletions

View File

@@ -522,9 +522,6 @@ pub(crate) struct Session {
next_internal_sub_id: AtomicU64,
}
const SEARCH_TOOL_DEVELOPER_INSTRUCTIONS: &str =
include_str!("../templates/search_tool/developer_instructions.md");
/// The context needed for a single turn of the thread.
#[derive(Debug)]
pub(crate) struct TurnContext {
@@ -2509,11 +2506,6 @@ impl Session {
if let Some(developer_instructions) = turn_context.developer_instructions.as_deref() {
items.push(DeveloperInstructions::new(developer_instructions.to_string()).into());
}
if turn_context.tools_config.search_tool {
items.push(
DeveloperInstructions::new(SEARCH_TOOL_DEVELOPER_INSTRUCTIONS.to_string()).into(),
);
}
// Add developer instructions for memories.
if let Some(memory_prompt) =
build_memory_tool_developer_instructions(&turn_context.config.codex_home).await
@@ -4549,14 +4541,15 @@ fn collect_explicit_app_ids_from_skill_items(
}
fn filter_connectors_for_input(
connectors: Vec<connectors::AppInfo>,
connectors: &[connectors::AppInfo],
input: &[ResponseItem],
explicitly_enabled_connectors: &HashSet<String>,
skill_name_counts_lower: &HashMap<String, usize>,
) -> Vec<connectors::AppInfo> {
let connectors = connectors
.into_iter()
let connectors: Vec<connectors::AppInfo> = connectors
.iter()
.filter(|connector| connector.is_enabled)
.cloned()
.collect::<Vec<_>>();
if connectors.is_empty() {
return Vec::new();
@@ -4624,7 +4617,7 @@ fn connector_inserted_in_messages(
}
fn filter_codex_apps_mcp_tools(
mut mcp_tools: HashMap<String, crate::mcp_connection_manager::ToolInfo>,
mcp_tools: &HashMap<String, crate::mcp_connection_manager::ToolInfo>,
connectors: &[connectors::AppInfo],
) -> HashMap<String, crate::mcp_connection_manager::ToolInfo> {
let allowed: HashSet<&str> = connectors
@@ -4632,17 +4625,19 @@ fn filter_codex_apps_mcp_tools(
.map(|connector| connector.id.as_str())
.collect();
mcp_tools.retain(|_, tool| {
if tool.server_name != CODEX_APPS_MCP_SERVER_NAME {
return true;
}
let Some(connector_id) = codex_apps_connector_id(tool) else {
return false;
};
allowed.contains(connector_id)
});
mcp_tools
.iter()
.filter(|(_, tool)| {
if tool.server_name != CODEX_APPS_MCP_SERVER_NAME {
return true;
}
let Some(connector_id) = codex_apps_connector_id(tool) else {
return false;
};
allowed.contains(connector_id)
})
.map(|(name, tool)| (name.clone(), tool.clone()))
.collect()
}
fn codex_apps_connector_id(tool: &crate::mcp_connection_manager::ToolInfo) -> Option<&str> {
@@ -4804,42 +4799,45 @@ async fn built_tools(
let mut effective_explicitly_enabled_connectors = explicitly_enabled_connectors.clone();
effective_explicitly_enabled_connectors.extend(sess.get_connector_selection().await);
let connectors_for_tools = if turn_context.config.features.enabled(Feature::Apps) {
let skill_name_counts_lower = skills_outcome.map_or_else(HashMap::new, |outcome| {
build_skill_name_counts(&outcome.skills, &outcome.disabled_paths).1
});
let connectors = connectors::with_app_enabled_state(
let connectors = if turn_context.features.enabled(Feature::Apps) {
Some(connectors::with_app_enabled_state(
connectors::accessible_connectors_from_mcp_tools(&mcp_tools),
&turn_context.config,
);
Some(filter_connectors_for_input(
connectors,
input,
&effective_explicitly_enabled_connectors,
&skill_name_counts_lower,
))
} else {
None
};
if turn_context.config.features.enabled(Feature::Apps) {
if let Some(connectors) = connectors.as_ref() {
let skill_name_counts_lower = skills_outcome.map_or_else(HashMap::new, |outcome| {
build_skill_name_counts(&outcome.skills, &outcome.disabled_paths).1
});
let explicitly_enabled = filter_connectors_for_input(
connectors,
input,
&effective_explicitly_enabled_connectors,
&skill_name_counts_lower,
);
let mut selected_mcp_tools =
if let Some(selected_tools) = sess.get_mcp_tool_selection().await {
filter_mcp_tools_by_name(mcp_tools.clone(), &selected_tools)
filter_mcp_tools_by_name(&mcp_tools, &selected_tools)
} else {
HashMap::new()
};
if let Some(connectors) = connectors_for_tools.as_ref() {
let apps_mcp_tools = filter_codex_apps_mcp_tools_only(mcp_tools, connectors);
selected_mcp_tools.extend(apps_mcp_tools);
}
let apps_mcp_tools =
filter_codex_apps_mcp_tools_only(&mcp_tools, explicitly_enabled.as_ref());
selected_mcp_tools.extend(apps_mcp_tools);
mcp_tools = selected_mcp_tools;
} else if let Some(connectors) = connectors_for_tools.as_ref() {
mcp_tools = filter_codex_apps_mcp_tools(mcp_tools, connectors);
}
let app_tools = connectors
.as_ref()
.map(|connectors| filter_codex_apps_mcp_tools(&mcp_tools, connectors));
Ok(Arc::new(ToolRouter::from_config(
&turn_context.tools_config,
Some(
@@ -4848,6 +4846,7 @@ async fn built_tools(
.map(|(name, tool)| (name, tool.tool))
.collect(),
),
app_tools,
turn_context.dynamic_tools.as_slice(),
)))
}
@@ -5803,7 +5802,7 @@ mod tests {
let skill_name_counts_lower = HashMap::new();
let selected = filter_connectors_for_input(
connectors,
&connectors,
&input,
&explicitly_enabled_connectors,
&skill_name_counts_lower,
@@ -5820,7 +5819,7 @@ mod tests {
let skill_name_counts_lower = HashMap::from([("todoist".to_string(), 1)]);
let selected = filter_connectors_for_input(
connectors,
&connectors,
&input,
&explicitly_enabled_connectors,
&skill_name_counts_lower,
@@ -5836,7 +5835,7 @@ mod tests {
let input = vec![user_message("use $calendar")];
let explicitly_enabled_connectors = HashSet::new();
let selected = filter_connectors_for_input(
vec![connector],
&[connector],
&input,
&explicitly_enabled_connectors,
&HashMap::new(),
@@ -5910,17 +5909,16 @@ mod tests {
),
]);
let mut selected_mcp_tools =
filter_mcp_tools_by_name(mcp_tools.clone(), &selected_tool_names);
let mut selected_mcp_tools = filter_mcp_tools_by_name(&mcp_tools, &selected_tool_names);
let connectors = connectors::accessible_connectors_from_mcp_tools(&mcp_tools);
let explicitly_enabled_connectors = HashSet::new();
let connectors = filter_connectors_for_input(
connectors,
&connectors,
&[user_message("run the selected tools")],
&explicitly_enabled_connectors,
&HashMap::new(),
);
let apps_mcp_tools = filter_codex_apps_mcp_tools_only(mcp_tools, &connectors);
let apps_mcp_tools = filter_codex_apps_mcp_tools_only(&mcp_tools, &connectors);
selected_mcp_tools.extend(apps_mcp_tools);
let mut tool_names: Vec<String> = selected_mcp_tools.into_keys().collect();
@@ -5953,17 +5951,16 @@ mod tests {
),
]);
let mut selected_mcp_tools =
filter_mcp_tools_by_name(mcp_tools.clone(), &selected_tool_names);
let mut selected_mcp_tools = filter_mcp_tools_by_name(&mcp_tools, &selected_tool_names);
let connectors = connectors::accessible_connectors_from_mcp_tools(&mcp_tools);
let explicitly_enabled_connectors = HashSet::new();
let connectors = filter_connectors_for_input(
connectors,
&connectors,
&[user_message("use $calendar and then echo the response")],
&explicitly_enabled_connectors,
&HashMap::new(),
);
let apps_mcp_tools = filter_codex_apps_mcp_tools_only(mcp_tools, &connectors);
let apps_mcp_tools = filter_codex_apps_mcp_tools_only(&mcp_tools, &connectors);
selected_mcp_tools.extend(apps_mcp_tools);
let mut tool_names: Vec<String> = selected_mcp_tools.into_keys().collect();
@@ -7627,6 +7624,7 @@ mod tests {
.list_all_tools()
.await
};
let app_tools = Some(tools.clone());
let router = ToolRouter::from_config(
&turn_context.tools_config,
Some(
@@ -7635,6 +7633,7 @@ mod tests {
.map(|(name, tool)| (name, tool.tool))
.collect(),
),
app_tools,
turn_context.dynamic_tools.as_slice(),
);
let item = ResponseItem::CustomToolCall {