fix: default unknown tool schemas to empty schemas (#22380)

## Why

Some tool providers, especially MCP servers and dynamic tool sources,
can supply schema nodes that omit `type` and have no recognized JSON
Schema shape hints. Previously, `sanitize_json_schema` filled those
unknown nodes in as `string`, which made the schema parseable but
invented a scalar constraint that the provider did not specify. For
description-only fields, that could incorrectly steer tool arguments
away from the provider's actual accepted shape.

The Responses API accepts permissive empty schemas such as `{}` at
nested property positions, so Codex should preserve that permissive
meaning instead of coercing unknown schema nodes into a misleading
scalar type.

## What Changed

- Changed the no-hints fallback in `codex-rs/tools/src/json_schema.rs`
to clear unrecognized object schema nodes to `{}`.
- Empty schemas now remain `{}` rather than becoming `type: "string"`.
- Description-only or otherwise metadata-only nested property schemas
now become `{}` while surrounding object/array/string/number inference
still applies when recognized hints are present.
- Updated `codex-tools` and `codex-core` tests to cover top-level empty
schemas, nested empty schemas, metadata-only malformed schemas, dynamic
tools, and MCP tool specs.

## Verification

- `cargo test -p codex-tools`
- `cargo test -p codex-core
test_mcp_tool_property_missing_type_defaults_to_empty_schema`
- Manually verified the real Responses API behavior for both
empty-schema positions:
- Top-level function `parameters: {}` is accepted and echoed back as
`{"type":"object","properties":{}}`; when forced to call the tool,
Responses emitted empty object arguments: `"arguments": "{}"`.
- Nested property schema `{}` is accepted and preserved as `{}`; when
forced to call a tool with `metadata.extra`, Responses emitted
`"arguments": "{\"metadata\":{\"extra\":\"codex schema sanitizer
behavior\"}}"`.
This commit is contained in:
Celia Chen
2026-05-18 12:41:10 -07:00
committed by GitHub
Unverified
parent 10f7dc6eb5
commit 4dbca61e20
4 changed files with 79 additions and 19 deletions
@@ -907,7 +907,7 @@ async fn search_tool_description_falls_back_to_connector_name_without_descriptio
}
#[tokio::test]
async fn test_mcp_tool_property_missing_type_defaults_to_string() {
async fn test_mcp_tool_property_missing_type_defaults_to_empty_schema() {
let config = test_config().await;
let model_info = construct_model_info_offline("gpt-5.4", &config);
let mut features = Features::with_defaults();
@@ -950,10 +950,7 @@ async fn test_mcp_tool_property_missing_type_defaults_to_string() {
name: "search".to_string(),
parameters: JsonSchema::object(
/*properties*/
BTreeMap::from([(
"query".to_string(),
JsonSchema::string(Some("search query".to_string())),
)]),
BTreeMap::from([("query".to_string(), JsonSchema::default())]),
/*required*/ None,
/*additional_properties*/ None
),