mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[mcp] Expand tool search to custom MCPs. (#16944)
- [x] Expand tool search to custom MCPs.
- [x] Rename several variables/fields to be more generic.
Updated tool & server name lifecycles:
**Raw Identity**
ToolInfo.server_name is raw MCP server name.
ToolInfo.tool.name is raw MCP tool name.
MCP calls route back to raw via parse_tool_name() returning
(tool.server_name, tool.tool.name).
mcpServerStatus/list now groups by raw server and keys tools by
Tool.name: mod.rs:599
App-server just forwards that grouped raw snapshot:
codex_message_processor.rs:5245
**Callable Names**
On list-tools, we create provisional callable_namespace / callable_name:
mcp_connection_manager.rs:1556
For non-app MCP, provisional callable name starts as raw tool name.
For codex-apps, provisional callable name is sanitized and strips
connector name/id prefix; namespace includes connector name.
Then qualify_tools() sanitizes callable namespace + name to ASCII alnum
/ _ only: mcp_tool_names.rs:128
Note: this is stricter than Responses API. Hyphen is currently replaced
with _ for code-mode compatibility.
**Collision Handling**
We do initially collapse example-server and example_server to the same
base.
Then qualify_tools() detects distinct raw namespace identities behind
the same sanitized namespace and appends a hash to the callable
namespace: mcp_tool_names.rs:137
Same idea for tool-name collisions: hash suffix goes on callable tool
name.
Final list_all_tools() map key is callable_namespace + callable_name:
mcp_connection_manager.rs:769
**Direct Model Tools**
Direct MCP tool declarations use the full qualified sanitized key as the
Responses function name.
The raw rmcp Tool is converted but renamed for model exposure.
**Tool Search / Deferred**
Tool search result namespace = final ToolInfo.callable_namespace:
tool_search.rs:85
Tool search result nested name = final ToolInfo.callable_name:
tool_search.rs:86
Deferred tool handler is registered as "{namespace}:{name}":
tool_registry_plan.rs:248
When a function call comes back, core recombines namespace + name, looks
up the full qualified key, and gets the raw server/tool for MCP
execution: codex.rs:4353
**Separate Legacy Snapshot**
collect_mcp_snapshot_from_manager_with_detail() still returns a map
keyed by qualified callable name.
mcpServerStatus/list no longer uses that; it uses
McpServerStatusSnapshot, which is raw-inventory shaped.
This commit is contained in:
committed by
GitHub
Unverified
parent
545f3daba0
commit
d7f99b0fa6
@@ -252,11 +252,11 @@ use codex_login::default_client::set_default_client_residency_requirement;
|
||||
use codex_login::login_with_api_key;
|
||||
use codex_login::request_device_code;
|
||||
use codex_login::run_login_server;
|
||||
use codex_mcp::McpServerStatusSnapshot;
|
||||
use codex_mcp::McpSnapshotDetail;
|
||||
use codex_mcp::collect_mcp_snapshot_with_detail;
|
||||
use codex_mcp::collect_mcp_server_status_snapshot_with_detail;
|
||||
use codex_mcp::discover_supported_scopes;
|
||||
use codex_mcp::effective_mcp_servers;
|
||||
use codex_mcp::qualified_mcp_tool_name_prefix;
|
||||
use codex_mcp::resolve_oauth_scopes;
|
||||
use codex_models_manager::collaboration_mode_presets::CollaborationModesConfig;
|
||||
use codex_protocol::ThreadId;
|
||||
@@ -5187,7 +5187,7 @@ impl CodexMessageProcessor {
|
||||
McpServerStatusDetail::ToolsAndAuthOnly => McpSnapshotDetail::ToolsAndAuthOnly,
|
||||
};
|
||||
|
||||
let snapshot = collect_mcp_snapshot_with_detail(
|
||||
let snapshot = collect_mcp_server_status_snapshot_with_detail(
|
||||
&mcp_config,
|
||||
auth.as_ref(),
|
||||
request_id.request_id.to_string(),
|
||||
@@ -5195,47 +5195,13 @@ impl CodexMessageProcessor {
|
||||
)
|
||||
.await;
|
||||
|
||||
// Rebuild the tool list per original server name instead of using
|
||||
// `group_tools_by_server()`: qualified tool names are sanitized for the
|
||||
// Responses API, so a config key like `some-server` is encoded as the
|
||||
// `mcp__some_server__` prefix. Matching with the original server name's
|
||||
// sanitized prefix preserves `/mcp` output for hyphenated names.
|
||||
let effective_servers = effective_mcp_servers(&mcp_config, auth.as_ref());
|
||||
let mut sanitized_prefix_counts = HashMap::<String, usize>::new();
|
||||
for name in effective_servers.keys() {
|
||||
let prefix = qualified_mcp_tool_name_prefix(name);
|
||||
*sanitized_prefix_counts.entry(prefix).or_default() += 1;
|
||||
}
|
||||
let tools_by_server = effective_servers
|
||||
.keys()
|
||||
.map(|name| {
|
||||
let prefix = qualified_mcp_tool_name_prefix(name);
|
||||
// If multiple server names normalize to the same prefix, the
|
||||
// qualified tool namespace is ambiguous (for example
|
||||
// `some-server` and `some_server` both become
|
||||
// `mcp__some_server__`). In that case, avoid attributing the
|
||||
// same tools to multiple servers.
|
||||
let tools = if sanitized_prefix_counts
|
||||
.get(&prefix)
|
||||
.copied()
|
||||
.unwrap_or_default()
|
||||
== 1
|
||||
{
|
||||
snapshot
|
||||
.tools
|
||||
.iter()
|
||||
.filter_map(|(qualified_name, tool)| {
|
||||
qualified_name
|
||||
.strip_prefix(&prefix)
|
||||
.map(|tool_name| (tool_name.to_string(), tool.clone()))
|
||||
})
|
||||
.collect::<HashMap<_, _>>()
|
||||
} else {
|
||||
HashMap::new()
|
||||
};
|
||||
(name.clone(), tools)
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
let McpServerStatusSnapshot {
|
||||
tools_by_server,
|
||||
resources,
|
||||
resource_templates,
|
||||
auth_statuses,
|
||||
} = snapshot;
|
||||
|
||||
let mut server_names: Vec<String> = config
|
||||
.mcp_servers
|
||||
@@ -5245,9 +5211,9 @@ impl CodexMessageProcessor {
|
||||
// effective runtime config even when they are not user-declared in
|
||||
// `config.mcp_servers`.
|
||||
.chain(effective_servers.keys().cloned())
|
||||
.chain(snapshot.auth_statuses.keys().cloned())
|
||||
.chain(snapshot.resources.keys().cloned())
|
||||
.chain(snapshot.resource_templates.keys().cloned())
|
||||
.chain(auth_statuses.keys().cloned())
|
||||
.chain(resources.keys().cloned())
|
||||
.chain(resource_templates.keys().cloned())
|
||||
.collect();
|
||||
server_names.sort();
|
||||
server_names.dedup();
|
||||
@@ -5288,14 +5254,9 @@ impl CodexMessageProcessor {
|
||||
.map(|name| McpServerStatus {
|
||||
name: name.clone(),
|
||||
tools: tools_by_server.get(name).cloned().unwrap_or_default(),
|
||||
resources: snapshot.resources.get(name).cloned().unwrap_or_default(),
|
||||
resource_templates: snapshot
|
||||
.resource_templates
|
||||
.get(name)
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
auth_status: snapshot
|
||||
.auth_statuses
|
||||
resources: resources.get(name).cloned().unwrap_or_default(),
|
||||
resource_templates: resource_templates.get(name).cloned().unwrap_or_default(),
|
||||
auth_status: auth_statuses
|
||||
.get(name)
|
||||
.cloned()
|
||||
.unwrap_or(CoreMcpAuthStatus::Unsupported)
|
||||
|
||||
Reference in New Issue
Block a user