-
Notifications
You must be signed in to change notification settings - Fork 6.1k
[app-server] remove serde(skip_serializing_if = "Option::is_none") annotations #5939
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
c2ac3f8 to
24a8b03
Compare
02b292f to
534c5d1
Compare
534c5d1 to
c8186a4
Compare
| "params": { | ||
| "pageSize": null, | ||
| "cursor": null | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI app-server can still take this as a valid request:
"method": "model/list",
"id": 6,
"params": {}
Even though this is what the struct serializes to in this test, this is independent from how codex app-server deserializes the incoming request.
| return RustProp(prop_name, serde_str) | ||
|
|
||
| if is_optional and serde_str: | ||
| ts_str = "#[ts(optional)]" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This actually makes our generated MCP protocol types match https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-06-18/schema.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, looks good. I added a couple of questions.
| id: String, | ||
| summary: Vec<ReasoningItemReasoningSummary>, | ||
| #[serde(default, skip_serializing_if = "should_serialize_reasoning_content")] | ||
| #[ts(optional = nullable)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the difference between #[ts(optional = nullable)] and #[ts(optional)]? Just want to make sure this change was intended.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[ts(optional = nullable)] generates field?: T | null
whereas #[ts(optional)] generates field: T | null
| id: Option<String>, | ||
| #[serde(default, skip_serializing_if = "Option::is_none")] | ||
| #[ts(optional = nullable)] | ||
| #[ts(optional)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious why this field gets a #[ts(optional)] but other Option<String> fields in the same struct do not. Just want to make sure that's intended.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh this is a really good question
just checked, it looks like fields marked #[serde(skip_serializing)] will still be rendered in the TS definition, which is wrong... we need to omit those fields. I'll look into this. Good catch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I only added #[ts(optional)] for fields with skip_serializing_if = "Option::is_none")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated so that fields with #[serde(skip_serializing)] also have #[ts(skip)]
We had this annotation everywhere in app-server APIs which made it so that fields get serialized as
field?: T, meaning if the field asNonewe would omit the field in the payload. Removing this annotation changes it so that we returnfield: T | nullinstead, which makes codex app-server's API more aligned with the convention of public OpenAI APIs like Responses.Separately, remove the
#[ts(optional_fields = nullable)]annotations that were recently added which made all the TS types becomefield?: T | nullwhich is not great since clients need to handle undefined and null.I think generally it'll be best to have optional types be either:
field: T | null(preferred, aligned with public OpenAI APIs)field?: Twhere we have to, such as types generated from the MCP schema: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-06-18/schema.ts (see changes tomcp-types/)I updated @etraut-openai's unit test to check that all generated TS types are one or the other, not both (so will error if we have a type that has
field?: T | null). I don't think there's currently a good use case for that - but we can always revisit.