Use MCP server instructions in deferred namespace descriptions (#21053)

## Why

MCP servers can provide `instructions` that explain what their tools are
for. Directly exposed MCP namespaces already use those instructions when
a connector description is not available, but deferred `tool_search`
results did not preserve that fallback. The direct path falls back from
connector metadata to server instructions, while the deferred path only
carried `connector_description` and otherwise fell back to generic
namespace text.

That meant a plain MCP server could provide useful model-facing guidance
and still appear as `Tools in the X namespace.` whenever it was
discovered lazily through `tool_search`.

## What changed

- Store one model-facing `namespace_description` on `ToolInfo`, using
connector descriptions for connector-backed tools and server
instructions for plain MCP servers.
- Thread that namespace description through the `tool_search` source
list, search indexing, and returned namespace metadata.
- Add an end-to-end regression test for deferred non-app MCP search
results exposing server instructions as the namespace description.

## Verification

- `cargo test -p codex-tools
search_tool_description_lists_each_mcp_source_once --lib`
- `cargo test -p codex-core --test all
tool_search_uses_non_app_mcp_server_instructions_as_namespace_description`
This commit is contained in:
sayan-oai
2026-05-04 19:36:07 +00:00
committed by GitHub
parent 48402be6fa
commit b9e8df47da
16 changed files with 138 additions and 52 deletions
+9 -5
View File
@@ -27,7 +27,7 @@ pub struct ToolSearchSourceInfo {
pub struct ToolSearchSource<'a> {
pub server_name: &'a str,
pub connector_name: Option<&'a str>,
pub connector_description: Option<&'a str>,
pub description: Option<&'a str>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -37,7 +37,7 @@ pub struct ToolSearchResultSource<'a> {
pub tool_name: &'a str,
pub tool: &'a rmcp::model::Tool,
pub connector_name: Option<&'a str>,
pub connector_description: Option<&'a str>,
pub description: Option<&'a str>,
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
@@ -215,7 +215,7 @@ pub fn tool_search_result_source_to_loadable_tool_spec(
fn tool_search_result_source_namespace_description(source: ToolSearchResultSource<'_>) -> String {
source
.connector_description
.description
.map(str::trim)
.filter(|description| !description.is_empty())
.map(str::to_string)
@@ -251,7 +251,7 @@ pub fn collect_tool_search_source_infos<'a>(
return Some(ToolSearchSourceInfo {
name: name.to_string(),
description: tool
.connector_description
.description
.map(str::trim)
.filter(|description| !description.is_empty())
.map(str::to_string),
@@ -265,7 +265,11 @@ pub fn collect_tool_search_source_infos<'a>(
Some(ToolSearchSourceInfo {
name: name.to_string(),
description: None,
description: tool
.description
.map(str::trim)
.filter(|description| !description.is_empty())
.map(str::to_string),
})
})
.collect()
+1 -1
View File
@@ -282,7 +282,7 @@ pub fn build_tool_registry_plan(
ToolSearchSource {
server_name: tool.server_name,
connector_name: tool.connector_name,
connector_description: tool.connector_description,
description: tool.description,
}
}))
})
@@ -1414,7 +1414,7 @@ fn search_tool_description_lists_each_mcp_source_once() {
"mcp__rmcp__",
"rmcp",
/*connector_name*/ None,
/*connector_description*/ None,
Some("Remote memory tools."),
),
]),
&[],
@@ -1433,7 +1433,7 @@ fn search_tool_description_lists_each_mcp_source_once() {
.count(),
1
);
assert!(description.contains("- rmcp"));
assert!(description.contains("- rmcp: Remote memory tools."));
assert!(!description.contains("mcp__rmcp__echo"));
assert!(handlers.contains(&ToolHandlerSpec {
@@ -1454,7 +1454,7 @@ fn search_tool_requires_model_capability_and_enabled_feature() {
"mcp__codex_apps__calendar",
CODEX_APPS_MCP_SERVER_NAME,
Some("Calendar"),
/*connector_description*/ None,
/*description*/ None,
)]);
let features = Features::with_defaults();
@@ -2355,13 +2355,13 @@ fn deferred_mcp_tool<'a>(
tool_namespace: &'a str,
server_name: &'a str,
connector_name: Option<&'a str>,
connector_description: Option<&'a str>,
description: Option<&'a str>,
) -> ToolRegistryPlanDeferredTool<'a> {
ToolRegistryPlanDeferredTool {
name: ToolName::namespaced(tool_namespace, tool_name),
server_name,
connector_name,
connector_description,
description,
}
}
@@ -85,7 +85,7 @@ pub struct ToolRegistryPlanDeferredTool<'a> {
pub name: ToolName,
pub server_name: &'a str,
pub connector_name: Option<&'a str>,
pub connector_description: Option<&'a str>,
pub description: Option<&'a str>,
}
impl ToolRegistryPlan {