From a5045881aaf84bbd910429b88c121afcc591b3bd Mon Sep 17 00:00:00 2001 From: Vivian Fang Date: Thu, 9 Apr 2026 02:55:52 -0700 Subject: [PATCH 1/7] Refine code mode MCP result typing --- codex-rs/code-mode/src/description.rs | 195 +++++++++++++++++- codex-rs/core/tests/suite/code_mode.rs | 9 +- .../rmcp-client/src/bin/rmcp_test_server.rs | 22 +- .../rmcp-client/src/bin/test_stdio_server.rs | 17 ++ .../src/bin/test_streamable_http_server.rs | 17 ++ codex-rs/tools/src/code_mode.rs | 15 +- codex-rs/tools/src/code_mode_tests.rs | 8 +- codex-rs/tools/src/lib.rs | 1 + codex-rs/tools/src/mcp_tool.rs | 8 +- codex-rs/tools/src/tool_registry_plan.rs | 16 +- .../tools/src/tool_registry_plan_tests.rs | 86 +++++++- 11 files changed, 372 insertions(+), 22 deletions(-) diff --git a/codex-rs/code-mode/src/description.rs b/codex-rs/code-mode/src/description.rs index 0cba45d7e210..3eb63f862830 100644 --- a/codex-rs/code-mode/src/description.rs +++ b/codex-rs/code-mode/src/description.rs @@ -169,7 +169,7 @@ pub fn is_code_mode_nested_tool(tool_name: &str) -> bool { } pub fn build_exec_tool_description( - enabled_tools: &[(String, String)], + enabled_tools: &[ToolDefinition], namespace_descriptions: &BTreeMap, code_mode_only: bool, ) -> String { @@ -185,8 +185,18 @@ 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); - for (name, nested_description) in enabled_tools { + if include_shared_mcp_types { + sections.push(format!( + "Shared MCP Types:\n```ts\n{}\n```", + render_mcp_typescript_preamble() + )); + } + + for tool in enabled_tools { + let name = tool.name.as_str(); + let nested_description = append_code_mode_sample_for_definition(tool); let next_namespace = namespace_descriptions .get(name) .map(|namespace_description| namespace_description.name.as_str()); @@ -300,6 +310,28 @@ 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) { + let structured_content_type = definition + .output_schema + .as_ref() + .and_then(extract_mcp_structured_content_schema) + .map(render_json_schema_to_typescript) + .unwrap_or_else(|| "unknown".to_string()); + let output_type = if structured_content_type == "unknown" { + "mcp_result".to_string() + } else { + format!("mcp_result<{structured_content_type}>") + }; + + return append_code_mode_sample( + &definition.description, + &definition.name, + input_name, + input_type, + output_type, + ); + } + let output_type = definition .output_schema .as_ref() @@ -328,6 +360,34 @@ pub fn render_json_schema_to_typescript(schema: &JsonValue) -> String { render_json_schema_to_typescript_inner(schema) } +fn extract_mcp_structured_content_schema(output_schema: &JsonValue) -> Option<&JsonValue> { + let properties = output_schema.get("properties")?.as_object()?; + Some( + properties + .get("structuredContent") + .unwrap_or(&JsonValue::Bool(true)), + ) +} + +fn is_mcp_tool(definition: &ToolDefinition) -> bool { + // MCP tools are qualified in `mcp_connection_manager::qualify_tools` as + // `mcp____`. 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", + "type mcp_result = {\n", + " _meta?: unknown;\n", + " content: Array;\n", + " isError?: boolean;\n", + " structuredContent?: TStructured;\n", + "};" + ) +} + fn render_json_schema_to_typescript_inner(schema: &JsonValue) -> String { match schema { JsonValue::Bool(true) => "unknown".to_string(), @@ -676,7 +736,13 @@ mod tests { #[test] fn code_mode_only_description_includes_nested_tools() { let description = build_exec_tool_description( - &[("foo".to_string(), "bar".to_string())], + &[ToolDefinition { + name: "foo".to_string(), + description: "bar".to_string(), + kind: CodeModeToolKind::Function, + input_schema: None, + output_schema: None, + }], &BTreeMap::new(), /*code_mode_only*/ true, ); @@ -711,8 +777,20 @@ mod tests { ]); let description = build_exec_tool_description( &[ - ("mcp__sample__alpha".to_string(), "First tool".to_string()), - ("mcp__sample__beta".to_string(), "Second tool".to_string()), + ToolDefinition { + name: "mcp__sample__alpha".to_string(), + description: "First tool".to_string(), + kind: CodeModeToolKind::Function, + input_schema: None, + output_schema: None, + }, + ToolDefinition { + name: "mcp__sample__beta".to_string(), + description: "Second tool".to_string(), + kind: CodeModeToolKind::Function, + input_schema: None, + output_schema: None, + }, ], &namespace_descriptions, /*code_mode_only*/ true, @@ -740,7 +818,13 @@ Second tool"# }, )]); let description = build_exec_tool_description( - &[("mcp__sample__alpha".to_string(), "First tool".to_string())], + &[ToolDefinition { + name: "mcp__sample__alpha".to_string(), + description: "First tool".to_string(), + kind: CodeModeToolKind::Function, + input_schema: None, + output_schema: None, + }], &namespace_descriptions, /*code_mode_only*/ true, ); @@ -748,4 +832,103 @@ Second tool"# assert!(!description.contains("## mcp__sample")); assert!(description.contains("### `mcp__sample__alpha` (`mcp__sample__alpha`)")); } + + #[test] + fn code_mode_only_description_renders_shared_mcp_types_once() { + let first_tool = augment_tool_definition(ToolDefinition { + name: "mcp__sample__alpha".to_string(), + description: "First tool".to_string(), + kind: CodeModeToolKind::Function, + input_schema: Some(json!({ + "type": "object", + "properties": {}, + "additionalProperties": false + })), + output_schema: Some(json!({ + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "type": "object" + } + }, + "structuredContent": { + "type": "object", + "properties": { + "echo": { "type": "string" } + }, + "required": ["echo"], + "additionalProperties": false + }, + "isError": { "type": "boolean" }, + "_meta": { "type": "object" } + }, + "required": ["content"], + "additionalProperties": false + })), + }); + let second_tool = augment_tool_definition(ToolDefinition { + name: "mcp__sample__beta".to_string(), + description: "Second tool".to_string(), + kind: CodeModeToolKind::Function, + input_schema: Some(json!({ + "type": "object", + "properties": {}, + "additionalProperties": false + })), + output_schema: Some(json!({ + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "type": "object" + } + }, + "structuredContent": { + "type": "object", + "properties": { + "count": { "type": "integer" } + }, + "required": ["count"], + "additionalProperties": false + }, + "isError": { "type": "boolean" }, + "_meta": { "type": "object" } + }, + "required": ["content"], + "additionalProperties": false + })), + }); + + let description = build_exec_tool_description( + &[ + ToolDefinition { + name: first_tool.name, + description: "First tool".to_string(), + kind: first_tool.kind, + input_schema: first_tool.input_schema, + output_schema: first_tool.output_schema, + }, + ToolDefinition { + name: second_tool.name, + description: "Second tool".to_string(), + kind: second_tool.kind, + input_schema: second_tool.input_schema, + output_schema: second_tool.output_schema, + }, + ], + &BTreeMap::new(), + /*code_mode_only*/ true, + ); + + assert_eq!( + description + .matches("type mcp_result") + .count(), + 1 + ); + assert_eq!(description.matches("Shared MCP Types:").count(), 1); + } } diff --git a/codex-rs/core/tests/suite/code_mode.rs b/codex-rs/core/tests/suite/code_mode.rs index 9f0dc5635845..6d2643b95a4e 100644 --- a/codex-rs/core/tests/suite/code_mode.rs +++ b/codex-rs/core/tests/suite/code_mode.rs @@ -2280,7 +2280,14 @@ text(JSON.stringify(tool)); parsed, serde_json::json!({ "name": "mcp__rmcp__echo", - "description": "Echo back the provided message and include environment data.\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__rmcp__echo(args: { env_var?: string; message: string; }): Promise<{ _meta?: unknown; content: Array; isError?: boolean; structuredContent?: unknown; }>; };\n```", + "description": concat!( + "Echo back the provided message and include environment data.\n\n", + "exec tool declaration:\n", + "```ts\n", + "declare const tools: { mcp__rmcp__echo(args: { env_var?: string; message: string; }): ", + "Promise>; };\n", + "```", + ), }) ); diff --git a/codex-rs/rmcp-client/src/bin/rmcp_test_server.rs b/codex-rs/rmcp-client/src/bin/rmcp_test_server.rs index 81898b0bc778..e513f12780e2 100644 --- a/codex-rs/rmcp-client/src/bin/rmcp_test_server.rs +++ b/codex-rs/rmcp-client/src/bin/rmcp_test_server.rs @@ -45,11 +45,29 @@ impl TestToolServer { })) .expect("echo tool schema should deserialize"); - Tool::new( + let mut tool = Tool::new( Cow::Borrowed("echo"), Cow::Borrowed("Echo back the provided message and include environment data."), Arc::new(schema), - ) + ); + #[expect(clippy::expect_used)] + let output_schema: JsonObject = serde_json::from_value(json!({ + "type": "object", + "properties": { + "echo": { "type": "string" }, + "env": { + "anyOf": [ + { "type": "string" }, + { "type": "null" } + ] + } + }, + "required": ["echo", "env"], + "additionalProperties": false + })) + .expect("echo tool output schema should deserialize"); + tool.output_schema = Some(Arc::new(output_schema)); + tool } } diff --git a/codex-rs/rmcp-client/src/bin/test_stdio_server.rs b/codex-rs/rmcp-client/src/bin/test_stdio_server.rs index d9202babae62..efee52041c89 100644 --- a/codex-rs/rmcp-client/src/bin/test_stdio_server.rs +++ b/codex-rs/rmcp-client/src/bin/test_stdio_server.rs @@ -91,6 +91,23 @@ impl TestToolServer { Cow::Borrowed(description), Arc::new(schema), ); + #[expect(clippy::expect_used)] + let output_schema: JsonObject = serde_json::from_value(json!({ + "type": "object", + "properties": { + "echo": { "type": "string" }, + "env": { + "anyOf": [ + { "type": "string" }, + { "type": "null" } + ] + } + }, + "required": ["echo", "env"], + "additionalProperties": false + })) + .expect("echo tool output schema should deserialize"); + tool.output_schema = Some(Arc::new(output_schema)); tool.annotations = Some(ToolAnnotations::new().read_only(true)); tool } diff --git a/codex-rs/rmcp-client/src/bin/test_streamable_http_server.rs b/codex-rs/rmcp-client/src/bin/test_streamable_http_server.rs index 50ea06c7fa23..93958804986d 100644 --- a/codex-rs/rmcp-client/src/bin/test_streamable_http_server.rs +++ b/codex-rs/rmcp-client/src/bin/test_streamable_http_server.rs @@ -90,6 +90,23 @@ impl TestToolServer { Cow::Borrowed("Echo back the provided message and include environment data."), Arc::new(schema), ); + #[expect(clippy::expect_used)] + let output_schema: JsonObject = serde_json::from_value(json!({ + "type": "object", + "properties": { + "echo": { "type": "string" }, + "env": { + "anyOf": [ + { "type": "string" }, + { "type": "null" } + ] + } + }, + "required": ["echo", "env"], + "additionalProperties": false + })) + .expect("echo tool output schema should deserialize"); + tool.output_schema = Some(Arc::new(output_schema)); tool.annotations = Some(ToolAnnotations::new().read_only(true)); tool } diff --git a/codex-rs/tools/src/code_mode.rs b/codex-rs/tools/src/code_mode.rs index c2b82e1da7d8..35d95b271ab4 100644 --- a/codex-rs/tools/src/code_mode.rs +++ b/codex-rs/tools/src/code_mode.rs @@ -49,6 +49,19 @@ pub fn collect_code_mode_tool_definitions<'a>( tool_definitions } +pub fn collect_code_mode_exec_prompt_tool_definitions<'a>( + specs: impl IntoIterator, +) -> Vec { + let mut tool_definitions = specs + .into_iter() + .filter_map(code_mode_tool_definition_for_spec) + .filter(|definition| codex_code_mode::is_code_mode_nested_tool(&definition.name)) + .collect::>(); + tool_definitions.sort_by(|left, right| left.name.cmp(&right.name)); + tool_definitions.dedup_by(|left, right| left.name == right.name); + tool_definitions +} + pub fn create_wait_tool() -> ToolSpec { let properties = BTreeMap::from([ ( @@ -95,7 +108,7 @@ pub fn create_wait_tool() -> ToolSpec { } pub fn create_code_mode_tool( - enabled_tools: &[(String, String)], + enabled_tools: &[CodeModeToolDefinition], namespace_descriptions: &BTreeMap, code_mode_only_enabled: bool, ) -> ToolSpec { diff --git a/codex-rs/tools/src/code_mode_tests.rs b/codex-rs/tools/src/code_mode_tests.rs index a38acacf6e53..ec30c6e8bfae 100644 --- a/codex-rs/tools/src/code_mode_tests.rs +++ b/codex-rs/tools/src/code_mode_tests.rs @@ -182,7 +182,13 @@ fn create_wait_tool_matches_expected_spec() { #[test] fn create_code_mode_tool_matches_expected_spec() { - let enabled_tools = vec![("update_plan".to_string(), "Update the plan".to_string())]; + let enabled_tools = vec![codex_code_mode::ToolDefinition { + name: "update_plan".to_string(), + description: "Update the plan".to_string(), + kind: codex_code_mode::CodeModeToolKind::Function, + input_schema: None, + output_schema: None, + }]; assert_eq!( create_code_mode_tool( diff --git a/codex-rs/tools/src/lib.rs b/codex-rs/tools/src/lib.rs index 07d044521398..9a3107dd666b 100644 --- a/codex-rs/tools/src/lib.rs +++ b/codex-rs/tools/src/lib.rs @@ -44,6 +44,7 @@ pub use apply_patch_tool::ApplyPatchToolArgs; pub use apply_patch_tool::create_apply_patch_freeform_tool; pub use apply_patch_tool::create_apply_patch_json_tool; pub use code_mode::augment_tool_spec_for_code_mode; +pub use code_mode::collect_code_mode_exec_prompt_tool_definitions; pub use code_mode::collect_code_mode_tool_definitions; pub use code_mode::create_code_mode_tool; pub use code_mode::create_wait_tool; diff --git a/codex-rs/tools/src/mcp_tool.rs b/codex-rs/tools/src/mcp_tool.rs index 3fd3c5e751a3..337a8e42adbb 100644 --- a/codex-rs/tools/src/mcp_tool.rs +++ b/codex-rs/tools/src/mcp_tool.rs @@ -42,13 +42,17 @@ pub fn mcp_call_tool_result_output_schema(structured_content_schema: JsonValue) "properties": { "content": { "type": "array", - "items": {} + "items": { + "type": "object" + } }, "structuredContent": structured_content_schema, "isError": { "type": "boolean" }, - "_meta": {} + "_meta": { + "type": "object" + } }, "required": ["content"], "additionalProperties": false diff --git a/codex-rs/tools/src/tool_registry_plan.rs b/codex-rs/tools/src/tool_registry_plan.rs index 1377324e637d..1135c5eb6bcc 100644 --- a/codex-rs/tools/src/tool_registry_plan.rs +++ b/codex-rs/tools/src/tool_registry_plan.rs @@ -13,8 +13,13 @@ use crate::ToolSpec; use crate::ToolsConfig; use crate::ViewImageToolOptions; use crate::WebSearchToolOptions; +<<<<<<< HEAD use crate::collect_code_mode_tool_definitions; use crate::collect_tool_search_source_infos; +======= +use crate::collect_code_mode_exec_prompt_tool_definitions; +use crate::collect_tool_search_app_infos; +>>>>>>> f6d6f0141 (Refine code mode MCP result typing) use crate::collect_tool_suggest_entries; use crate::create_apply_patch_freeform_tool; use crate::create_apply_patch_json_tool; @@ -93,17 +98,14 @@ pub fn build_tool_registry_plan( ..params }, ); - let mut enabled_tools = collect_code_mode_tool_definitions( + let mut enabled_tools = collect_code_mode_exec_prompt_tool_definitions( nested_plan .specs .iter() .map(|configured_tool| &configured_tool.spec), - ) - .into_iter() - .map(|tool| (tool.name, tool.description)) - .collect::>(); - enabled_tools.sort_by(|(left_name, _), (right_name, _)| { - compare_code_mode_tool_names(left_name, right_name, &namespace_descriptions) + ); + enabled_tools.sort_by(|left, right| { + compare_code_mode_tool_names(&left.name, &right.name, &namespace_descriptions) }); plan.push_spec( create_code_mode_tool( diff --git a/codex-rs/tools/src/tool_registry_plan_tests.rs b/codex-rs/tools/src/tool_registry_plan_tests.rs index 13ef18495e5f..a1acaff67c6f 100644 --- a/codex-rs/tools/src/tool_registry_plan_tests.rs +++ b/codex-rs/tools/src/tool_registry_plan_tests.rs @@ -1608,7 +1608,7 @@ fn code_mode_augments_mcp_tool_descriptions_with_namespaced_sample() { exec tool declaration: ```ts -declare const tools: { mcp__sample__echo(args: { message: string; }): Promise<{ _meta?: unknown; content: Array; isError?: boolean; structuredContent?: unknown; }>; }; +declare const tools: { mcp__sample__echo(args: { message: string; }): Promise; }; ```"# ); } @@ -1694,7 +1694,7 @@ fn code_mode_preserves_nullable_and_literal_mcp_input_shapes() { assert!(description.contains( r#"exec tool declaration: ```ts -declare const tools: { mcp__sample__fn(args: { open?: Array<{ lineno?: number | null; ref_id: string; }> | null; response_length?: "short" | "medium" | "long"; tagged_list?: Array<{ kind: "tagged"; scope: "one" | "two"; variant: "alpha" | "beta"; }> | null; }): Promise<{ _meta?: unknown; content: Array; isError?: boolean; structuredContent?: unknown; }>; }; +declare const tools: { mcp__sample__fn(args: { open?: Array<{ lineno?: number | null; ref_id: string; }> | null; response_length?: "short" | "medium" | "long"; tagged_list?: Array<{ kind: "tagged"; scope: "one" | "two"; variant: "alpha" | "beta"; }> | null; }): Promise; }; ```"# )); } @@ -1919,6 +1919,88 @@ fn mcp_tool(name: &str, description: &str, input_schema: serde_json::Value) -> r } } +fn mcp_tool_with_output_schema( + name: &str, + description: &str, + input_schema: serde_json::Value, + output_schema: serde_json::Value, +) -> rmcp::model::Tool { + let mut tool = mcp_tool(name, description, input_schema); + tool.output_schema = Some(std::sync::Arc::new(rmcp::model::object(output_schema))); + tool +} + +#[test] +fn code_mode_augments_mcp_tool_descriptions_with_structured_output_sample() { + let model_info = model_info(); + let mut features = Features::with_defaults(); + features.enable(Feature::CodeMode); + features.enable(Feature::CodeModeOnly); + features.enable(Feature::UnifiedExec); + let available_models = Vec::new(); + let tools_config = ToolsConfig::new(&ToolsConfigParams { + model_info: &model_info, + available_models: &available_models, + features: &features, + image_generation_tool_auth_allowed: true, + web_search_mode: Some(WebSearchMode::Cached), + session_source: SessionSource::Cli, + sandbox_policy: &SandboxPolicy::DangerFullAccess, + windows_sandbox_level: WindowsSandboxLevel::Disabled, + }); + + let (tools, _) = build_specs( + &tools_config, + Some(HashMap::from([( + "mcp__sample__echo".to_string(), + mcp_tool_with_output_schema( + "echo", + "Echo text", + serde_json::json!({ + "type": "object", + "properties": { + "message": {"type": "string"} + }, + "required": ["message"], + "additionalProperties": false + }), + serde_json::json!({ + "type": "object", + "properties": { + "echo": {"type": "string"}, + "env": { + "anyOf": [ + {"type": "string"}, + {"type": "null"} + ] + } + }, + "required": ["echo", "env"], + "additionalProperties": false + }), + ), + )])), + /*app_tools*/ None, + &[], + ); + + let ToolSpec::Function(ResponsesApiTool { description, .. }) = + &find_tool(&tools, "mcp__sample__echo").spec + else { + panic!("expected function tool"); + }; + + assert_eq!( + description, + r#"Echo text + +exec tool declaration: +```ts +declare const tools: { mcp__sample__echo(args: { message: string; }): Promise>; }; +```"# + ); +} + fn discoverable_connector(id: &str, name: &str, description: &str) -> DiscoverableTool { let slug = name.replace(' ', "-").to_lowercase(); DiscoverableTool::Connector(Box::new(AppInfo { From 4275ad2b58d8c365f1568777ef01e59f516beee8 Mon Sep 17 00:00:00 2001 From: Vivian Fang Date: Thu, 9 Apr 2026 03:27:45 -0700 Subject: [PATCH 2/7] Thread tool origin into code mode rendering --- codex-rs/code-mode/src/description.rs | 115 +++++++++++++----- codex-rs/code-mode/src/lib.rs | 3 +- codex-rs/tools/src/agent_job_tool.rs | 3 + codex-rs/tools/src/agent_job_tool_tests.rs | 2 + codex-rs/tools/src/agent_tool.rs | 12 ++ codex-rs/tools/src/apply_patch_tool.rs | 2 + codex-rs/tools/src/apply_patch_tool_tests.rs | 1 + codex-rs/tools/src/code_mode.rs | 9 ++ codex-rs/tools/src/code_mode_tests.rs | 6 + codex-rs/tools/src/dynamic_tool.rs | 2 + codex-rs/tools/src/dynamic_tool_tests.rs | 3 + codex-rs/tools/src/js_repl_tool.rs | 2 + codex-rs/tools/src/js_repl_tool_tests.rs | 1 + codex-rs/tools/src/lib.rs | 1 + codex-rs/tools/src/local_tool.rs | 6 + codex-rs/tools/src/local_tool_tests.rs | 6 + codex-rs/tools/src/mcp_resource_tool.rs | 4 + codex-rs/tools/src/mcp_resource_tool_tests.rs | 3 + codex-rs/tools/src/mcp_tool.rs | 2 + codex-rs/tools/src/mcp_tool_tests.rs | 4 + codex-rs/tools/src/plan_tool.rs | 2 + codex-rs/tools/src/request_user_input_tool.rs | 2 + .../src/request_user_input_tool_tests.rs | 1 + codex-rs/tools/src/responses_api.rs | 4 + codex-rs/tools/src/responses_api_tests.rs | 6 + codex-rs/tools/src/tool_definition.rs | 9 ++ codex-rs/tools/src/tool_definition_tests.rs | 3 + codex-rs/tools/src/tool_discovery.rs | 2 + codex-rs/tools/src/tool_discovery_tests.rs | 5 + .../tools/src/tool_registry_plan_tests.rs | 6 + codex-rs/tools/src/tool_spec_tests.rs | 4 + codex-rs/tools/src/utility_tool.rs | 3 + codex-rs/tools/src/utility_tool_tests.rs | 2 + codex-rs/tools/src/view_image.rs | 2 + codex-rs/tools/src/view_image_tests.rs | 2 + 35 files changed, 211 insertions(+), 29 deletions(-) diff --git a/codex-rs/code-mode/src/description.rs b/codex-rs/code-mode/src/description.rs index 3eb63f862830..ff845545565a 100644 --- a/codex-rs/code-mode/src/description.rs +++ b/codex-rs/code-mode/src/description.rs @@ -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, pub output_schema: Option, + 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____`. 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__sample__beta` +Second tool + +exec tool declaration: +```ts +declare const tools: { mcp__sample__beta(args: {}): Promise; }; +```"# )); } @@ -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(), diff --git a/codex-rs/code-mode/src/lib.rs b/codex-rs/code-mode/src/lib.rs index f7ab0d48e074..585a3b7427d6 100644 --- a/codex-rs/code-mode/src/lib.rs +++ b/codex-rs/code-mode/src/lib.rs @@ -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; diff --git a/codex-rs/tools/src/agent_job_tool.rs b/codex-rs/tools/src/agent_job_tool.rs index bcdec5dde252..deb89c6f83ee 100644 --- a/codex-rs/tools/src/agent_job_tool.rs +++ b/codex-rs/tools/src/agent_job_tool.rs @@ -1,5 +1,6 @@ use crate::JsonSchema; use crate::ResponsesApiTool; +use crate::ToolOrigin; use crate::ToolSpec; use std::collections::BTreeMap; @@ -60,6 +61,7 @@ pub fn create_spawn_agents_on_csv_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["csv_path".to_string(), "instruction".to_string()]), Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) } @@ -99,6 +101,7 @@ pub fn create_report_agent_job_result_tool() -> ToolSpec { "result".to_string(), ]), Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/agent_job_tool_tests.rs b/codex-rs/tools/src/agent_job_tool_tests.rs index 95f865977307..d542334fc495 100644 --- a/codex-rs/tools/src/agent_job_tool_tests.rs +++ b/codex-rs/tools/src/agent_job_tool_tests.rs @@ -69,6 +69,7 @@ fn spawn_agents_on_csv_tool_requires_csv_and_instruction() { ), ]), Some(vec!["csv_path".to_string(), "instruction".to_string()]), Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) ); } @@ -114,6 +115,7 @@ fn report_agent_job_result_tool_requires_result_payload() { "result".to_string(), ]), Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) ); } diff --git a/codex-rs/tools/src/agent_tool.rs b/codex-rs/tools/src/agent_tool.rs index 6e75963ba186..d3c80c4d5fc1 100644 --- a/codex-rs/tools/src/agent_tool.rs +++ b/codex-rs/tools/src/agent_tool.rs @@ -1,5 +1,6 @@ use crate::JsonSchema; use crate::ResponsesApiTool; +use crate::ToolOrigin; use crate::ToolSpec; use codex_protocol::openai_models::ModelPreset; use serde_json::Value; @@ -44,6 +45,7 @@ pub fn create_spawn_agent_tool_v1(options: SpawnAgentToolOptions<'_>) -> ToolSpe defer_loading: None, parameters: JsonSchema::object(properties, /*required*/ None, Some(false.into())), output_schema: Some(spawn_agent_output_schema_v1()), + origin: ToolOrigin::Native, }) } @@ -85,6 +87,7 @@ pub fn create_spawn_agent_tool_v2(options: SpawnAgentToolOptions<'_>) -> ToolSpe output_schema: Some(spawn_agent_output_schema_v2( options.hide_agent_type_model_reasoning, )), + origin: ToolOrigin::Native, }) } @@ -119,6 +122,7 @@ pub fn create_send_input_tool_v1() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["target".to_string()]), Some(false.into())), output_schema: Some(send_input_output_schema()), + origin: ToolOrigin::Native, }) } @@ -146,6 +150,7 @@ pub fn create_send_message_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["target".to_string(), "message".to_string()]), Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) } @@ -180,6 +185,7 @@ pub fn create_followup_task_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["target".to_string(), "message".to_string()]), Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) } @@ -198,6 +204,7 @@ pub fn create_resume_agent_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["id".to_string()]), Some(false.into())), output_schema: Some(resume_agent_output_schema()), + origin: ToolOrigin::Native, }) } @@ -210,6 +217,7 @@ pub fn create_wait_agent_tool_v1(options: WaitAgentTimeoutOptions) -> ToolSpec { defer_loading: None, parameters: wait_agent_tool_parameters_v1(options), output_schema: Some(wait_output_schema_v1()), + origin: ToolOrigin::Native, }) } @@ -222,6 +230,7 @@ pub fn create_wait_agent_tool_v2(options: WaitAgentTimeoutOptions) -> ToolSpec { defer_loading: None, parameters: wait_agent_tool_parameters_v2(options), output_schema: Some(wait_output_schema_v2()), + origin: ToolOrigin::Native, }) } @@ -243,6 +252,7 @@ pub fn create_list_agents_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, /*required*/ None, Some(false.into())), output_schema: Some(list_agents_output_schema()), + origin: ToolOrigin::Native, }) } @@ -259,6 +269,7 @@ pub fn create_close_agent_tool_v1() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["target".to_string()]), Some(false.into())), output_schema: Some(close_agent_output_schema()), + origin: ToolOrigin::Native, }) } @@ -277,6 +288,7 @@ pub fn create_close_agent_tool_v2() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["target".to_string()]), Some(false.into())), output_schema: Some(close_agent_output_schema()), + origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/apply_patch_tool.rs b/codex-rs/tools/src/apply_patch_tool.rs index 469bb5236769..9256dfea5901 100644 --- a/codex-rs/tools/src/apply_patch_tool.rs +++ b/codex-rs/tools/src/apply_patch_tool.rs @@ -2,6 +2,7 @@ use crate::FreeformTool; use crate::FreeformToolFormat; use crate::JsonSchema; use crate::ResponsesApiTool; +use crate::ToolOrigin; use crate::ToolSpec; use serde::Deserialize; use serde::Serialize; @@ -118,6 +119,7 @@ pub fn create_apply_patch_json_tool() -> ToolSpec { Some(false.into()), ), output_schema: None, + origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/apply_patch_tool_tests.rs b/codex-rs/tools/src/apply_patch_tool_tests.rs index c128594587a3..8b227cf735df 100644 --- a/codex-rs/tools/src/apply_patch_tool_tests.rs +++ b/codex-rs/tools/src/apply_patch_tool_tests.rs @@ -41,6 +41,7 @@ fn create_apply_patch_json_tool_matches_expected_spec() { Some(false.into()) ), output_schema: None, + origin: ToolOrigin::Native, }) ); } diff --git a/codex-rs/tools/src/code_mode.rs b/codex-rs/tools/src/code_mode.rs index 35d95b271ab4..2cd0c26ee155 100644 --- a/codex-rs/tools/src/code_mode.rs +++ b/codex-rs/tools/src/code_mode.rs @@ -2,9 +2,11 @@ use crate::FreeformTool; use crate::FreeformToolFormat; use crate::JsonSchema; use crate::ResponsesApiTool; +use crate::ToolOrigin; use crate::ToolSpec; use codex_code_mode::CodeModeToolKind; use codex_code_mode::ToolDefinition as CodeModeToolDefinition; +use codex_code_mode::ToolOrigin as CodeModeToolOrigin; use std::collections::BTreeMap; /// Augment tool descriptions with code-mode-specific exec samples. @@ -103,6 +105,7 @@ pub fn create_wait_tool() -> ToolSpec { Some(false.into()), ), output_schema: None, + origin: ToolOrigin::Native, defer_loading: None, }) } @@ -145,6 +148,11 @@ fn code_mode_tool_definition_for_spec(spec: &ToolSpec) -> Option CodeModeToolOrigin::Native, + ToolOrigin::Mcp => CodeModeToolOrigin::Mcp, + ToolOrigin::Dynamic => CodeModeToolOrigin::Dynamic, + }, }), ToolSpec::Freeform(tool) => Some(CodeModeToolDefinition { name: tool.name.clone(), @@ -152,6 +160,7 @@ fn code_mode_tool_definition_for_spec(spec: &ToolSpec) -> Option; }; kind: codex_code_mode::CodeModeToolKind::Freeform, input_schema: None, output_schema: None, + origin: codex_code_mode::ToolOrigin::Native, }) ); } @@ -176,6 +180,7 @@ fn create_wait_tool_matches_expected_spec() { ), ]), Some(vec!["cell_id".to_string()]), Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) ); } @@ -188,6 +193,7 @@ fn create_code_mode_tool_matches_expected_spec() { kind: codex_code_mode::CodeModeToolKind::Function, input_schema: None, output_schema: None, + origin: codex_code_mode::ToolOrigin::Native, }]; assert_eq!( diff --git a/codex-rs/tools/src/dynamic_tool.rs b/codex-rs/tools/src/dynamic_tool.rs index 372dbce29d07..356cff2027e1 100644 --- a/codex-rs/tools/src/dynamic_tool.rs +++ b/codex-rs/tools/src/dynamic_tool.rs @@ -1,4 +1,5 @@ use crate::ToolDefinition; +use crate::ToolOrigin; use crate::parse_tool_input_schema; use codex_protocol::dynamic_tools::DynamicToolSpec; @@ -14,6 +15,7 @@ pub fn parse_dynamic_tool(tool: &DynamicToolSpec) -> Result ToolSpec { defer_loading: None, parameters: JsonSchema::object(BTreeMap::new(), /*required*/ None, Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/js_repl_tool_tests.rs b/codex-rs/tools/src/js_repl_tool_tests.rs index 7d6d63f179b9..77c87aff90f6 100644 --- a/codex-rs/tools/src/js_repl_tool_tests.rs +++ b/codex-rs/tools/src/js_repl_tool_tests.rs @@ -36,6 +36,7 @@ fn js_repl_reset_tool_matches_expected_spec() { Some(false.into()) ), output_schema: None, + origin: ToolOrigin::Native, }) ); } diff --git a/codex-rs/tools/src/lib.rs b/codex-rs/tools/src/lib.rs index 9a3107dd666b..ec9c87e32976 100644 --- a/codex-rs/tools/src/lib.rs +++ b/codex-rs/tools/src/lib.rs @@ -95,6 +95,7 @@ pub use tool_config::ToolsConfigParams; pub use tool_config::UnifiedExecShellMode; pub use tool_config::ZshForkConfig; pub use tool_definition::ToolDefinition; +pub use tool_definition::ToolOrigin; pub use tool_discovery::DiscoverablePluginInfo; pub use tool_discovery::DiscoverableTool; pub use tool_discovery::DiscoverableToolAction; diff --git a/codex-rs/tools/src/local_tool.rs b/codex-rs/tools/src/local_tool.rs index 3e369ab1e331..0661187d594c 100644 --- a/codex-rs/tools/src/local_tool.rs +++ b/codex-rs/tools/src/local_tool.rs @@ -1,5 +1,6 @@ use crate::JsonSchema; use crate::ResponsesApiTool; +use crate::ToolOrigin; use crate::ToolSpec; use serde_json::Value; use serde_json::json; @@ -86,6 +87,7 @@ pub fn create_exec_command_tool(options: CommandToolOptions) -> ToolSpec { Some(false.into()), ), output_schema: Some(unified_exec_output_schema()), + origin: ToolOrigin::Native, }) } @@ -130,6 +132,7 @@ pub fn create_write_stdin_tool() -> ToolSpec { Some(false.into()), ), output_schema: Some(unified_exec_output_schema()), + origin: ToolOrigin::Native, }) } @@ -193,6 +196,7 @@ Examples of valid command strings: Some(false.into()), ), output_schema: None, + origin: ToolOrigin::Native, }) } @@ -263,6 +267,7 @@ Examples of valid command strings: Some(false.into()), ), output_schema: None, + origin: ToolOrigin::Native, }) } @@ -288,6 +293,7 @@ pub fn create_request_permissions_tool(description: String) -> ToolSpec { Some(false.into()), ), output_schema: None, + origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/local_tool_tests.rs b/codex-rs/tools/src/local_tool_tests.rs index b751545b3ace..bb38014b272a 100644 --- a/codex-rs/tools/src/local_tool_tests.rs +++ b/codex-rs/tools/src/local_tool_tests.rs @@ -87,6 +87,7 @@ Examples of valid command strings: Some(false.into()) ), output_schema: None, + origin: ToolOrigin::Native, }) ); } @@ -170,6 +171,7 @@ fn exec_command_tool_matches_expected_spec() { Some(false.into()) ), output_schema: Some(unified_exec_output_schema()), + origin: ToolOrigin::Native, }) ); } @@ -220,6 +222,7 @@ fn write_stdin_tool_matches_expected_spec() { Some(false.into()) ), output_schema: Some(unified_exec_output_schema()), + origin: ToolOrigin::Native, }) ); } @@ -291,6 +294,7 @@ Examples of valid command strings: Some(false.into()) ), output_schema: None, + origin: ToolOrigin::Native, }) ); } @@ -323,6 +327,7 @@ fn request_permissions_tool_includes_full_permission_schema() { Some(false.into()) ), output_schema: None, + origin: ToolOrigin::Native, }) ); } @@ -397,6 +402,7 @@ Examples of valid command strings: Some(false.into()) ), output_schema: None, + origin: ToolOrigin::Native, }) ); } diff --git a/codex-rs/tools/src/mcp_resource_tool.rs b/codex-rs/tools/src/mcp_resource_tool.rs index fd2e0ac2a4e5..191aed6ab376 100644 --- a/codex-rs/tools/src/mcp_resource_tool.rs +++ b/codex-rs/tools/src/mcp_resource_tool.rs @@ -1,5 +1,6 @@ use crate::JsonSchema; use crate::ResponsesApiTool; +use crate::ToolOrigin; use crate::ToolSpec; use std::collections::BTreeMap; @@ -28,6 +29,7 @@ pub fn create_list_mcp_resources_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, /*required*/ None, Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) } @@ -56,6 +58,7 @@ pub fn create_list_mcp_resource_templates_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, /*required*/ None, Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) } @@ -90,6 +93,7 @@ pub fn create_read_mcp_resource_tool() -> ToolSpec { Some(false.into()), ), output_schema: None, + origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/mcp_resource_tool_tests.rs b/codex-rs/tools/src/mcp_resource_tool_tests.rs index 2c0d03ee5138..6c4ec2a37ecb 100644 --- a/codex-rs/tools/src/mcp_resource_tool_tests.rs +++ b/codex-rs/tools/src/mcp_resource_tool_tests.rs @@ -29,6 +29,7 @@ fn list_mcp_resources_tool_matches_expected_spec() { ), ]), /*required*/ None, Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) ); } @@ -59,6 +60,7 @@ fn list_mcp_resource_templates_tool_matches_expected_spec() { ), ]), /*required*/ None, Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) ); } @@ -91,6 +93,7 @@ fn read_mcp_resource_tool_matches_expected_spec() { ), ]), Some(vec!["server".to_string(), "uri".to_string()]), Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) ); } diff --git a/codex-rs/tools/src/mcp_tool.rs b/codex-rs/tools/src/mcp_tool.rs index 337a8e42adbb..968f738d68c5 100644 --- a/codex-rs/tools/src/mcp_tool.rs +++ b/codex-rs/tools/src/mcp_tool.rs @@ -1,4 +1,5 @@ use crate::ToolDefinition; +use crate::ToolOrigin; use crate::parse_tool_input_schema; use serde_json::Value as JsonValue; use serde_json::json; @@ -32,6 +33,7 @@ pub fn parse_mcp_tool(tool: &rmcp::model::Tool) -> Result ToolSpec { Some(false.into()), ), output_schema: None, + origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/request_user_input_tool_tests.rs b/codex-rs/tools/src/request_user_input_tool_tests.rs index 68c2639e406c..3f015da9f903 100644 --- a/codex-rs/tools/src/request_user_input_tool_tests.rs +++ b/codex-rs/tools/src/request_user_input_tool_tests.rs @@ -85,6 +85,7 @@ fn request_user_input_tool_includes_questions_schema() { ), )]), Some(vec!["questions".to_string()]), Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) ); } diff --git a/codex-rs/tools/src/responses_api.rs b/codex-rs/tools/src/responses_api.rs index 50bb4f223243..c61083ac9d6c 100644 --- a/codex-rs/tools/src/responses_api.rs +++ b/codex-rs/tools/src/responses_api.rs @@ -1,5 +1,6 @@ use crate::JsonSchema; use crate::ToolDefinition; +use crate::ToolOrigin; use crate::parse_dynamic_tool; use crate::parse_mcp_tool; use codex_protocol::dynamic_tools::DynamicToolSpec; @@ -34,6 +35,8 @@ pub struct ResponsesApiTool { pub parameters: JsonSchema, #[serde(skip)] pub output_schema: Option, + #[serde(skip)] + pub origin: ToolOrigin, } #[derive(Debug, Clone, Serialize, PartialEq)] @@ -95,6 +98,7 @@ pub fn tool_definition_to_responses_api_tool(tool_definition: ToolDefinition) -> defer_loading: tool_definition.defer_loading.then_some(true), parameters: tool_definition.input_schema, output_schema: tool_definition.output_schema, + origin: tool_definition.origin, } } diff --git a/codex-rs/tools/src/responses_api_tests.rs b/codex-rs/tools/src/responses_api_tests.rs index c3ce4cec2b7c..a0f0819c0c30 100644 --- a/codex-rs/tools/src/responses_api_tests.rs +++ b/codex-rs/tools/src/responses_api_tests.rs @@ -7,6 +7,7 @@ use super::mcp_tool_to_deferred_responses_api_tool; use super::tool_definition_to_responses_api_tool; use crate::JsonSchema; use crate::ToolDefinition; +use crate::ToolOrigin; use codex_protocol::dynamic_tools::DynamicToolSpec; use pretty_assertions::assert_eq; use serde_json::json; @@ -27,6 +28,7 @@ fn tool_definition_to_responses_api_tool_omits_false_defer_loading() { Some(false.into()) ), output_schema: Some(json!({"type": "object"})), + origin: ToolOrigin::Native, defer_loading: false, }), ResponsesApiTool { @@ -43,6 +45,7 @@ fn tool_definition_to_responses_api_tool_omits_false_defer_loading() { Some(false.into()) ), output_schema: Some(json!({"type": "object"})), + origin: ToolOrigin::Native, } ); } @@ -79,6 +82,7 @@ fn dynamic_tool_to_responses_api_tool_preserves_defer_loading() { Some(false.into()) ), output_schema: None, + origin: ToolOrigin::Dynamic, } ); } @@ -120,6 +124,7 @@ fn mcp_tool_to_deferred_responses_api_tool_sets_defer_loading() { JsonSchema::string(/*description*/ None), )]), Some(vec!["order_id".to_string()]), Some(false.into())), output_schema: None, + origin: ToolOrigin::Mcp, } ); } @@ -140,6 +145,7 @@ fn tool_search_output_namespace_serializes_with_deferred_child_tools() { /*additional_properties*/ None, ), output_schema: None, + origin: ToolOrigin::Mcp, })], }); diff --git a/codex-rs/tools/src/tool_definition.rs b/codex-rs/tools/src/tool_definition.rs index d3c5b0209baa..7fc1115a02d1 100644 --- a/codex-rs/tools/src/tool_definition.rs +++ b/codex-rs/tools/src/tool_definition.rs @@ -1,6 +1,14 @@ use crate::JsonSchema; use serde_json::Value as JsonValue; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum ToolOrigin { + #[default] + Native, + Mcp, + Dynamic, +} + /// Tool metadata and schemas that downstream crates can adapt into higher-level /// tool specs. #[derive(Debug, PartialEq)] @@ -9,6 +17,7 @@ pub struct ToolDefinition { pub description: String, pub input_schema: JsonSchema, pub output_schema: Option, + pub origin: ToolOrigin, pub defer_loading: bool, } diff --git a/codex-rs/tools/src/tool_definition_tests.rs b/codex-rs/tools/src/tool_definition_tests.rs index 0d4d97f5ce9e..67c9ecfc78c6 100644 --- a/codex-rs/tools/src/tool_definition_tests.rs +++ b/codex-rs/tools/src/tool_definition_tests.rs @@ -1,5 +1,6 @@ use super::ToolDefinition; use crate::JsonSchema; +use crate::ToolOrigin; use pretty_assertions::assert_eq; use std::collections::BTreeMap; @@ -15,6 +16,7 @@ fn tool_definition() -> ToolDefinition { output_schema: Some(serde_json::json!({ "type": "object", })), + origin: ToolOrigin::Native, defer_loading: false, } } @@ -36,6 +38,7 @@ fn into_deferred_drops_output_schema_and_sets_defer_loading() { tool_definition().into_deferred(), ToolDefinition { output_schema: None, + origin: ToolOrigin::Native, defer_loading: true, ..tool_definition() } diff --git a/codex-rs/tools/src/tool_discovery.rs b/codex-rs/tools/src/tool_discovery.rs index 594d4011b862..e84a68d5fed4 100644 --- a/codex-rs/tools/src/tool_discovery.rs +++ b/codex-rs/tools/src/tool_discovery.rs @@ -2,6 +2,7 @@ use crate::JsonSchema; use crate::ResponsesApiNamespace; use crate::ResponsesApiNamespaceTool; use crate::ResponsesApiTool; +use crate::ToolOrigin; use crate::ToolSearchOutputTool; use crate::ToolSpec; use crate::mcp_tool_to_deferred_responses_api_tool; @@ -348,6 +349,7 @@ pub fn create_tool_suggest_tool(discoverable_tools: &[ToolSuggestEntry]) -> Tool Some(false.into()), ), output_schema: None, + origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/tool_discovery_tests.rs b/codex-rs/tools/src/tool_discovery_tests.rs index dbbf56c3cdb8..34190e03b143 100644 --- a/codex-rs/tools/src/tool_discovery_tests.rs +++ b/codex-rs/tools/src/tool_discovery_tests.rs @@ -132,6 +132,7 @@ fn create_tool_suggest_tool_uses_plugin_summary_fallback() { "suggest_reason".to_string(), ]), Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) ); } @@ -197,6 +198,7 @@ fn collect_tool_search_output_tools_groups_results_by_namespace() { /*additional_properties*/ None ), output_schema: None, + origin: ToolOrigin::Native, }), ResponsesApiNamespaceTool::Function(ResponsesApiTool { name: "_list_events".to_string(), @@ -209,6 +211,7 @@ fn collect_tool_search_output_tools_groups_results_by_namespace() { /*additional_properties*/ None ), output_schema: None, + origin: ToolOrigin::Native, }), ], }), @@ -226,6 +229,7 @@ fn collect_tool_search_output_tools_groups_results_by_namespace() { /*additional_properties*/ None ), output_schema: None, + origin: ToolOrigin::Native, })], }), ToolSearchOutputTool::Namespace(ResponsesApiNamespace { @@ -278,6 +282,7 @@ fn collect_tool_search_output_tools_falls_back_to_connector_name_description() { /*additional_properties*/ None ), output_schema: None, + origin: ToolOrigin::Native, })], })], ); diff --git a/codex-rs/tools/src/tool_registry_plan_tests.rs b/codex-rs/tools/src/tool_registry_plan_tests.rs index a1acaff67c6f..8c4ed0869b26 100644 --- a/codex-rs/tools/src/tool_registry_plan_tests.rs +++ b/codex-rs/tools/src/tool_registry_plan_tests.rs @@ -12,7 +12,12 @@ use crate::ResponsesApiWebSearchFilters; use crate::ResponsesApiWebSearchUserLocation; use crate::ToolHandlerSpec; use crate::ToolNamespace; +<<<<<<< HEAD use crate::ToolRegistryPlanDeferredTool; +======= +use crate::ToolOrigin; +use crate::ToolRegistryPlanAppTool; +>>>>>>> 546fa338d (Thread tool origin into code mode rendering) use crate::ToolsConfigParams; use crate::WaitAgentTimeoutOptions; use crate::mcp_call_tool_result_output_schema; @@ -1145,6 +1150,7 @@ fn test_build_specs_mcp_tools_converted() { strict: false, output_schema: Some(mcp_call_tool_result_output_schema(serde_json::json!({}))), defer_loading: None, + origin: ToolOrigin::Mcp, }) ); } diff --git a/codex-rs/tools/src/tool_spec_tests.rs b/codex-rs/tools/src/tool_spec_tests.rs index c94f54e6b5af..16802f4a036b 100644 --- a/codex-rs/tools/src/tool_spec_tests.rs +++ b/codex-rs/tools/src/tool_spec_tests.rs @@ -7,6 +7,7 @@ use crate::FreeformTool; use crate::FreeformToolFormat; use crate::JsonSchema; use crate::ResponsesApiTool; +use crate::ToolOrigin; use crate::create_tools_json_for_responses_api; use codex_protocol::config_types::WebSearchContextSize; use codex_protocol::config_types::WebSearchFilters as ConfigWebSearchFilters; @@ -30,6 +31,7 @@ fn tool_spec_name_covers_all_variants() { /*additional_properties*/ None ), output_schema: None, + origin: ToolOrigin::Native, }) .name(), "lookup_order" @@ -96,6 +98,7 @@ fn configured_tool_spec_name_delegates_to_tool_spec() { /*additional_properties*/ None ), output_schema: None, + origin: ToolOrigin::Native, }), /*supports_parallel_tool_calls*/ true, ) @@ -146,6 +149,7 @@ fn create_tools_json_for_responses_api_includes_top_level_name() { /*additional_properties*/ None ), output_schema: None, + origin: ToolOrigin::Native, })]) .expect("serialize tools"), vec![json!({ diff --git a/codex-rs/tools/src/utility_tool.rs b/codex-rs/tools/src/utility_tool.rs index b0f93c9726f9..2882007c7b6f 100644 --- a/codex-rs/tools/src/utility_tool.rs +++ b/codex-rs/tools/src/utility_tool.rs @@ -1,5 +1,6 @@ use crate::JsonSchema; use crate::ResponsesApiTool; +use crate::ToolOrigin; use crate::ToolSpec; use std::collections::BTreeMap; @@ -36,6 +37,7 @@ pub fn create_list_dir_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["dir_path".to_string()]), Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) } @@ -91,6 +93,7 @@ pub fn create_test_sync_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, /*required*/ None, Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/utility_tool_tests.rs b/codex-rs/tools/src/utility_tool_tests.rs index 2984d02f481b..2e45aaba2e90 100644 --- a/codex-rs/tools/src/utility_tool_tests.rs +++ b/codex-rs/tools/src/utility_tool_tests.rs @@ -43,6 +43,7 @@ fn list_dir_tool_matches_expected_spec() { ), ]), Some(vec!["dir_path".to_string()]), Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) ); } @@ -103,6 +104,7 @@ fn test_sync_tool_matches_expected_spec() { ), ]), /*required*/ None, Some(false.into())), output_schema: None, + origin: ToolOrigin::Native, }) ); } diff --git a/codex-rs/tools/src/view_image.rs b/codex-rs/tools/src/view_image.rs index 5d591a339f43..a152e5b56863 100644 --- a/codex-rs/tools/src/view_image.rs +++ b/codex-rs/tools/src/view_image.rs @@ -1,5 +1,6 @@ use crate::JsonSchema; use crate::ResponsesApiTool; +use crate::ToolOrigin; use crate::ToolSpec; use codex_protocol::models::VIEW_IMAGE_TOOL_NAME; use serde_json::Value; @@ -33,6 +34,7 @@ pub fn create_view_image_tool(options: ViewImageToolOptions) -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["path".to_string()]), Some(false.into())), output_schema: Some(view_image_output_schema()), + origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/view_image_tests.rs b/codex-rs/tools/src/view_image_tests.rs index 3d6782965756..5edc36d49552 100644 --- a/codex-rs/tools/src/view_image_tests.rs +++ b/codex-rs/tools/src/view_image_tests.rs @@ -20,6 +20,7 @@ fn view_image_tool_omits_detail_without_original_detail_feature() { JsonSchema::string(Some("Local filesystem path to an image file".to_string()),), )]), Some(vec!["path".to_string()]), Some(false.into())), output_schema: Some(view_image_output_schema()), + origin: ToolOrigin::Native, }) ); } @@ -49,6 +50,7 @@ fn view_image_tool_includes_detail_with_original_detail_feature() { ), ]), Some(vec!["path".to_string()]), Some(false.into())), output_schema: Some(view_image_output_schema()), + origin: ToolOrigin::Native, }) ); } From e0598723369c223a575973346be1618fdd1cb468 Mon Sep 17 00:00:00 2001 From: Vivian Fang Date: Thu, 9 Apr 2026 03:46:22 -0700 Subject: [PATCH 3/7] Use raw string for MCP code mode preamble --- codex-rs/code-mode/src/description.rs | 54 +++++++++++++++++++++------ codex-rs/tools/src/tool_definition.rs | 9 +++++ 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/codex-rs/code-mode/src/description.rs b/codex-rs/code-mode/src/description.rs index ff845545565a..dcbee45823f8 100644 --- a/codex-rs/code-mode/src/description.rs +++ b/codex-rs/code-mode/src/description.rs @@ -389,16 +389,46 @@ fn extract_mcp_structured_content_schema(output_schema: &JsonValue) -> Option<&J ) } +const MCP_TYPESCRIPT_PREAMBLE: &str = r#"type mcp_annotations = { + audience?: Array<"user" | "assistant">; + priority?: number; + lastModified?: string; +}; +type mcp_resource = + | { + uri: string; + mimeType?: string; + text: string; + annotations?: mcp_annotations; + } + | { + uri: string; + mimeType?: string; + blob: string; + annotations?: mcp_annotations; + }; +type mcp_output = + | { type: "text"; text: string; annotations?: mcp_annotations } + | { type: "image"; data: string; mimeType: string; annotations?: mcp_annotations } + | { type: "audio"; data: string; mimeType: string; annotations?: mcp_annotations } + | { + type: "resource_link"; + uri: string; + name: string; + description?: string; + mimeType?: string; + annotations?: mcp_annotations; + } + | { type: "resource"; resource: mcp_resource }; +type mcp_result = { + _meta?: unknown; + content: Array; + isError?: boolean; + structuredContent?: TStructured; +};"#; + fn render_mcp_typescript_preamble() -> &'static str { - concat!( - "type mcp_output = { [key: string]: unknown; };\n", - "type mcp_result = {\n", - " _meta?: unknown;\n", - " content: Array;\n", - " isError?: boolean;\n", - " structuredContent?: TStructured;\n", - "};" - ) + MCP_TYPESCRIPT_PREAMBLE } fn render_json_schema_to_typescript_inner(schema: &JsonValue) -> String { @@ -763,8 +793,10 @@ mod tests { &BTreeMap::new(), /*code_mode_only*/ true, ); - assert!(description.contains("### `foo` -bar")); + assert!(description.contains( + "### `foo` +bar" + )); } #[test] diff --git a/codex-rs/tools/src/tool_definition.rs b/codex-rs/tools/src/tool_definition.rs index 7fc1115a02d1..f867973201c5 100644 --- a/codex-rs/tools/src/tool_definition.rs +++ b/codex-rs/tools/src/tool_definition.rs @@ -1,11 +1,20 @@ use crate::JsonSchema; use serde_json::Value as JsonValue; +/// Where a tool definition originated before it was normalized into the shared +/// `ToolDefinition` shape. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum ToolOrigin { + /// A built-in Codex tool defined directly in Rust, such as `exec_command`, + /// `view_image`, `update_plan`, or the agent/collaboration tools. #[default] Native, + /// A tool discovered from an MCP server and converted through the MCP tool + /// pipeline. These tools may advertise an MCP `outputSchema`, which code + /// mode uses to render shared `mcp_result` aliases. Mcp, + /// A runtime-provided non-MCP tool definition, such as a `DynamicToolSpec` + /// supplied externally rather than compiled into the binary. Dynamic, } From 692dbccadc719ed55b304e53b67a228198dec4bd Mon Sep 17 00:00:00 2001 From: Vivian Fang Date: Thu, 9 Apr 2026 03:50:10 -0700 Subject: [PATCH 4/7] Inline MCP code mode preamble constant --- codex-rs/code-mode/src/description.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/codex-rs/code-mode/src/description.rs b/codex-rs/code-mode/src/description.rs index dcbee45823f8..882547f61499 100644 --- a/codex-rs/code-mode/src/description.rs +++ b/codex-rs/code-mode/src/description.rs @@ -201,7 +201,7 @@ pub fn build_exec_tool_description( if include_shared_mcp_types { sections.push(format!( "Shared MCP Types:\n```ts\n{}\n```", - render_mcp_typescript_preamble() + MCP_TYPESCRIPT_PREAMBLE )); } @@ -427,10 +427,6 @@ type mcp_result = { structuredContent?: TStructured; };"#; -fn render_mcp_typescript_preamble() -> &'static str { - MCP_TYPESCRIPT_PREAMBLE -} - fn render_json_schema_to_typescript_inner(schema: &JsonValue) -> String { match schema { JsonValue::Bool(true) => "unknown".to_string(), From 66cae70652743dd3b04dfee0533b31a56329a796 Mon Sep 17 00:00:00 2001 From: Vivian Fang Date: Thu, 9 Apr 2026 14:44:42 -0700 Subject: [PATCH 5/7] Place shared MCP types before MCP tool blocks --- codex-rs/code-mode/src/description.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/codex-rs/code-mode/src/description.rs b/codex-rs/code-mode/src/description.rs index 882547f61499..df19924defca 100644 --- a/codex-rs/code-mode/src/description.rs +++ b/codex-rs/code-mode/src/description.rs @@ -194,19 +194,17 @@ 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(|tool| tool.origin == ToolOrigin::Mcp); - - if include_shared_mcp_types { - sections.push(format!( - "Shared MCP Types:\n```ts\n{}\n```", - MCP_TYPESCRIPT_PREAMBLE - )); - } + let mut shared_mcp_types_rendered = false; for tool in enabled_tools { let name = tool.name.as_str(); + if tool.origin == ToolOrigin::Mcp && !shared_mcp_types_rendered { + nested_tool_sections.push(format!( + "Shared MCP Types:\n```ts\n{}\n```", + MCP_TYPESCRIPT_PREAMBLE + )); + shared_mcp_types_rendered = true; + } let nested_description = render_code_mode_sample_for_definition(tool); let next_namespace = namespace_descriptions .get(name) From 896c7ebd6000c36d9cf4fd4b35fe9ecc1c6469a4 Mon Sep 17 00:00:00 2001 From: Vivian Fang Date: Thu, 9 Apr 2026 15:32:39 -0700 Subject: [PATCH 6/7] Resolve rebase conflicts after code mode MCP refactor --- codex-rs/tools/src/tool_registry_plan.rs | 5 ----- codex-rs/tools/src/tool_registry_plan_tests.rs | 4 ---- 2 files changed, 9 deletions(-) diff --git a/codex-rs/tools/src/tool_registry_plan.rs b/codex-rs/tools/src/tool_registry_plan.rs index 1135c5eb6bcc..cefc1475f136 100644 --- a/codex-rs/tools/src/tool_registry_plan.rs +++ b/codex-rs/tools/src/tool_registry_plan.rs @@ -13,13 +13,8 @@ use crate::ToolSpec; use crate::ToolsConfig; use crate::ViewImageToolOptions; use crate::WebSearchToolOptions; -<<<<<<< HEAD -use crate::collect_code_mode_tool_definitions; -use crate::collect_tool_search_source_infos; -======= use crate::collect_code_mode_exec_prompt_tool_definitions; use crate::collect_tool_search_app_infos; ->>>>>>> f6d6f0141 (Refine code mode MCP result typing) use crate::collect_tool_suggest_entries; use crate::create_apply_patch_freeform_tool; use crate::create_apply_patch_json_tool; diff --git a/codex-rs/tools/src/tool_registry_plan_tests.rs b/codex-rs/tools/src/tool_registry_plan_tests.rs index 8c4ed0869b26..f7b1bf0bd5d8 100644 --- a/codex-rs/tools/src/tool_registry_plan_tests.rs +++ b/codex-rs/tools/src/tool_registry_plan_tests.rs @@ -12,12 +12,8 @@ use crate::ResponsesApiWebSearchFilters; use crate::ResponsesApiWebSearchUserLocation; use crate::ToolHandlerSpec; use crate::ToolNamespace; -<<<<<<< HEAD -use crate::ToolRegistryPlanDeferredTool; -======= use crate::ToolOrigin; use crate::ToolRegistryPlanAppTool; ->>>>>>> 546fa338d (Thread tool origin into code mode rendering) use crate::ToolsConfigParams; use crate::WaitAgentTimeoutOptions; use crate::mcp_call_tool_result_output_schema; From 3c7fd70c6406105449914e573cb0f13a90d63507 Mon Sep 17 00:00:00 2001 From: Vivian Fang Date: Thu, 9 Apr 2026 19:56:22 -0700 Subject: [PATCH 7/7] Detect MCP tool results from output schema --- codex-rs/code-mode/src/description.rs | 281 ++++++++++-------- codex-rs/code-mode/src/lib.rs | 1 - codex-rs/core/tests/suite/code_mode.rs | 2 +- codex-rs/tools/src/agent_job_tool.rs | 3 - codex-rs/tools/src/agent_job_tool_tests.rs | 2 - codex-rs/tools/src/agent_tool.rs | 12 - codex-rs/tools/src/apply_patch_tool.rs | 2 - codex-rs/tools/src/apply_patch_tool_tests.rs | 1 - codex-rs/tools/src/code_mode.rs | 9 - codex-rs/tools/src/code_mode_tests.rs | 6 - codex-rs/tools/src/dynamic_tool.rs | 2 - codex-rs/tools/src/dynamic_tool_tests.rs | 3 - codex-rs/tools/src/js_repl_tool.rs | 2 - codex-rs/tools/src/js_repl_tool_tests.rs | 1 - codex-rs/tools/src/lib.rs | 1 - codex-rs/tools/src/local_tool.rs | 6 - codex-rs/tools/src/local_tool_tests.rs | 6 - codex-rs/tools/src/mcp_resource_tool.rs | 4 - codex-rs/tools/src/mcp_resource_tool_tests.rs | 3 - codex-rs/tools/src/mcp_tool.rs | 2 - codex-rs/tools/src/mcp_tool_tests.rs | 4 - codex-rs/tools/src/plan_tool.rs | 2 - codex-rs/tools/src/request_user_input_tool.rs | 2 - .../src/request_user_input_tool_tests.rs | 1 - codex-rs/tools/src/responses_api.rs | 4 - codex-rs/tools/src/responses_api_tests.rs | 6 - codex-rs/tools/src/tool_definition.rs | 18 -- codex-rs/tools/src/tool_definition_tests.rs | 3 - codex-rs/tools/src/tool_discovery.rs | 2 - codex-rs/tools/src/tool_discovery_tests.rs | 5 - codex-rs/tools/src/tool_registry_plan.rs | 2 +- .../tools/src/tool_registry_plan_tests.rs | 90 +++--- codex-rs/tools/src/tool_spec_tests.rs | 4 - codex-rs/tools/src/utility_tool.rs | 3 - codex-rs/tools/src/utility_tool_tests.rs | 2 - codex-rs/tools/src/view_image.rs | 2 - codex-rs/tools/src/view_image_tests.rs | 2 - 37 files changed, 205 insertions(+), 296 deletions(-) diff --git a/codex-rs/code-mode/src/description.rs b/codex-rs/code-mode/src/description.rs index df19924defca..ac18fafd896f 100644 --- a/codex-rs/code-mode/src/description.rs +++ b/codex-rs/code-mode/src/description.rs @@ -39,6 +39,83 @@ const WAIT_DESCRIPTION_TEMPLATE: &str = r#"- Use `wait` only after `exec` return - `wait` returns only the new output since the last yield, or the final completion or termination result for that cell. - If the cell is still running, `wait` may yield again with the same `cell_id`. - If the cell has already finished, `wait` returns the completed result and closes the cell."#; +// Based off of https://modelcontextprotocol.io/specification/draft/schema#calltoolresult +const MCP_TYPESCRIPT_PREAMBLE: &str = r#"type Role = "user" | "assistant"; +type MetaObject = Record; +type Annotations = { + audience?: Role[]; + priority?: number; + lastModified?: string; +}; +type Icon = { + src: string; + mimeType?: string; + sizes?: string[]; + theme?: "light" | "dark"; +}; +type TextResourceContents = { + uri: string; + mimeType?: string; + _meta?: MetaObject; + text: string; +}; +type BlobResourceContents = { + uri: string; + mimeType?: string; + _meta?: MetaObject; + blob: string; +}; +type TextContent = { + type: "text"; + text: string; + annotations?: Annotations; + _meta?: MetaObject; +}; +type ImageContent = { + type: "image"; + data: string; + mimeType: string; + annotations?: Annotations; + _meta?: MetaObject; +}; +type AudioContent = { + type: "audio"; + data: string; + mimeType: string; + annotations?: Annotations; + _meta?: MetaObject; +}; +type ResourceLink = { + icons?: Icon[]; + name: string; + title?: string; + uri: string; + description?: string; + mimeType?: string; + annotations?: Annotations; + size?: number; + _meta?: MetaObject; + type: "resource_link"; +}; +type EmbeddedResource = { + type: "resource"; + resource: TextResourceContents | BlobResourceContents; + annotations?: Annotations; + _meta?: MetaObject; +}; +type ContentBlock = + | TextContent + | ImageContent + | AudioContent + | ResourceLink + | EmbeddedResource; +type CallToolResult = { + _meta?: MetaObject; + content: ContentBlock[]; + isError?: boolean; + structuredContent?: TStructured; + [key: string]: unknown; +};"#; pub const CODE_MODE_PRAGMA_PREFIX: &str = "// @exec:"; @@ -49,14 +126,6 @@ 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, @@ -64,7 +133,6 @@ pub struct ToolDefinition { pub kind: CodeModeToolKind, pub input_schema: Option, pub output_schema: Option, - pub origin: ToolOrigin, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -194,17 +262,12 @@ 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 mut shared_mcp_types_rendered = false; + let has_mcp_tools = enabled_tools + .iter() + .any(|tool| mcp_structured_content_schema(tool.output_schema.as_ref()).is_some()); for tool in enabled_tools { let name = tool.name.as_str(); - if tool.origin == ToolOrigin::Mcp && !shared_mcp_types_rendered { - nested_tool_sections.push(format!( - "Shared MCP Types:\n```ts\n{}\n```", - MCP_TYPESCRIPT_PREAMBLE - )); - shared_mcp_types_rendered = true; - } let nested_description = render_code_mode_sample_for_definition(tool); let next_namespace = namespace_descriptions .get(name) @@ -234,6 +297,11 @@ pub fn build_exec_tool_description( } } + if has_mcp_tools { + sections.push(format!( + "Shared MCP Types:\n```ts\n{MCP_TYPESCRIPT_PREAMBLE}\n```" + )); + } let nested_tool_reference = nested_tool_sections.join("\n\n"); sections.push(nested_tool_reference); } @@ -320,33 +388,22 @@ fn render_code_mode_sample_for_definition(definition: &ToolDefinition) -> String .unwrap_or_else(|| "unknown".to_string()), CodeModeToolKind::Freeform => "string".to_string(), }; - if definition.origin == ToolOrigin::Mcp { - let structured_content_type = definition + let output_type = if let Some(structured_content_schema) = + mcp_structured_content_schema(definition.output_schema.as_ref()) + { + let structured_content_type = render_json_schema_to_typescript(structured_content_schema); + if structured_content_type == "unknown" { + "CallToolResult".to_string() + } else { + format!("CallToolResult<{structured_content_type}>") + } + } else { + definition .output_schema .as_ref() - .and_then(extract_mcp_structured_content_schema) .map(render_json_schema_to_typescript) - .unwrap_or_else(|| "unknown".to_string()); - let output_type = if structured_content_type == "unknown" { - "mcp_result".to_string() - } else { - format!("mcp_result<{structured_content_type}>") - }; - - return render_code_mode_sample( - &definition.description, - &definition.name, - input_name, - input_type, - output_type, - ); - } - - let output_type = definition - .output_schema - .as_ref() - .map(render_json_schema_to_typescript) - .unwrap_or_else(|| "unknown".to_string()); + .unwrap_or_else(|| "unknown".to_string()) + }; render_code_mode_sample( &definition.description, &definition.name, @@ -378,8 +435,40 @@ pub fn render_json_schema_to_typescript(schema: &JsonValue) -> String { render_json_schema_to_typescript_inner(schema) } -fn extract_mcp_structured_content_schema(output_schema: &JsonValue) -> Option<&JsonValue> { - let properties = output_schema.get("properties")?.as_object()?; +fn mcp_structured_content_schema(output_schema: Option<&JsonValue>) -> Option<&JsonValue> { + let output_schema = output_schema?; + let properties = output_schema + .get("properties") + .and_then(JsonValue::as_object)?; + let content_schema = properties.get("content").and_then(JsonValue::as_object)?; + if content_schema.get("type").and_then(JsonValue::as_str) != Some("array") { + return None; + } + + if content_schema + .get("items") + .and_then(JsonValue::as_object) + .is_none_or(|items| items.get("type").and_then(JsonValue::as_str) != Some("object")) + { + return None; + } + + if properties + .get("isError") + .and_then(JsonValue::as_object) + .is_none_or(|schema| schema.get("type").and_then(JsonValue::as_str) != Some("boolean")) + { + return None; + } + + if properties + .get("_meta") + .and_then(JsonValue::as_object) + .is_none_or(|schema| schema.get("type").and_then(JsonValue::as_str) != Some("object")) + { + return None; + } + Some( properties .get("structuredContent") @@ -387,44 +476,6 @@ fn extract_mcp_structured_content_schema(output_schema: &JsonValue) -> Option<&J ) } -const MCP_TYPESCRIPT_PREAMBLE: &str = r#"type mcp_annotations = { - audience?: Array<"user" | "assistant">; - priority?: number; - lastModified?: string; -}; -type mcp_resource = - | { - uri: string; - mimeType?: string; - text: string; - annotations?: mcp_annotations; - } - | { - uri: string; - mimeType?: string; - blob: string; - annotations?: mcp_annotations; - }; -type mcp_output = - | { type: "text"; text: string; annotations?: mcp_annotations } - | { type: "image"; data: string; mimeType: string; annotations?: mcp_annotations } - | { type: "audio"; data: string; mimeType: string; annotations?: mcp_annotations } - | { - type: "resource_link"; - uri: string; - name: string; - description?: string; - mimeType?: string; - annotations?: mcp_annotations; - } - | { type: "resource"; resource: mcp_resource }; -type mcp_result = { - _meta?: unknown; - content: Array; - isError?: boolean; - structuredContent?: TStructured; -};"#; - fn render_json_schema_to_typescript_inner(schema: &JsonValue) -> String { match schema { JsonValue::Bool(true) => "unknown".to_string(), @@ -651,15 +702,34 @@ 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; use super::parse_exec_source; use pretty_assertions::assert_eq; + use serde_json::Value as JsonValue; use serde_json::json; use std::collections::BTreeMap; + fn mcp_call_tool_result_schema(structured_content_schema: JsonValue) -> JsonValue { + json!({ + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "type": "object" + } + }, + "structuredContent": structured_content_schema, + "isError": { "type": "boolean" }, + "_meta": { "type": "object" } + }, + "required": ["content"], + "additionalProperties": false + }) + } + #[test] fn parse_exec_source_without_pragma() { assert_eq!( @@ -713,7 +783,6 @@ mod tests { "properties": { "ok": { "type": "boolean" } }, "required": ["ok"] })), - origin: ToolOrigin::Native, }; let description = augment_tool_definition(definition).description; @@ -758,7 +827,6 @@ mod tests { }, "required": ["forecast"] })), - origin: ToolOrigin::Native, }; let description = augment_tool_definition(definition).description; @@ -782,7 +850,6 @@ mod tests { kind: CodeModeToolKind::Function, input_schema: None, output_schema: None, - origin: ToolOrigin::Native, }], &BTreeMap::new(), /*code_mode_only*/ true, @@ -830,12 +897,11 @@ bar" "properties": {}, "additionalProperties": false })), - output_schema: Some(json!({ + output_schema: Some(mcp_call_tool_result_schema(json!({ "type": "object", "properties": {}, "additionalProperties": false - })), - origin: ToolOrigin::Mcp, + }))), }, ToolDefinition { name: "mcp__sample__beta".to_string(), @@ -846,37 +912,23 @@ bar" "properties": {}, "additionalProperties": false })), - output_schema: Some(json!({ + output_schema: Some(mcp_call_tool_result_schema(json!({ "type": "object", "properties": {}, "additionalProperties": false - })), - origin: ToolOrigin::Mcp, + }))), }, ], &namespace_descriptions, /*code_mode_only*/ true, ); assert_eq!(description.matches("## mcp__sample").count(), 1); + assert!(description.contains("## mcp__sample\nShared namespace guidance.")); assert!(description.contains( - r#"## mcp__sample -Shared namespace guidance. - -### `mcp__sample__alpha` -First tool - -exec tool declaration: -```ts -declare const tools: { mcp__sample__alpha(args: {}): Promise; }; -``` - -### `mcp__sample__beta` -Second tool - -exec tool declaration: -```ts -declare const tools: { mcp__sample__beta(args: {}): Promise; }; -```"# + "declare const tools: { mcp__sample__alpha(args: {}): Promise>; };" + )); + assert!(description.contains( + "declare const tools: { mcp__sample__beta(args: {}): Promise>; };" )); } @@ -899,12 +951,11 @@ declare const tools: { mcp__sample__beta(args: {}): Promise; }; "properties": {}, "additionalProperties": false })), - output_schema: Some(json!({ + output_schema: Some(mcp_call_tool_result_schema(json!({ "type": "object", "properties": {}, "additionalProperties": false - })), - origin: ToolOrigin::Mcp, + }))), }], &namespace_descriptions, /*code_mode_only*/ true, @@ -948,7 +999,6 @@ declare const tools: { mcp__sample__beta(args: {}): Promise; }; "required": ["content"], "additionalProperties": false })), - origin: ToolOrigin::Mcp, }); let second_tool = augment_tool_definition(ToolDefinition { name: "mcp__sample__beta".to_string(), @@ -982,7 +1032,6 @@ declare const tools: { mcp__sample__beta(args: {}): Promise; }; "required": ["content"], "additionalProperties": false })), - origin: ToolOrigin::Mcp, }); let description = build_exec_tool_description( @@ -993,7 +1042,6 @@ declare const tools: { mcp__sample__beta(args: {}): Promise; }; 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, @@ -1001,7 +1049,6 @@ declare const tools: { mcp__sample__beta(args: {}): Promise; }; kind: second_tool.kind, input_schema: second_tool.input_schema, output_schema: second_tool.output_schema, - origin: second_tool.origin, }, ], &BTreeMap::new(), @@ -1010,7 +1057,7 @@ declare const tools: { mcp__sample__beta(args: {}): Promise; }; assert_eq!( description - .matches("type mcp_result") + .matches("type CallToolResult") .count(), 1 ); diff --git a/codex-rs/code-mode/src/lib.rs b/codex-rs/code-mode/src/lib.rs index 585a3b7427d6..880e84ef4a23 100644 --- a/codex-rs/code-mode/src/lib.rs +++ b/codex-rs/code-mode/src/lib.rs @@ -7,7 +7,6 @@ pub use description::CODE_MODE_PRAGMA_PREFIX; pub use description::CodeModeToolKind; pub use description::ToolDefinition; pub use description::ToolNamespaceDescription; -pub use description::ToolOrigin; pub use description::augment_tool_definition; pub use description::build_exec_tool_description; pub use description::build_wait_tool_description; diff --git a/codex-rs/core/tests/suite/code_mode.rs b/codex-rs/core/tests/suite/code_mode.rs index 6d2643b95a4e..54da2f4fe4ed 100644 --- a/codex-rs/core/tests/suite/code_mode.rs +++ b/codex-rs/core/tests/suite/code_mode.rs @@ -2285,7 +2285,7 @@ text(JSON.stringify(tool)); "exec tool declaration:\n", "```ts\n", "declare const tools: { mcp__rmcp__echo(args: { env_var?: string; message: string; }): ", - "Promise>; };\n", + "Promise>; };\n", "```", ), }) diff --git a/codex-rs/tools/src/agent_job_tool.rs b/codex-rs/tools/src/agent_job_tool.rs index deb89c6f83ee..bcdec5dde252 100644 --- a/codex-rs/tools/src/agent_job_tool.rs +++ b/codex-rs/tools/src/agent_job_tool.rs @@ -1,6 +1,5 @@ use crate::JsonSchema; use crate::ResponsesApiTool; -use crate::ToolOrigin; use crate::ToolSpec; use std::collections::BTreeMap; @@ -61,7 +60,6 @@ pub fn create_spawn_agents_on_csv_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["csv_path".to_string(), "instruction".to_string()]), Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) } @@ -101,7 +99,6 @@ pub fn create_report_agent_job_result_tool() -> ToolSpec { "result".to_string(), ]), Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/agent_job_tool_tests.rs b/codex-rs/tools/src/agent_job_tool_tests.rs index d542334fc495..95f865977307 100644 --- a/codex-rs/tools/src/agent_job_tool_tests.rs +++ b/codex-rs/tools/src/agent_job_tool_tests.rs @@ -69,7 +69,6 @@ fn spawn_agents_on_csv_tool_requires_csv_and_instruction() { ), ]), Some(vec!["csv_path".to_string(), "instruction".to_string()]), Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) ); } @@ -115,7 +114,6 @@ fn report_agent_job_result_tool_requires_result_payload() { "result".to_string(), ]), Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) ); } diff --git a/codex-rs/tools/src/agent_tool.rs b/codex-rs/tools/src/agent_tool.rs index d3c80c4d5fc1..6e75963ba186 100644 --- a/codex-rs/tools/src/agent_tool.rs +++ b/codex-rs/tools/src/agent_tool.rs @@ -1,6 +1,5 @@ use crate::JsonSchema; use crate::ResponsesApiTool; -use crate::ToolOrigin; use crate::ToolSpec; use codex_protocol::openai_models::ModelPreset; use serde_json::Value; @@ -45,7 +44,6 @@ pub fn create_spawn_agent_tool_v1(options: SpawnAgentToolOptions<'_>) -> ToolSpe defer_loading: None, parameters: JsonSchema::object(properties, /*required*/ None, Some(false.into())), output_schema: Some(spawn_agent_output_schema_v1()), - origin: ToolOrigin::Native, }) } @@ -87,7 +85,6 @@ pub fn create_spawn_agent_tool_v2(options: SpawnAgentToolOptions<'_>) -> ToolSpe output_schema: Some(spawn_agent_output_schema_v2( options.hide_agent_type_model_reasoning, )), - origin: ToolOrigin::Native, }) } @@ -122,7 +119,6 @@ pub fn create_send_input_tool_v1() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["target".to_string()]), Some(false.into())), output_schema: Some(send_input_output_schema()), - origin: ToolOrigin::Native, }) } @@ -150,7 +146,6 @@ pub fn create_send_message_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["target".to_string(), "message".to_string()]), Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) } @@ -185,7 +180,6 @@ pub fn create_followup_task_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["target".to_string(), "message".to_string()]), Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) } @@ -204,7 +198,6 @@ pub fn create_resume_agent_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["id".to_string()]), Some(false.into())), output_schema: Some(resume_agent_output_schema()), - origin: ToolOrigin::Native, }) } @@ -217,7 +210,6 @@ pub fn create_wait_agent_tool_v1(options: WaitAgentTimeoutOptions) -> ToolSpec { defer_loading: None, parameters: wait_agent_tool_parameters_v1(options), output_schema: Some(wait_output_schema_v1()), - origin: ToolOrigin::Native, }) } @@ -230,7 +222,6 @@ pub fn create_wait_agent_tool_v2(options: WaitAgentTimeoutOptions) -> ToolSpec { defer_loading: None, parameters: wait_agent_tool_parameters_v2(options), output_schema: Some(wait_output_schema_v2()), - origin: ToolOrigin::Native, }) } @@ -252,7 +243,6 @@ pub fn create_list_agents_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, /*required*/ None, Some(false.into())), output_schema: Some(list_agents_output_schema()), - origin: ToolOrigin::Native, }) } @@ -269,7 +259,6 @@ pub fn create_close_agent_tool_v1() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["target".to_string()]), Some(false.into())), output_schema: Some(close_agent_output_schema()), - origin: ToolOrigin::Native, }) } @@ -288,7 +277,6 @@ pub fn create_close_agent_tool_v2() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["target".to_string()]), Some(false.into())), output_schema: Some(close_agent_output_schema()), - origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/apply_patch_tool.rs b/codex-rs/tools/src/apply_patch_tool.rs index 9256dfea5901..469bb5236769 100644 --- a/codex-rs/tools/src/apply_patch_tool.rs +++ b/codex-rs/tools/src/apply_patch_tool.rs @@ -2,7 +2,6 @@ use crate::FreeformTool; use crate::FreeformToolFormat; use crate::JsonSchema; use crate::ResponsesApiTool; -use crate::ToolOrigin; use crate::ToolSpec; use serde::Deserialize; use serde::Serialize; @@ -119,7 +118,6 @@ pub fn create_apply_patch_json_tool() -> ToolSpec { Some(false.into()), ), output_schema: None, - origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/apply_patch_tool_tests.rs b/codex-rs/tools/src/apply_patch_tool_tests.rs index 8b227cf735df..c128594587a3 100644 --- a/codex-rs/tools/src/apply_patch_tool_tests.rs +++ b/codex-rs/tools/src/apply_patch_tool_tests.rs @@ -41,7 +41,6 @@ fn create_apply_patch_json_tool_matches_expected_spec() { Some(false.into()) ), output_schema: None, - origin: ToolOrigin::Native, }) ); } diff --git a/codex-rs/tools/src/code_mode.rs b/codex-rs/tools/src/code_mode.rs index 2cd0c26ee155..35d95b271ab4 100644 --- a/codex-rs/tools/src/code_mode.rs +++ b/codex-rs/tools/src/code_mode.rs @@ -2,11 +2,9 @@ use crate::FreeformTool; use crate::FreeformToolFormat; use crate::JsonSchema; use crate::ResponsesApiTool; -use crate::ToolOrigin; use crate::ToolSpec; use codex_code_mode::CodeModeToolKind; use codex_code_mode::ToolDefinition as CodeModeToolDefinition; -use codex_code_mode::ToolOrigin as CodeModeToolOrigin; use std::collections::BTreeMap; /// Augment tool descriptions with code-mode-specific exec samples. @@ -105,7 +103,6 @@ pub fn create_wait_tool() -> ToolSpec { Some(false.into()), ), output_schema: None, - origin: ToolOrigin::Native, defer_loading: None, }) } @@ -148,11 +145,6 @@ fn code_mode_tool_definition_for_spec(spec: &ToolSpec) -> Option CodeModeToolOrigin::Native, - ToolOrigin::Mcp => CodeModeToolOrigin::Mcp, - ToolOrigin::Dynamic => CodeModeToolOrigin::Dynamic, - }, }), ToolSpec::Freeform(tool) => Some(CodeModeToolDefinition { name: tool.name.clone(), @@ -160,7 +152,6 @@ fn code_mode_tool_definition_for_spec(spec: &ToolSpec) -> Option; }; kind: codex_code_mode::CodeModeToolKind::Freeform, input_schema: None, output_schema: None, - origin: codex_code_mode::ToolOrigin::Native, }) ); } @@ -180,7 +176,6 @@ fn create_wait_tool_matches_expected_spec() { ), ]), Some(vec!["cell_id".to_string()]), Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) ); } @@ -193,7 +188,6 @@ fn create_code_mode_tool_matches_expected_spec() { kind: codex_code_mode::CodeModeToolKind::Function, input_schema: None, output_schema: None, - origin: codex_code_mode::ToolOrigin::Native, }]; assert_eq!( diff --git a/codex-rs/tools/src/dynamic_tool.rs b/codex-rs/tools/src/dynamic_tool.rs index 356cff2027e1..372dbce29d07 100644 --- a/codex-rs/tools/src/dynamic_tool.rs +++ b/codex-rs/tools/src/dynamic_tool.rs @@ -1,5 +1,4 @@ use crate::ToolDefinition; -use crate::ToolOrigin; use crate::parse_tool_input_schema; use codex_protocol::dynamic_tools::DynamicToolSpec; @@ -15,7 +14,6 @@ pub fn parse_dynamic_tool(tool: &DynamicToolSpec) -> Result ToolSpec { defer_loading: None, parameters: JsonSchema::object(BTreeMap::new(), /*required*/ None, Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/js_repl_tool_tests.rs b/codex-rs/tools/src/js_repl_tool_tests.rs index 77c87aff90f6..7d6d63f179b9 100644 --- a/codex-rs/tools/src/js_repl_tool_tests.rs +++ b/codex-rs/tools/src/js_repl_tool_tests.rs @@ -36,7 +36,6 @@ fn js_repl_reset_tool_matches_expected_spec() { Some(false.into()) ), output_schema: None, - origin: ToolOrigin::Native, }) ); } diff --git a/codex-rs/tools/src/lib.rs b/codex-rs/tools/src/lib.rs index ec9c87e32976..9a3107dd666b 100644 --- a/codex-rs/tools/src/lib.rs +++ b/codex-rs/tools/src/lib.rs @@ -95,7 +95,6 @@ pub use tool_config::ToolsConfigParams; pub use tool_config::UnifiedExecShellMode; pub use tool_config::ZshForkConfig; pub use tool_definition::ToolDefinition; -pub use tool_definition::ToolOrigin; pub use tool_discovery::DiscoverablePluginInfo; pub use tool_discovery::DiscoverableTool; pub use tool_discovery::DiscoverableToolAction; diff --git a/codex-rs/tools/src/local_tool.rs b/codex-rs/tools/src/local_tool.rs index 0661187d594c..3e369ab1e331 100644 --- a/codex-rs/tools/src/local_tool.rs +++ b/codex-rs/tools/src/local_tool.rs @@ -1,6 +1,5 @@ use crate::JsonSchema; use crate::ResponsesApiTool; -use crate::ToolOrigin; use crate::ToolSpec; use serde_json::Value; use serde_json::json; @@ -87,7 +86,6 @@ pub fn create_exec_command_tool(options: CommandToolOptions) -> ToolSpec { Some(false.into()), ), output_schema: Some(unified_exec_output_schema()), - origin: ToolOrigin::Native, }) } @@ -132,7 +130,6 @@ pub fn create_write_stdin_tool() -> ToolSpec { Some(false.into()), ), output_schema: Some(unified_exec_output_schema()), - origin: ToolOrigin::Native, }) } @@ -196,7 +193,6 @@ Examples of valid command strings: Some(false.into()), ), output_schema: None, - origin: ToolOrigin::Native, }) } @@ -267,7 +263,6 @@ Examples of valid command strings: Some(false.into()), ), output_schema: None, - origin: ToolOrigin::Native, }) } @@ -293,7 +288,6 @@ pub fn create_request_permissions_tool(description: String) -> ToolSpec { Some(false.into()), ), output_schema: None, - origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/local_tool_tests.rs b/codex-rs/tools/src/local_tool_tests.rs index bb38014b272a..b751545b3ace 100644 --- a/codex-rs/tools/src/local_tool_tests.rs +++ b/codex-rs/tools/src/local_tool_tests.rs @@ -87,7 +87,6 @@ Examples of valid command strings: Some(false.into()) ), output_schema: None, - origin: ToolOrigin::Native, }) ); } @@ -171,7 +170,6 @@ fn exec_command_tool_matches_expected_spec() { Some(false.into()) ), output_schema: Some(unified_exec_output_schema()), - origin: ToolOrigin::Native, }) ); } @@ -222,7 +220,6 @@ fn write_stdin_tool_matches_expected_spec() { Some(false.into()) ), output_schema: Some(unified_exec_output_schema()), - origin: ToolOrigin::Native, }) ); } @@ -294,7 +291,6 @@ Examples of valid command strings: Some(false.into()) ), output_schema: None, - origin: ToolOrigin::Native, }) ); } @@ -327,7 +323,6 @@ fn request_permissions_tool_includes_full_permission_schema() { Some(false.into()) ), output_schema: None, - origin: ToolOrigin::Native, }) ); } @@ -402,7 +397,6 @@ Examples of valid command strings: Some(false.into()) ), output_schema: None, - origin: ToolOrigin::Native, }) ); } diff --git a/codex-rs/tools/src/mcp_resource_tool.rs b/codex-rs/tools/src/mcp_resource_tool.rs index 191aed6ab376..fd2e0ac2a4e5 100644 --- a/codex-rs/tools/src/mcp_resource_tool.rs +++ b/codex-rs/tools/src/mcp_resource_tool.rs @@ -1,6 +1,5 @@ use crate::JsonSchema; use crate::ResponsesApiTool; -use crate::ToolOrigin; use crate::ToolSpec; use std::collections::BTreeMap; @@ -29,7 +28,6 @@ pub fn create_list_mcp_resources_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, /*required*/ None, Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) } @@ -58,7 +56,6 @@ pub fn create_list_mcp_resource_templates_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, /*required*/ None, Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) } @@ -93,7 +90,6 @@ pub fn create_read_mcp_resource_tool() -> ToolSpec { Some(false.into()), ), output_schema: None, - origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/mcp_resource_tool_tests.rs b/codex-rs/tools/src/mcp_resource_tool_tests.rs index 6c4ec2a37ecb..2c0d03ee5138 100644 --- a/codex-rs/tools/src/mcp_resource_tool_tests.rs +++ b/codex-rs/tools/src/mcp_resource_tool_tests.rs @@ -29,7 +29,6 @@ fn list_mcp_resources_tool_matches_expected_spec() { ), ]), /*required*/ None, Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) ); } @@ -60,7 +59,6 @@ fn list_mcp_resource_templates_tool_matches_expected_spec() { ), ]), /*required*/ None, Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) ); } @@ -93,7 +91,6 @@ fn read_mcp_resource_tool_matches_expected_spec() { ), ]), Some(vec!["server".to_string(), "uri".to_string()]), Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) ); } diff --git a/codex-rs/tools/src/mcp_tool.rs b/codex-rs/tools/src/mcp_tool.rs index 968f738d68c5..337a8e42adbb 100644 --- a/codex-rs/tools/src/mcp_tool.rs +++ b/codex-rs/tools/src/mcp_tool.rs @@ -1,5 +1,4 @@ use crate::ToolDefinition; -use crate::ToolOrigin; use crate::parse_tool_input_schema; use serde_json::Value as JsonValue; use serde_json::json; @@ -33,7 +32,6 @@ pub fn parse_mcp_tool(tool: &rmcp::model::Tool) -> Result ToolSpec { Some(false.into()), ), output_schema: None, - origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/request_user_input_tool_tests.rs b/codex-rs/tools/src/request_user_input_tool_tests.rs index 3f015da9f903..68c2639e406c 100644 --- a/codex-rs/tools/src/request_user_input_tool_tests.rs +++ b/codex-rs/tools/src/request_user_input_tool_tests.rs @@ -85,7 +85,6 @@ fn request_user_input_tool_includes_questions_schema() { ), )]), Some(vec!["questions".to_string()]), Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) ); } diff --git a/codex-rs/tools/src/responses_api.rs b/codex-rs/tools/src/responses_api.rs index c61083ac9d6c..50bb4f223243 100644 --- a/codex-rs/tools/src/responses_api.rs +++ b/codex-rs/tools/src/responses_api.rs @@ -1,6 +1,5 @@ use crate::JsonSchema; use crate::ToolDefinition; -use crate::ToolOrigin; use crate::parse_dynamic_tool; use crate::parse_mcp_tool; use codex_protocol::dynamic_tools::DynamicToolSpec; @@ -35,8 +34,6 @@ pub struct ResponsesApiTool { pub parameters: JsonSchema, #[serde(skip)] pub output_schema: Option, - #[serde(skip)] - pub origin: ToolOrigin, } #[derive(Debug, Clone, Serialize, PartialEq)] @@ -98,7 +95,6 @@ pub fn tool_definition_to_responses_api_tool(tool_definition: ToolDefinition) -> defer_loading: tool_definition.defer_loading.then_some(true), parameters: tool_definition.input_schema, output_schema: tool_definition.output_schema, - origin: tool_definition.origin, } } diff --git a/codex-rs/tools/src/responses_api_tests.rs b/codex-rs/tools/src/responses_api_tests.rs index a0f0819c0c30..c3ce4cec2b7c 100644 --- a/codex-rs/tools/src/responses_api_tests.rs +++ b/codex-rs/tools/src/responses_api_tests.rs @@ -7,7 +7,6 @@ use super::mcp_tool_to_deferred_responses_api_tool; use super::tool_definition_to_responses_api_tool; use crate::JsonSchema; use crate::ToolDefinition; -use crate::ToolOrigin; use codex_protocol::dynamic_tools::DynamicToolSpec; use pretty_assertions::assert_eq; use serde_json::json; @@ -28,7 +27,6 @@ fn tool_definition_to_responses_api_tool_omits_false_defer_loading() { Some(false.into()) ), output_schema: Some(json!({"type": "object"})), - origin: ToolOrigin::Native, defer_loading: false, }), ResponsesApiTool { @@ -45,7 +43,6 @@ fn tool_definition_to_responses_api_tool_omits_false_defer_loading() { Some(false.into()) ), output_schema: Some(json!({"type": "object"})), - origin: ToolOrigin::Native, } ); } @@ -82,7 +79,6 @@ fn dynamic_tool_to_responses_api_tool_preserves_defer_loading() { Some(false.into()) ), output_schema: None, - origin: ToolOrigin::Dynamic, } ); } @@ -124,7 +120,6 @@ fn mcp_tool_to_deferred_responses_api_tool_sets_defer_loading() { JsonSchema::string(/*description*/ None), )]), Some(vec!["order_id".to_string()]), Some(false.into())), output_schema: None, - origin: ToolOrigin::Mcp, } ); } @@ -145,7 +140,6 @@ fn tool_search_output_namespace_serializes_with_deferred_child_tools() { /*additional_properties*/ None, ), output_schema: None, - origin: ToolOrigin::Mcp, })], }); diff --git a/codex-rs/tools/src/tool_definition.rs b/codex-rs/tools/src/tool_definition.rs index f867973201c5..d3c5b0209baa 100644 --- a/codex-rs/tools/src/tool_definition.rs +++ b/codex-rs/tools/src/tool_definition.rs @@ -1,23 +1,6 @@ use crate::JsonSchema; use serde_json::Value as JsonValue; -/// Where a tool definition originated before it was normalized into the shared -/// `ToolDefinition` shape. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub enum ToolOrigin { - /// A built-in Codex tool defined directly in Rust, such as `exec_command`, - /// `view_image`, `update_plan`, or the agent/collaboration tools. - #[default] - Native, - /// A tool discovered from an MCP server and converted through the MCP tool - /// pipeline. These tools may advertise an MCP `outputSchema`, which code - /// mode uses to render shared `mcp_result` aliases. - Mcp, - /// A runtime-provided non-MCP tool definition, such as a `DynamicToolSpec` - /// supplied externally rather than compiled into the binary. - Dynamic, -} - /// Tool metadata and schemas that downstream crates can adapt into higher-level /// tool specs. #[derive(Debug, PartialEq)] @@ -26,7 +9,6 @@ pub struct ToolDefinition { pub description: String, pub input_schema: JsonSchema, pub output_schema: Option, - pub origin: ToolOrigin, pub defer_loading: bool, } diff --git a/codex-rs/tools/src/tool_definition_tests.rs b/codex-rs/tools/src/tool_definition_tests.rs index 67c9ecfc78c6..0d4d97f5ce9e 100644 --- a/codex-rs/tools/src/tool_definition_tests.rs +++ b/codex-rs/tools/src/tool_definition_tests.rs @@ -1,6 +1,5 @@ use super::ToolDefinition; use crate::JsonSchema; -use crate::ToolOrigin; use pretty_assertions::assert_eq; use std::collections::BTreeMap; @@ -16,7 +15,6 @@ fn tool_definition() -> ToolDefinition { output_schema: Some(serde_json::json!({ "type": "object", })), - origin: ToolOrigin::Native, defer_loading: false, } } @@ -38,7 +36,6 @@ fn into_deferred_drops_output_schema_and_sets_defer_loading() { tool_definition().into_deferred(), ToolDefinition { output_schema: None, - origin: ToolOrigin::Native, defer_loading: true, ..tool_definition() } diff --git a/codex-rs/tools/src/tool_discovery.rs b/codex-rs/tools/src/tool_discovery.rs index e84a68d5fed4..594d4011b862 100644 --- a/codex-rs/tools/src/tool_discovery.rs +++ b/codex-rs/tools/src/tool_discovery.rs @@ -2,7 +2,6 @@ use crate::JsonSchema; use crate::ResponsesApiNamespace; use crate::ResponsesApiNamespaceTool; use crate::ResponsesApiTool; -use crate::ToolOrigin; use crate::ToolSearchOutputTool; use crate::ToolSpec; use crate::mcp_tool_to_deferred_responses_api_tool; @@ -349,7 +348,6 @@ pub fn create_tool_suggest_tool(discoverable_tools: &[ToolSuggestEntry]) -> Tool Some(false.into()), ), output_schema: None, - origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/tool_discovery_tests.rs b/codex-rs/tools/src/tool_discovery_tests.rs index 34190e03b143..dbbf56c3cdb8 100644 --- a/codex-rs/tools/src/tool_discovery_tests.rs +++ b/codex-rs/tools/src/tool_discovery_tests.rs @@ -132,7 +132,6 @@ fn create_tool_suggest_tool_uses_plugin_summary_fallback() { "suggest_reason".to_string(), ]), Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) ); } @@ -198,7 +197,6 @@ fn collect_tool_search_output_tools_groups_results_by_namespace() { /*additional_properties*/ None ), output_schema: None, - origin: ToolOrigin::Native, }), ResponsesApiNamespaceTool::Function(ResponsesApiTool { name: "_list_events".to_string(), @@ -211,7 +209,6 @@ fn collect_tool_search_output_tools_groups_results_by_namespace() { /*additional_properties*/ None ), output_schema: None, - origin: ToolOrigin::Native, }), ], }), @@ -229,7 +226,6 @@ fn collect_tool_search_output_tools_groups_results_by_namespace() { /*additional_properties*/ None ), output_schema: None, - origin: ToolOrigin::Native, })], }), ToolSearchOutputTool::Namespace(ResponsesApiNamespace { @@ -282,7 +278,6 @@ fn collect_tool_search_output_tools_falls_back_to_connector_name_description() { /*additional_properties*/ None ), output_schema: None, - origin: ToolOrigin::Native, })], })], ); diff --git a/codex-rs/tools/src/tool_registry_plan.rs b/codex-rs/tools/src/tool_registry_plan.rs index cefc1475f136..72be7843ccea 100644 --- a/codex-rs/tools/src/tool_registry_plan.rs +++ b/codex-rs/tools/src/tool_registry_plan.rs @@ -14,7 +14,7 @@ use crate::ToolsConfig; use crate::ViewImageToolOptions; use crate::WebSearchToolOptions; use crate::collect_code_mode_exec_prompt_tool_definitions; -use crate::collect_tool_search_app_infos; +use crate::collect_tool_search_source_infos; use crate::collect_tool_suggest_entries; use crate::create_apply_patch_freeform_tool; use crate::create_apply_patch_json_tool; diff --git a/codex-rs/tools/src/tool_registry_plan_tests.rs b/codex-rs/tools/src/tool_registry_plan_tests.rs index f7b1bf0bd5d8..3454a2299664 100644 --- a/codex-rs/tools/src/tool_registry_plan_tests.rs +++ b/codex-rs/tools/src/tool_registry_plan_tests.rs @@ -12,8 +12,7 @@ use crate::ResponsesApiWebSearchFilters; use crate::ResponsesApiWebSearchUserLocation; use crate::ToolHandlerSpec; use crate::ToolNamespace; -use crate::ToolOrigin; -use crate::ToolRegistryPlanAppTool; +use crate::ToolRegistryPlanDeferredTool; use crate::ToolsConfigParams; use crate::WaitAgentTimeoutOptions; use crate::mcp_call_tool_result_output_schema; @@ -1146,7 +1145,6 @@ fn test_build_specs_mcp_tools_converted() { strict: false, output_schema: Some(mcp_call_tool_result_output_schema(serde_json::json!({}))), defer_loading: None, - origin: ToolOrigin::Mcp, }) ); } @@ -1610,7 +1608,7 @@ fn code_mode_augments_mcp_tool_descriptions_with_namespaced_sample() { exec tool declaration: ```ts -declare const tools: { mcp__sample__echo(args: { message: string; }): Promise; }; +declare const tools: { mcp__sample__echo(args: { message: string; }): Promise; }; ```"# ); } @@ -1696,7 +1694,7 @@ fn code_mode_preserves_nullable_and_literal_mcp_input_shapes() { assert!(description.contains( r#"exec tool declaration: ```ts -declare const tools: { mcp__sample__fn(args: { open?: Array<{ lineno?: number | null; ref_id: string; }> | null; response_length?: "short" | "medium" | "long"; tagged_list?: Array<{ kind: "tagged"; scope: "one" | "two"; variant: "alpha" | "beta"; }> | null; }): Promise; }; +declare const tools: { mcp__sample__fn(args: { open?: Array<{ lineno?: number | null; ref_id: string; }> | null; response_length?: "short" | "medium" | "long"; tagged_list?: Array<{ kind: "tagged"; scope: "one" | "two"; variant: "alpha" | "beta"; }> | null; }): Promise; }; ```"# )); } @@ -1771,8 +1769,8 @@ fn code_mode_only_exec_description_includes_full_nested_tool_details() { assert!(description.starts_with( "Use `exec/wait` tool to run all other tools, do not attempt to use any other tools directly" )); - assert!(description.contains("### `update_plan` (`update_plan`)")); - assert!(description.contains("### `view_image` (`view_image`)")); + assert!(description.contains("### `update_plan`")); + assert!(description.contains("### `view_image`")); } #[test] @@ -1806,8 +1804,8 @@ fn code_mode_exec_description_omits_nested_tool_details_when_not_code_mode_only( assert!(!description.starts_with( "Use `exec/wait` tool to run all other tools, do not attempt to use any other tools directly" )); - assert!(!description.contains("### `update_plan` (`update_plan`)")); - assert!(!description.contains("### `view_image` (`view_image`)")); + assert!(!description.contains("### `update_plan`")); + assert!(!description.contains("### `view_image`")); } fn model_info() -> ModelInfo { @@ -1921,17 +1919,6 @@ fn mcp_tool(name: &str, description: &str, input_schema: serde_json::Value) -> r } } -fn mcp_tool_with_output_schema( - name: &str, - description: &str, - input_schema: serde_json::Value, - output_schema: serde_json::Value, -) -> rmcp::model::Tool { - let mut tool = mcp_tool(name, description, input_schema); - tool.output_schema = Some(std::sync::Arc::new(rmcp::model::object(output_schema))); - tool -} - #[test] fn code_mode_augments_mcp_tool_descriptions_with_structured_output_sample() { let model_info = model_info(); @@ -1951,38 +1938,39 @@ fn code_mode_augments_mcp_tool_descriptions_with_structured_output_sample() { windows_sandbox_level: WindowsSandboxLevel::Disabled, }); + let mut tool = mcp_tool( + "echo", + "Echo text", + serde_json::json!({ + "type": "object", + "properties": { + "message": {"type": "string"} + }, + "required": ["message"], + "additionalProperties": false + }), + ); + tool.output_schema = Some(std::sync::Arc::new(rmcp::model::object( + serde_json::json!({ + "type": "object", + "properties": { + "echo": {"type": "string"}, + "env": { + "anyOf": [ + {"type": "string"}, + {"type": "null"} + ] + } + }, + "required": ["echo", "env"], + "additionalProperties": false + }), + ))); + let (tools, _) = build_specs( &tools_config, - Some(HashMap::from([( - "mcp__sample__echo".to_string(), - mcp_tool_with_output_schema( - "echo", - "Echo text", - serde_json::json!({ - "type": "object", - "properties": { - "message": {"type": "string"} - }, - "required": ["message"], - "additionalProperties": false - }), - serde_json::json!({ - "type": "object", - "properties": { - "echo": {"type": "string"}, - "env": { - "anyOf": [ - {"type": "string"}, - {"type": "null"} - ] - } - }, - "required": ["echo", "env"], - "additionalProperties": false - }), - ), - )])), - /*app_tools*/ None, + Some(HashMap::from([("mcp__sample__echo".to_string(), tool)])), + /*deferred_mcp_tools*/ None, &[], ); @@ -1998,7 +1986,7 @@ fn code_mode_augments_mcp_tool_descriptions_with_structured_output_sample() { exec tool declaration: ```ts -declare const tools: { mcp__sample__echo(args: { message: string; }): Promise>; }; +declare const tools: { mcp__sample__echo(args: { message: string; }): Promise>; }; ```"# ); } diff --git a/codex-rs/tools/src/tool_spec_tests.rs b/codex-rs/tools/src/tool_spec_tests.rs index 16802f4a036b..c94f54e6b5af 100644 --- a/codex-rs/tools/src/tool_spec_tests.rs +++ b/codex-rs/tools/src/tool_spec_tests.rs @@ -7,7 +7,6 @@ use crate::FreeformTool; use crate::FreeformToolFormat; use crate::JsonSchema; use crate::ResponsesApiTool; -use crate::ToolOrigin; use crate::create_tools_json_for_responses_api; use codex_protocol::config_types::WebSearchContextSize; use codex_protocol::config_types::WebSearchFilters as ConfigWebSearchFilters; @@ -31,7 +30,6 @@ fn tool_spec_name_covers_all_variants() { /*additional_properties*/ None ), output_schema: None, - origin: ToolOrigin::Native, }) .name(), "lookup_order" @@ -98,7 +96,6 @@ fn configured_tool_spec_name_delegates_to_tool_spec() { /*additional_properties*/ None ), output_schema: None, - origin: ToolOrigin::Native, }), /*supports_parallel_tool_calls*/ true, ) @@ -149,7 +146,6 @@ fn create_tools_json_for_responses_api_includes_top_level_name() { /*additional_properties*/ None ), output_schema: None, - origin: ToolOrigin::Native, })]) .expect("serialize tools"), vec![json!({ diff --git a/codex-rs/tools/src/utility_tool.rs b/codex-rs/tools/src/utility_tool.rs index 2882007c7b6f..b0f93c9726f9 100644 --- a/codex-rs/tools/src/utility_tool.rs +++ b/codex-rs/tools/src/utility_tool.rs @@ -1,6 +1,5 @@ use crate::JsonSchema; use crate::ResponsesApiTool; -use crate::ToolOrigin; use crate::ToolSpec; use std::collections::BTreeMap; @@ -37,7 +36,6 @@ pub fn create_list_dir_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["dir_path".to_string()]), Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) } @@ -93,7 +91,6 @@ pub fn create_test_sync_tool() -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, /*required*/ None, Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/utility_tool_tests.rs b/codex-rs/tools/src/utility_tool_tests.rs index 2e45aaba2e90..2984d02f481b 100644 --- a/codex-rs/tools/src/utility_tool_tests.rs +++ b/codex-rs/tools/src/utility_tool_tests.rs @@ -43,7 +43,6 @@ fn list_dir_tool_matches_expected_spec() { ), ]), Some(vec!["dir_path".to_string()]), Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) ); } @@ -104,7 +103,6 @@ fn test_sync_tool_matches_expected_spec() { ), ]), /*required*/ None, Some(false.into())), output_schema: None, - origin: ToolOrigin::Native, }) ); } diff --git a/codex-rs/tools/src/view_image.rs b/codex-rs/tools/src/view_image.rs index a152e5b56863..5d591a339f43 100644 --- a/codex-rs/tools/src/view_image.rs +++ b/codex-rs/tools/src/view_image.rs @@ -1,6 +1,5 @@ use crate::JsonSchema; use crate::ResponsesApiTool; -use crate::ToolOrigin; use crate::ToolSpec; use codex_protocol::models::VIEW_IMAGE_TOOL_NAME; use serde_json::Value; @@ -34,7 +33,6 @@ pub fn create_view_image_tool(options: ViewImageToolOptions) -> ToolSpec { defer_loading: None, parameters: JsonSchema::object(properties, Some(vec!["path".to_string()]), Some(false.into())), output_schema: Some(view_image_output_schema()), - origin: ToolOrigin::Native, }) } diff --git a/codex-rs/tools/src/view_image_tests.rs b/codex-rs/tools/src/view_image_tests.rs index 5edc36d49552..3d6782965756 100644 --- a/codex-rs/tools/src/view_image_tests.rs +++ b/codex-rs/tools/src/view_image_tests.rs @@ -20,7 +20,6 @@ fn view_image_tool_omits_detail_without_original_detail_feature() { JsonSchema::string(Some("Local filesystem path to an image file".to_string()),), )]), Some(vec!["path".to_string()]), Some(false.into())), output_schema: Some(view_image_output_schema()), - origin: ToolOrigin::Native, }) ); } @@ -50,7 +49,6 @@ fn view_image_tool_includes_detail_with_original_detail_feature() { ), ]), Some(vec!["path".to_string()]), Some(false.into())), output_schema: Some(view_image_output_schema()), - origin: ToolOrigin::Native, }) ); }