Thread tool origin into code mode rendering

This commit is contained in:
Vivian Fang
2026-04-09 03:27:45 -07:00
parent f6d6f0141c
commit 546fa338d6
35 changed files with 207 additions and 29 deletions

View File

@@ -49,6 +49,14 @@ pub enum CodeModeToolKind {
Freeform,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ToolOrigin {
Native,
Mcp,
Dynamic,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ToolDefinition {
pub name: String,
@@ -56,6 +64,7 @@ pub struct ToolDefinition {
pub kind: CodeModeToolKind,
pub input_schema: Option<JsonValue>,
pub output_schema: Option<JsonValue>,
pub origin: ToolOrigin,
}
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -185,7 +194,9 @@ pub fn build_exec_tool_description(
if !enabled_tools.is_empty() {
let mut current_namespace: Option<&str> = None;
let mut nested_tool_sections = Vec::with_capacity(enabled_tools.len());
let include_shared_mcp_types = enabled_tools.iter().any(is_mcp_tool);
let include_shared_mcp_types = enabled_tools
.iter()
.any(|tool| tool.origin == ToolOrigin::Mcp);
if include_shared_mcp_types {
sections.push(format!(
@@ -196,7 +207,7 @@ pub fn build_exec_tool_description(
for tool in enabled_tools {
let name = tool.name.as_str();
let nested_description = append_code_mode_sample_for_definition(tool);
let nested_description = render_code_mode_sample_for_definition(tool);
let next_namespace = namespace_descriptions
.get(name)
.map(|namespace_description| namespace_description.name.as_str());
@@ -216,10 +227,11 @@ pub fn build_exec_tool_description(
let global_name = normalize_code_mode_identifier(name);
let nested_description = nested_description.trim();
if nested_description.is_empty() {
nested_tool_sections.push(format!("### `{global_name}` (`{name}`)"));
nested_tool_sections.push(render_tool_heading(&global_name, name));
} else {
nested_tool_sections.push(format!(
"### `{global_name}` (`{name}`)\n{nested_description}"
"{}\n{nested_description}",
render_tool_heading(&global_name, name)
));
}
}
@@ -261,7 +273,7 @@ pub fn normalize_code_mode_identifier(tool_key: &str) -> String {
pub fn augment_tool_definition(mut definition: ToolDefinition) -> ToolDefinition {
if definition.name != PUBLIC_TOOL_NAME {
definition.description = append_code_mode_sample_for_definition(&definition);
definition.description = render_code_mode_sample_for_definition(&definition);
}
definition
}
@@ -283,7 +295,7 @@ pub struct EnabledToolMetadata {
pub kind: CodeModeToolKind,
}
pub fn append_code_mode_sample(
pub fn render_code_mode_sample(
description: &str,
tool_name: &str,
input_name: &str,
@@ -297,7 +309,7 @@ pub fn append_code_mode_sample(
format!("{description}\n\nexec tool declaration:\n```ts\n{declaration}\n```")
}
fn append_code_mode_sample_for_definition(definition: &ToolDefinition) -> String {
fn render_code_mode_sample_for_definition(definition: &ToolDefinition) -> String {
let input_name = match definition.kind {
CodeModeToolKind::Function => "args",
CodeModeToolKind::Freeform => "input",
@@ -310,7 +322,7 @@ fn append_code_mode_sample_for_definition(definition: &ToolDefinition) -> String
.unwrap_or_else(|| "unknown".to_string()),
CodeModeToolKind::Freeform => "string".to_string(),
};
if is_mcp_tool(definition) {
if definition.origin == ToolOrigin::Mcp {
let structured_content_type = definition
.output_schema
.as_ref()
@@ -323,7 +335,7 @@ fn append_code_mode_sample_for_definition(definition: &ToolDefinition) -> String
format!("mcp_result<{structured_content_type}>")
};
return append_code_mode_sample(
return render_code_mode_sample(
&definition.description,
&definition.name,
input_name,
@@ -337,7 +349,7 @@ fn append_code_mode_sample_for_definition(definition: &ToolDefinition) -> String
.as_ref()
.map(render_json_schema_to_typescript)
.unwrap_or_else(|| "unknown".to_string());
append_code_mode_sample(
render_code_mode_sample(
&definition.description,
&definition.name,
input_name,
@@ -356,6 +368,14 @@ fn render_code_mode_tool_declaration(
format!("{tool_name}({input_name}: {input_type}): Promise<{output_type}>;")
}
fn render_tool_heading(global_name: &str, raw_name: &str) -> String {
if global_name == raw_name {
format!("### `{global_name}`")
} else {
format!("### `{global_name}` (`{raw_name}`)")
}
}
pub fn render_json_schema_to_typescript(schema: &JsonValue) -> String {
render_json_schema_to_typescript_inner(schema)
}
@@ -369,13 +389,6 @@ fn extract_mcp_structured_content_schema(output_schema: &JsonValue) -> Option<&J
)
}
fn is_mcp_tool(definition: &ToolDefinition) -> bool {
// MCP tools are qualified in `mcp_connection_manager::qualify_tools` as
// `mcp__<server>__<tool>`. Code mode nested tool specs do not include the
// separate `codex_apps` app-tool registration path.
definition.name.starts_with("mcp__")
}
fn render_mcp_typescript_preamble() -> &'static str {
concat!(
"type mcp_output = { [key: string]: unknown; };\n",
@@ -614,6 +627,7 @@ mod tests {
use super::ParsedExecSource;
use super::ToolDefinition;
use super::ToolNamespaceDescription;
use super::ToolOrigin;
use super::augment_tool_definition;
use super::build_exec_tool_description;
use super::normalize_code_mode_identifier;
@@ -675,6 +689,7 @@ mod tests {
"properties": { "ok": { "type": "boolean" } },
"required": ["ok"]
})),
origin: ToolOrigin::Native,
};
let description = augment_tool_definition(definition).description;
@@ -719,6 +734,7 @@ mod tests {
},
"required": ["forecast"]
})),
origin: ToolOrigin::Native,
};
let description = augment_tool_definition(definition).description;
@@ -742,11 +758,13 @@ mod tests {
kind: CodeModeToolKind::Function,
input_schema: None,
output_schema: None,
origin: ToolOrigin::Native,
}],
&BTreeMap::new(),
/*code_mode_only*/ true,
);
assert!(description.contains("### `foo` (`foo`)"));
assert!(description.contains("### `foo`
bar"));
}
#[test]
@@ -781,15 +799,33 @@ mod tests {
name: "mcp__sample__alpha".to_string(),
description: "First tool".to_string(),
kind: CodeModeToolKind::Function,
input_schema: None,
output_schema: None,
input_schema: Some(json!({
"type": "object",
"properties": {},
"additionalProperties": false
})),
output_schema: Some(json!({
"type": "object",
"properties": {},
"additionalProperties": false
})),
origin: ToolOrigin::Mcp,
},
ToolDefinition {
name: "mcp__sample__beta".to_string(),
description: "Second tool".to_string(),
kind: CodeModeToolKind::Function,
input_schema: None,
output_schema: None,
input_schema: Some(json!({
"type": "object",
"properties": {},
"additionalProperties": false
})),
output_schema: Some(json!({
"type": "object",
"properties": {},
"additionalProperties": false
})),
origin: ToolOrigin::Mcp,
},
],
&namespace_descriptions,
@@ -800,11 +836,21 @@ mod tests {
r#"## mcp__sample
Shared namespace guidance.
### `mcp__sample__alpha` (`mcp__sample__alpha`)
### `mcp__sample__alpha`
First tool
### `mcp__sample__beta` (`mcp__sample__beta`)
Second tool"#
exec tool declaration:
```ts
declare const tools: { mcp__sample__alpha(args: {}): Promise<mcp_result>; };
```
### `mcp__sample__beta`
Second tool
exec tool declaration:
```ts
declare const tools: { mcp__sample__beta(args: {}): Promise<mcp_result>; };
```"#
));
}
@@ -822,15 +868,24 @@ Second tool"#
name: "mcp__sample__alpha".to_string(),
description: "First tool".to_string(),
kind: CodeModeToolKind::Function,
input_schema: None,
output_schema: None,
input_schema: Some(json!({
"type": "object",
"properties": {},
"additionalProperties": false
})),
output_schema: Some(json!({
"type": "object",
"properties": {},
"additionalProperties": false
})),
origin: ToolOrigin::Mcp,
}],
&namespace_descriptions,
/*code_mode_only*/ true,
);
assert!(!description.contains("## mcp__sample"));
assert!(description.contains("### `mcp__sample__alpha` (`mcp__sample__alpha`)"));
assert!(description.contains("### `mcp__sample__alpha`"));
}
#[test]
@@ -867,6 +922,7 @@ Second tool"#
"required": ["content"],
"additionalProperties": false
})),
origin: ToolOrigin::Mcp,
});
let second_tool = augment_tool_definition(ToolDefinition {
name: "mcp__sample__beta".to_string(),
@@ -900,6 +956,7 @@ Second tool"#
"required": ["content"],
"additionalProperties": false
})),
origin: ToolOrigin::Mcp,
});
let description = build_exec_tool_description(
@@ -910,6 +967,7 @@ Second tool"#
kind: first_tool.kind,
input_schema: first_tool.input_schema,
output_schema: first_tool.output_schema,
origin: first_tool.origin,
},
ToolDefinition {
name: second_tool.name,
@@ -917,6 +975,7 @@ Second tool"#
kind: second_tool.kind,
input_schema: second_tool.input_schema,
output_schema: second_tool.output_schema,
origin: second_tool.origin,
},
],
&BTreeMap::new(),

View File

@@ -7,13 +7,14 @@ pub use description::CODE_MODE_PRAGMA_PREFIX;
pub use description::CodeModeToolKind;
pub use description::ToolDefinition;
pub use description::ToolNamespaceDescription;
pub use description::append_code_mode_sample;
pub use description::ToolOrigin;
pub use description::augment_tool_definition;
pub use description::build_exec_tool_description;
pub use description::build_wait_tool_description;
pub use description::is_code_mode_nested_tool;
pub use description::normalize_code_mode_identifier;
pub use description::parse_exec_source;
pub use description::render_code_mode_sample;
pub use description::render_json_schema_to_typescript;
pub use response::FunctionCallOutputContentItem;
pub use response::ImageDetail;