[tool search] support namespaced deferred dynamic tools#18413
Conversation
|
@codex review |
|
Codex Review: Didn't find any major issues. 🎉 ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
9d8f242 to
ad75171
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
codex/codex-rs/app-server/src/codex_message_processor.rs
Lines 9206 to 9208 in ad75171
validate_dynamic_tools only checks tool.name for reserved mcp prefixes and duplicate conflicts, but never validates tool.namespace. A dynamic tool can therefore use an MCP namespace/name pair and collide with real MCP tools. That key is later registered as a dynamic handler and can overwrite the existing handler, causing tool calls to be routed to the wrong backend.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
what is this used for that we don't care about ns?
There was a problem hiding this comment.
when making the actual leaf ResponsesApiTool function, we dont care about the namespace, because these leaf functions are contained in a ResponsesApiNamespace where the namespace is captured.
mcp tools do the same thing here (ignoring namespace when making ResponsesApiTool).
ad75171 to
7b3d791
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7b3d791662
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
7b3d791 to
984d794
Compare
|
@codex review |
984d794 to
d8411aa
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 984d7945c7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
9f7fd1f to
b619c18
Compare
|
@codex review |
Review result: changes requestedI re-reviewed the PR state at 1) P2 —
|
b619c18 to
61927ed
Compare
61927ed to
7a8eb68
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7a8eb680cb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| { | ||
| tool_name.display() | ||
| } else { | ||
| tool_name.name.clone() |
There was a problem hiding this comment.
Preserve namespaces in code-mode tool names
Return a fully-qualified name for non-MCP namespaced tools. Flattening to tool_name.name makes different tools share the same code-mode identifier (e.g. shell), and later dedup/lookup logic is name-based. This can hide one tool from ALL_TOOLS and route tools.<name>(...) to the wrong handler when names collide across namespaces or with built-ins.
Useful? React with 👍 / 👎.
|
|
||
| fn dynamic_tool_search_entry(tool: &DynamicToolSpec) -> Result<ToolSearchEntry, serde_json::Error> { | ||
| let output_tool = dynamic_tool_to_responses_api_tool(tool)?; | ||
| let output = match tool.namespace.as_ref() { |
There was a problem hiding this comment.
because dynamic tools can now be namespaced
| Some(namespace) => format!("{namespace}_{}", tool_name.name), | ||
| None => tool_name.name.clone(), | ||
| } |
There was a problem hiding this comment.
localized to code_mode; this makes it so the functions created from namespaced tools look reasonable (codex_apps_foo_tool rather than codex_appsfoo_tool)
| } | ||
|
|
||
| fn dynamic_tool_search_entry(tool: &DynamicToolSpec) -> Result<ToolSearchEntry, serde_json::Error> { | ||
| let output_tool = dynamic_tool_to_responses_api_tool(tool)?; |
There was a problem hiding this comment.
really dislike how many layers of transformations we go through to build a tool definition.
There was a problem hiding this comment.
reworked to go direct from DynamicToolSpec -> ResponsesApiTool.
renamed ToolSearchOutputTool to LoadableToolSpec (lmk if there's a clearer name) as a side effect so we can use this across dynamic tool parsing in tool search and in tool registration.
|
@codex review this |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: df7c8410fc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| pub fn code_mode_name_for_tool_name(tool_name: &ToolName) -> String { | ||
| match tool_name.namespace.as_deref() { | ||
| Some(namespace) if namespace.ends_with('_') || tool_name.name.starts_with('_') => { | ||
| format!("{namespace}{}", tool_name.name) | ||
| } | ||
| Some(namespace) => format!("{namespace}_{}", tool_name.name), | ||
| None => tool_name.name.clone(), |
There was a problem hiding this comment.
Make code-mode tool name mapping collision-free
code_mode_name_for_tool_name is not one-to-one. Different (namespace, name) pairs can produce the same code-mode name (e.g. (a, b_c) and (a_b, c) both map to a_b_c). Code mode uses this value as the tools object key, so later tools overwrite earlier ones and one callable becomes unreachable.
Useful? React with 👍 / 👎.
| if namespace == "mcp" || namespace.starts_with("mcp__") { | ||
| return Err(format!( | ||
| "dynamic tool namespace is reserved for {name}: {namespace}" | ||
| )); | ||
| } | ||
| } | ||
| if !seen.insert((namespace, name)) { | ||
| if let Some(namespace) = namespace { |
There was a problem hiding this comment.
Reject dynamic tools that shadow existing namespaced tools
Validation only blocks mcp* namespaces and duplicate names within the dynamic list. It allows a dynamic tool to reuse another registered (namespace, name) pair (e.g. connector namespaces), and handler registration then overwrites the existing handler for that key. Calls can be silently routed to the wrong tool.
Useful? React with 👍 / 👎.
| for tool in params.dynamic_tools { | ||
| match dynamic_tool_to_responses_api_tool(tool) { | ||
| Ok(converted_tool) => { | ||
| match dynamic_tool_to_loadable_tool_spec(tool) { | ||
| Ok(loadable_tool) => { | ||
| let handler_name = ToolName::new(tool.namespace.clone(), tool.name.clone()); | ||
| plan.push_spec( | ||
| ToolSpec::Function(converted_tool), | ||
| loadable_tool.into(), | ||
| /*supports_parallel_tool_calls*/ false, |
There was a problem hiding this comment.
Coalesce duplicate dynamic namespace specs before registration
build_tool_registry_plan pushes each namespaced dynamic tool as its own ToolSpec::Namespace. Multiple tools in one namespace therefore emit repeated namespace wrappers, unlike MCP where entries are grouped first. This bloats tool payloads and can create ambiguous duplicate namespace declarations in model-visible specs.
Useful? React with 👍 / 👎.
| } | ||
| } | ||
|
|
||
| impl From<LoadableToolSpec> for ToolSpec { |
There was a problem hiding this comment.
Why aren't we using ToolSpec directly?
pakrym-oai
left a comment
There was a problem hiding this comment.
Cleaner! Please check the namespace deduplication comment that codex left. Sounds plausible.
Deferred dynamic tools need to round-trip a namespace so a tool returned by
tool_searchcan be called through the same registry key that core uses for dispatch.This change adds namespace support for dynamic tool specs/calls, persists it through app-server thread state, and routes dynamic tool calls by full
ToolNamewhile still sending the app the leaf tool name. Deferred dynamic tools must provide a namespace; non-deferred dynamic tools may remain top-level.It also introduces
LoadableToolSpecas the shared function-or-namespace Responses shape used by bothtool_searchoutput and dynamic tool registration, so dynamic tools use the same wrapping logic in both paths.Validation:
cargo test -p codex-toolscargo test -p codex-core tool_search