Preserve MCP JSON schema structure in codex-rs#22166
Open
soheil-oai wants to merge 2 commits into
Open
Conversation
Contributor
|
I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
0e9c3bd to
7b19a8c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
JsonSchemabuildersproperties$defs,$ref,allOf, descriptions,additionalProperties, and nested{}property schemasProblem
Follow-up to the schema handling investigation and proposal:
https://www.notion.so/35d8e50b62b0813f8376d5ef25bdda22
The lossy step was
parse_tool_input_schema: incoming MCP/dynamic schemas were forced into Codex's narrow typedJsonSchemarepresentation. That representation is useful for hand-authored Codex tools, but it cannot faithfully model every provider-native schema shape ($ref,$defs,allOf,oneOf, literal{}, metadata siblings, and arbitrary permissive schemas).Before this PR, the sanitizer filled those gaps by inventing types. In practice, unknown or permissive shapes could become
string, so Codex sent Responses a less accurate schema than the connector exposed. The reported cases include nested provider objects such as Outlook-style$refproperties and permissive shapes such as Notionbody.parent: {}.Approach
This PR avoids implementing JSON Schema semantics in Codex. Instead, MCP and dynamic tools now preserve their raw schema payload for serialization, while local/static tools continue using the typed
JsonSchemabuilder API.The raw-schema path only normalizes the root tool-parameter envelope:
typebecomes"object"propertiesbecomes{}That means
strict: false-compatible provider-native constructs such as$defs,$ref,allOf,oneOf,anyOf,additionalProperties, type arrays, descriptions, enums, and{}property schemas are no longer simplified before reaching Responses.Non-Goals
$refexpansion or recursive ref resolution.allOf/oneOf/anyOfinterpretation.The important principle is: if Responses accepts a non-strict schema shape, Codex should not preemptively simplify it.
Validation
cargo test -p codex-toolsjust fmtjust fix -p codex-toolsgit diff --cached --checkBase used for this clean branch:
origin/mainat704ad620f625c92d83da1cc85e99bf15a7fb2f31.Testing Methodology
In addition to the unit tests above, I validated the reported Outlook Calendar failure mode with local instrumentation on this branch:
Added temporary opt-in logging at the MCP boundary to print the raw upstream
rmcp::model::Toolschema and annotations before Codex normalization.Added temporary opt-in logging at the Responses conversion boundary to print the model-visible
ResponsesApiTool.parameterspayload after Codex conversion.Rebuilt the local CLI with that instrumentation and ran an end-to-end Outlook Calendar write through Responses API tool calling.
The CLI was invoked with the Outlook Calendar connector enabled and destructive/open-world tools approved:
The prompt intentionally did not tell the model the
create_eventargument shape. It only described the user task.Confirmed the upstream Outlook Calendar
create_eventtool annotations were:{ "readOnlyHint": false, "destructiveHint": true, "openWorldHint": true }Confirmed the upstream Outlook Calendar
create_eventschema exposesstartandendasEventDateTimeobjects with nesteddateTimeandtimeZonestring properties.Confirmed the model-visible Responses tool schema still exposes
startandendas objects with the same nested properties, rather than collapsing either field tostring.Confirmed the Responses API tool call went through to the Outlook Calendar connector. The model inferred and sent object-shaped arguments without schema-shape hints:
{ "subject": "Codex Outlook schema smoke test (safe to delete)", "start": { "dateTime": "2026-05-11T20:45:00", "timeZone": "Eastern Standard Time" }, "end": { "dateTime": "2026-05-11T21:00:00", "timeZone": "Eastern Standard Time" }, "body_content": "Created by local codex-rs Outlook schema smoke test; safe to delete.", "body_content_type": "Text", "attendees": [], "calendar_id": null }Confirmed the connector completed successfully and created exactly one dummy Outlook Calendar event:
AAMkADFiZDc5OGE0LWY5MmYtNDg0Ny1hZDRiLWRlMDJhOWRjM2Q4MwBGAAAAAADv5C0rm4T0TL1fE7-wwPEFBwDH912JMQjmSrZkxFhjLO2NAAAAAAENAADH912JMQjmSrZkxFhjLO2NAAA3ucCiAAA=https://outlook.office365.com/owa/?itemid=AAMkADFiZDc5OGE0LWY5MmYtNDg0Ny1hZDRiLWRlMDJhOWRjM2Q4MwBGAAAAAADv5C0rm4T0TL1fE7%2FwwPEFBwDH912JMQjmSrZkxFhjLO2NAAAAAAENAADH912JMQjmSrZkxFhjLO2NAAA3ucCiAAA%3D&exvsurl=1&path=/calendar/itemThe earlier cancellation observed during manual testing was not a schema or Responses tool-calling failure. It came from using the wrong config override path (
apps.apps.<connector_id>...) for the Outlook Calendar app. The working path is the flattened app config keyapps.<connector_id>....