mirror of
https://github.com/openai/codex.git
synced 2026-05-03 21:01:55 +03:00
[code mode] defer mcp tools from exec description (#17287)
## Summary - hide deferred MCP/app nested tool descriptions from the `exec` prompt in code mode - add short guidance that omitted nested tools are still available through `ALL_TOOLS` - cover the code_mode_only path with an integration test that discovers and calls a deferred app tool ## Motivation `code_mode_only` exposes only top-level `exec`/`wait`, but the `exec` description could still include a large nested-tool reference. This keeps deferred nested tools callable while avoiding that prompt bloat. ## Tests - `just fmt` - `just fix -p codex-code-mode` - `just fix -p codex-tools` - `cargo test -p codex-code-mode exec_description_mentions_deferred_nested_tools_when_available` - `cargo test -p codex-tools create_code_mode_tool_matches_expected_spec` - `cargo test -p codex-core code_mode_only_guides_all_tools_search_and_calls_deferred_app_tools`
This commit is contained in:
@@ -9,6 +9,8 @@ use crate::PUBLIC_TOOL_NAME;
|
||||
const MAX_JS_SAFE_INTEGER: u64 = (1_u64 << 53) - 1;
|
||||
const CODE_MODE_ONLY_PREFACE: &str =
|
||||
"Use `exec/wait` tool to run all other tools, do not attempt to use any other tools directly";
|
||||
const DEFERRED_NESTED_TOOLS_GUIDANCE: &str = r#"Some nested MCP/app tools may be omitted from this description. They are still available on the global `tools` object and listed in `ALL_TOOLS`.
|
||||
To find one, filter `ALL_TOOLS` by `name` and `description`; do not print the full `ALL_TOOLS` array. Print only a small set of relevant matches if you need to inspect them."#;
|
||||
const EXEC_DESCRIPTION_TEMPLATE: &str = r#"Run JavaScript code to orchestrate/compose tool calls
|
||||
- Evaluates the provided JavaScript code in a fresh V8 isolate as an async module.
|
||||
- All nested tools are available on the global `tools` object, for example `await tools.exec_command(...)`. Tool names are exposed as normalized JavaScript identifiers, for example `await tools.mcp__ologs__get_profile(...)`.
|
||||
@@ -251,15 +253,19 @@ pub fn build_exec_tool_description(
|
||||
enabled_tools: &[ToolDefinition],
|
||||
namespace_descriptions: &BTreeMap<String, ToolNamespaceDescription>,
|
||||
code_mode_only: bool,
|
||||
deferred_tools_available: bool,
|
||||
) -> String {
|
||||
if !code_mode_only {
|
||||
return EXEC_DESCRIPTION_TEMPLATE.to_string();
|
||||
let mut sections = Vec::new();
|
||||
if code_mode_only {
|
||||
sections.push(CODE_MODE_ONLY_PREFACE.to_string());
|
||||
}
|
||||
sections.push(EXEC_DESCRIPTION_TEMPLATE.to_string());
|
||||
if deferred_tools_available {
|
||||
sections.push(DEFERRED_NESTED_TOOLS_GUIDANCE.to_string());
|
||||
}
|
||||
if !code_mode_only {
|
||||
return sections.join("\n\n");
|
||||
}
|
||||
|
||||
let mut sections = vec![
|
||||
CODE_MODE_ONLY_PREFACE.to_string(),
|
||||
EXEC_DESCRIPTION_TEMPLATE.to_string(),
|
||||
];
|
||||
|
||||
if !enabled_tools.is_empty() {
|
||||
let mut current_namespace: Option<&str> = None;
|
||||
@@ -863,6 +869,7 @@ mod tests {
|
||||
}],
|
||||
&BTreeMap::new(),
|
||||
/*code_mode_only*/ true,
|
||||
/*deferred_tools_available*/ false,
|
||||
);
|
||||
assert!(description.contains(
|
||||
"### `foo`
|
||||
@@ -872,8 +879,12 @@ bar"
|
||||
|
||||
#[test]
|
||||
fn exec_description_mentions_timeout_helpers() {
|
||||
let description =
|
||||
build_exec_tool_description(&[], &BTreeMap::new(), /*code_mode_only*/ false);
|
||||
let description = build_exec_tool_description(
|
||||
&[],
|
||||
&BTreeMap::new(),
|
||||
/*code_mode_only*/ false,
|
||||
/*deferred_tools_available*/ false,
|
||||
);
|
||||
assert!(description.contains("`setTimeout(callback: () => void, delayMs?: number)`"));
|
||||
assert!(description.contains("`clearTimeout(timeoutId?: number)`"));
|
||||
}
|
||||
@@ -924,6 +935,7 @@ bar"
|
||||
],
|
||||
&namespace_descriptions,
|
||||
/*code_mode_only*/ true,
|
||||
/*deferred_tools_available*/ false,
|
||||
);
|
||||
assert_eq!(description.matches("## mcp__sample").count(), 1);
|
||||
assert!(description.contains("## mcp__sample\nShared namespace guidance."));
|
||||
@@ -963,6 +975,7 @@ bar"
|
||||
}],
|
||||
&namespace_descriptions,
|
||||
/*code_mode_only*/ true,
|
||||
/*deferred_tools_available*/ false,
|
||||
);
|
||||
|
||||
assert!(!description.contains("## mcp__sample"));
|
||||
@@ -1061,6 +1074,7 @@ bar"
|
||||
],
|
||||
&BTreeMap::new(),
|
||||
/*code_mode_only*/ true,
|
||||
/*deferred_tools_available*/ false,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -1071,4 +1085,17 @@ bar"
|
||||
);
|
||||
assert_eq!(description.matches("Shared MCP Types:").count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_description_mentions_deferred_nested_tools_when_available() {
|
||||
let description = build_exec_tool_description(
|
||||
&[],
|
||||
&BTreeMap::new(),
|
||||
/*code_mode_only*/ false,
|
||||
/*deferred_tools_available*/ true,
|
||||
);
|
||||
|
||||
assert!(description.contains("Some nested MCP/app tools may be omitted"));
|
||||
assert!(description.contains("filter `ALL_TOOLS` by `name` and `description`"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user