diff --git a/codex-rs/app-server-protocol/schema/json/McpServerElicitationRequestParams.json b/codex-rs/app-server-protocol/schema/json/McpServerElicitationRequestParams.json index 07113a366b..aa7fa817ae 100644 --- a/codex-rs/app-server-protocol/schema/json/McpServerElicitationRequestParams.json +++ b/codex-rs/app-server-protocol/schema/json/McpServerElicitationRequestParams.json @@ -8,7 +8,7 @@ "type": "string" }, "McpElicitationBooleanSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "type": [ @@ -44,6 +44,7 @@ "type": "string" }, "McpElicitationConstOption": { + "additionalProperties": false, "properties": { "const": { "type": "string" @@ -65,29 +66,39 @@ }, { "$ref": "#/definitions/McpElicitationMultiSelectEnumSchema" + }, + { + "$ref": "#/definitions/McpElicitationLegacyTitledEnumSchema" } ] }, - "McpElicitationIntegerSchema": { - "additionalProperties": true, + "McpElicitationLegacyTitledEnumSchema": { + "additionalProperties": false, "properties": { + "default": { + "type": [ + "string", + "null" + ] + }, "description": { "type": [ "string", "null" ] }, - "maximum": { - "format": "int64", - "type": [ - "integer", - "null" - ] + "enum": { + "items": { + "type": "string" + }, + "type": "array" }, - "minimum": { - "format": "int64", + "enumNames": { + "items": { + "type": "string" + }, "type": [ - "integer", + "array", "null" ] }, @@ -98,20 +109,15 @@ ] }, "type": { - "$ref": "#/definitions/McpElicitationIntegerType" + "$ref": "#/definitions/McpElicitationStringType" } }, "required": [ + "enum", "type" ], "type": "object" }, - "McpElicitationIntegerType": { - "enum": [ - "integer" - ], - "type": "string" - }, "McpElicitationMultiSelectEnumSchema": { "anyOf": [ { @@ -123,8 +129,15 @@ ] }, "McpElicitationNumberSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { + "default": { + "format": "double", + "type": [ + "number", + "null" + ] + }, "description": { "type": [ "string", @@ -162,7 +175,8 @@ }, "McpElicitationNumberType": { "enum": [ - "number" + "number", + "integer" ], "type": "string" }, @@ -183,17 +197,14 @@ { "$ref": "#/definitions/McpElicitationNumberSchema" }, - { - "$ref": "#/definitions/McpElicitationIntegerSchema" - }, { "$ref": "#/definitions/McpElicitationBooleanSchema" } ] }, "McpElicitationSchema": { - "additionalProperties": true, - "description": "Typed form schema for MCP `elicitation/create` requests.\n\nThis mirrors the `requestedSchema` shape from the MCP `ElicitRequestFormParams` schema while preserving extension keywords such as Codex-specific `x-codex-*` metadata.", + "additionalProperties": false, + "description": "Typed form schema for MCP `elicitation/create` requests.\n\nThis matches the `requestedSchema` shape from the MCP 2025-11-25 `ElicitRequestFormParams` schema.", "properties": { "$schema": { "type": [ @@ -201,12 +212,6 @@ "null" ] }, - "description": { - "type": [ - "string", - "null" - ] - }, "properties": { "additionalProperties": { "$ref": "#/definitions/McpElicitationPrimitiveSchema" @@ -222,12 +227,6 @@ "null" ] }, - "title": { - "type": [ - "string", - "null" - ] - }, "type": { "$ref": "#/definitions/McpElicitationObjectType" } @@ -258,8 +257,14 @@ "type": "string" }, "McpElicitationStringSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { + "default": { + "type": [ + "string", + "null" + ] + }, "description": { "type": [ "string", @@ -314,6 +319,7 @@ "type": "string" }, "McpElicitationTitledEnumItems": { + "additionalProperties": false, "properties": { "anyOf": { "items": { @@ -328,7 +334,7 @@ "type": "object" }, "McpElicitationTitledMultiSelectEnumSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "items": { @@ -381,7 +387,7 @@ "type": "object" }, "McpElicitationTitledSingleSelectEnumSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "type": [ @@ -418,6 +424,7 @@ "type": "object" }, "McpElicitationUntitledEnumItems": { + "additionalProperties": false, "properties": { "enum": { "items": { @@ -436,7 +443,7 @@ "type": "object" }, "McpElicitationUntitledMultiSelectEnumSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "items": { @@ -489,7 +496,7 @@ "type": "object" }, "McpElicitationUntitledSingleSelectEnumSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "type": [ @@ -540,19 +547,13 @@ "type": "string" }, "requestedSchema": { - "anyOf": [ - { - "$ref": "#/definitions/McpElicitationSchema" - }, - { - "type": "null" - } - ] + "$ref": "#/definitions/McpElicitationSchema" } }, "required": [ "message", - "mode" + "mode", + "requestedSchema" ], "type": "object" }, diff --git a/codex-rs/app-server-protocol/schema/json/ServerRequest.json b/codex-rs/app-server-protocol/schema/json/ServerRequest.json index d059b30561..6e637d5665 100644 --- a/codex-rs/app-server-protocol/schema/json/ServerRequest.json +++ b/codex-rs/app-server-protocol/schema/json/ServerRequest.json @@ -659,7 +659,7 @@ "type": "string" }, "McpElicitationBooleanSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "type": [ @@ -695,6 +695,7 @@ "type": "string" }, "McpElicitationConstOption": { + "additionalProperties": false, "properties": { "const": { "type": "string" @@ -716,29 +717,39 @@ }, { "$ref": "#/definitions/McpElicitationMultiSelectEnumSchema" + }, + { + "$ref": "#/definitions/McpElicitationLegacyTitledEnumSchema" } ] }, - "McpElicitationIntegerSchema": { - "additionalProperties": true, + "McpElicitationLegacyTitledEnumSchema": { + "additionalProperties": false, "properties": { + "default": { + "type": [ + "string", + "null" + ] + }, "description": { "type": [ "string", "null" ] }, - "maximum": { - "format": "int64", - "type": [ - "integer", - "null" - ] + "enum": { + "items": { + "type": "string" + }, + "type": "array" }, - "minimum": { - "format": "int64", + "enumNames": { + "items": { + "type": "string" + }, "type": [ - "integer", + "array", "null" ] }, @@ -749,20 +760,15 @@ ] }, "type": { - "$ref": "#/definitions/McpElicitationIntegerType" + "$ref": "#/definitions/McpElicitationStringType" } }, "required": [ + "enum", "type" ], "type": "object" }, - "McpElicitationIntegerType": { - "enum": [ - "integer" - ], - "type": "string" - }, "McpElicitationMultiSelectEnumSchema": { "anyOf": [ { @@ -774,8 +780,15 @@ ] }, "McpElicitationNumberSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { + "default": { + "format": "double", + "type": [ + "number", + "null" + ] + }, "description": { "type": [ "string", @@ -813,7 +826,8 @@ }, "McpElicitationNumberType": { "enum": [ - "number" + "number", + "integer" ], "type": "string" }, @@ -834,17 +848,14 @@ { "$ref": "#/definitions/McpElicitationNumberSchema" }, - { - "$ref": "#/definitions/McpElicitationIntegerSchema" - }, { "$ref": "#/definitions/McpElicitationBooleanSchema" } ] }, "McpElicitationSchema": { - "additionalProperties": true, - "description": "Typed form schema for MCP `elicitation/create` requests.\n\nThis mirrors the `requestedSchema` shape from the MCP `ElicitRequestFormParams` schema while preserving extension keywords such as Codex-specific `x-codex-*` metadata.", + "additionalProperties": false, + "description": "Typed form schema for MCP `elicitation/create` requests.\n\nThis matches the `requestedSchema` shape from the MCP 2025-11-25 `ElicitRequestFormParams` schema.", "properties": { "$schema": { "type": [ @@ -852,12 +863,6 @@ "null" ] }, - "description": { - "type": [ - "string", - "null" - ] - }, "properties": { "additionalProperties": { "$ref": "#/definitions/McpElicitationPrimitiveSchema" @@ -873,12 +878,6 @@ "null" ] }, - "title": { - "type": [ - "string", - "null" - ] - }, "type": { "$ref": "#/definitions/McpElicitationObjectType" } @@ -909,8 +908,14 @@ "type": "string" }, "McpElicitationStringSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { + "default": { + "type": [ + "string", + "null" + ] + }, "description": { "type": [ "string", @@ -965,6 +970,7 @@ "type": "string" }, "McpElicitationTitledEnumItems": { + "additionalProperties": false, "properties": { "anyOf": { "items": { @@ -979,7 +985,7 @@ "type": "object" }, "McpElicitationTitledMultiSelectEnumSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "items": { @@ -1032,7 +1038,7 @@ "type": "object" }, "McpElicitationTitledSingleSelectEnumSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "type": [ @@ -1069,6 +1075,7 @@ "type": "object" }, "McpElicitationUntitledEnumItems": { + "additionalProperties": false, "properties": { "enum": { "items": { @@ -1087,7 +1094,7 @@ "type": "object" }, "McpElicitationUntitledMultiSelectEnumSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "items": { @@ -1140,7 +1147,7 @@ "type": "object" }, "McpElicitationUntitledSingleSelectEnumSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "type": [ @@ -1191,19 +1198,13 @@ "type": "string" }, "requestedSchema": { - "anyOf": [ - { - "$ref": "#/definitions/McpElicitationSchema" - }, - { - "type": "null" - } - ] + "$ref": "#/definitions/McpElicitationSchema" } }, "required": [ "message", - "mode" + "mode", + "requestedSchema" ], "type": "object" }, diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index d8e908572b..e07b502531 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -5305,7 +5305,7 @@ "type": "string" }, "McpElicitationBooleanSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "type": [ @@ -5341,6 +5341,7 @@ "type": "string" }, "McpElicitationConstOption": { + "additionalProperties": false, "properties": { "const": { "type": "string" @@ -5362,29 +5363,39 @@ }, { "$ref": "#/definitions/McpElicitationMultiSelectEnumSchema" + }, + { + "$ref": "#/definitions/McpElicitationLegacyTitledEnumSchema" } ] }, - "McpElicitationIntegerSchema": { - "additionalProperties": true, + "McpElicitationLegacyTitledEnumSchema": { + "additionalProperties": false, "properties": { + "default": { + "type": [ + "string", + "null" + ] + }, "description": { "type": [ "string", "null" ] }, - "maximum": { - "format": "int64", - "type": [ - "integer", - "null" - ] + "enum": { + "items": { + "type": "string" + }, + "type": "array" }, - "minimum": { - "format": "int64", + "enumNames": { + "items": { + "type": "string" + }, "type": [ - "integer", + "array", "null" ] }, @@ -5395,20 +5406,15 @@ ] }, "type": { - "$ref": "#/definitions/McpElicitationIntegerType" + "$ref": "#/definitions/McpElicitationStringType" } }, "required": [ + "enum", "type" ], "type": "object" }, - "McpElicitationIntegerType": { - "enum": [ - "integer" - ], - "type": "string" - }, "McpElicitationMultiSelectEnumSchema": { "anyOf": [ { @@ -5420,8 +5426,15 @@ ] }, "McpElicitationNumberSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { + "default": { + "format": "double", + "type": [ + "number", + "null" + ] + }, "description": { "type": [ "string", @@ -5459,7 +5472,8 @@ }, "McpElicitationNumberType": { "enum": [ - "number" + "number", + "integer" ], "type": "string" }, @@ -5480,17 +5494,14 @@ { "$ref": "#/definitions/McpElicitationNumberSchema" }, - { - "$ref": "#/definitions/McpElicitationIntegerSchema" - }, { "$ref": "#/definitions/McpElicitationBooleanSchema" } ] }, "McpElicitationSchema": { - "additionalProperties": true, - "description": "Typed form schema for MCP `elicitation/create` requests.\n\nThis mirrors the `requestedSchema` shape from the MCP `ElicitRequestFormParams` schema while preserving extension keywords such as Codex-specific `x-codex-*` metadata.", + "additionalProperties": false, + "description": "Typed form schema for MCP `elicitation/create` requests.\n\nThis matches the `requestedSchema` shape from the MCP 2025-11-25 `ElicitRequestFormParams` schema.", "properties": { "$schema": { "type": [ @@ -5498,12 +5509,6 @@ "null" ] }, - "description": { - "type": [ - "string", - "null" - ] - }, "properties": { "additionalProperties": { "$ref": "#/definitions/McpElicitationPrimitiveSchema" @@ -5519,12 +5524,6 @@ "null" ] }, - "title": { - "type": [ - "string", - "null" - ] - }, "type": { "$ref": "#/definitions/McpElicitationObjectType" } @@ -5555,8 +5554,14 @@ "type": "string" }, "McpElicitationStringSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { + "default": { + "type": [ + "string", + "null" + ] + }, "description": { "type": [ "string", @@ -5611,6 +5616,7 @@ "type": "string" }, "McpElicitationTitledEnumItems": { + "additionalProperties": false, "properties": { "anyOf": { "items": { @@ -5625,7 +5631,7 @@ "type": "object" }, "McpElicitationTitledMultiSelectEnumSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "items": { @@ -5678,7 +5684,7 @@ "type": "object" }, "McpElicitationTitledSingleSelectEnumSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "type": [ @@ -5715,6 +5721,7 @@ "type": "object" }, "McpElicitationUntitledEnumItems": { + "additionalProperties": false, "properties": { "enum": { "items": { @@ -5733,7 +5740,7 @@ "type": "object" }, "McpElicitationUntitledMultiSelectEnumSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "items": { @@ -5786,7 +5793,7 @@ "type": "object" }, "McpElicitationUntitledSingleSelectEnumSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { "default": { "type": [ @@ -5866,19 +5873,13 @@ "type": "string" }, "requestedSchema": { - "anyOf": [ - { - "$ref": "#/definitions/McpElicitationSchema" - }, - { - "type": "null" - } - ] + "$ref": "#/definitions/McpElicitationSchema" } }, "required": [ "message", - "mode" + "mode", + "requestedSchema" ], "type": "object" }, diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationBooleanSchema.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationBooleanSchema.ts index ee85bdd057..ae0f4a4976 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationBooleanSchema.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationBooleanSchema.ts @@ -1,7 +1,6 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { JsonValue } from "../serde_json/JsonValue"; import type { McpElicitationBooleanType } from "./McpElicitationBooleanType"; -export type McpElicitationBooleanSchema = { type: McpElicitationBooleanType, title?: string, description?: string, default?: boolean, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); +export type McpElicitationBooleanSchema = { type: McpElicitationBooleanType, title?: string, description?: string, default?: boolean, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationEnumSchema.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationEnumSchema.ts index 7aed51b84a..e9155db4ad 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationEnumSchema.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationEnumSchema.ts @@ -1,7 +1,8 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationLegacyTitledEnumSchema } from "./McpElicitationLegacyTitledEnumSchema"; import type { McpElicitationMultiSelectEnumSchema } from "./McpElicitationMultiSelectEnumSchema"; import type { McpElicitationSingleSelectEnumSchema } from "./McpElicitationSingleSelectEnumSchema"; -export type McpElicitationEnumSchema = McpElicitationSingleSelectEnumSchema | McpElicitationMultiSelectEnumSchema; +export type McpElicitationEnumSchema = McpElicitationSingleSelectEnumSchema | McpElicitationMultiSelectEnumSchema | McpElicitationLegacyTitledEnumSchema; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationIntegerSchema.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationIntegerSchema.ts deleted file mode 100644 index e87b774b3f..0000000000 --- a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationIntegerSchema.ts +++ /dev/null @@ -1,7 +0,0 @@ -// GENERATED CODE! DO NOT MODIFY BY HAND! - -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { JsonValue } from "../serde_json/JsonValue"; -import type { McpElicitationIntegerType } from "./McpElicitationIntegerType"; - -export type McpElicitationIntegerSchema = { type: McpElicitationIntegerType, title?: string, description?: string, minimum?: bigint, maximum?: bigint, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationIntegerType.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationIntegerType.ts deleted file mode 100644 index 139a3b07bb..0000000000 --- a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationIntegerType.ts +++ /dev/null @@ -1,5 +0,0 @@ -// GENERATED CODE! DO NOT MODIFY BY HAND! - -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. - -export type McpElicitationIntegerType = "integer"; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationLegacyTitledEnumSchema.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationLegacyTitledEnumSchema.ts new file mode 100644 index 0000000000..8dcec31746 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationLegacyTitledEnumSchema.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationStringType } from "./McpElicitationStringType"; + +export type McpElicitationLegacyTitledEnumSchema = { type: McpElicitationStringType, title?: string, description?: string, enum: Array, enumNames?: Array, default?: string, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationNumberSchema.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationNumberSchema.ts index d1ce805f09..6628db9215 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationNumberSchema.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationNumberSchema.ts @@ -1,7 +1,6 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { JsonValue } from "../serde_json/JsonValue"; import type { McpElicitationNumberType } from "./McpElicitationNumberType"; -export type McpElicitationNumberSchema = { type: McpElicitationNumberType, title?: string, description?: string, minimum?: number, maximum?: number, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); +export type McpElicitationNumberSchema = { type: McpElicitationNumberType, title?: string, description?: string, minimum?: number, maximum?: number, default?: number, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationNumberType.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationNumberType.ts index 182f06754c..96a9ded760 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationNumberType.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationNumberType.ts @@ -2,4 +2,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type McpElicitationNumberType = "number"; +export type McpElicitationNumberType = "number" | "integer"; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationPrimitiveSchema.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationPrimitiveSchema.ts index a95a7432ce..2828ae5895 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationPrimitiveSchema.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationPrimitiveSchema.ts @@ -3,8 +3,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { McpElicitationBooleanSchema } from "./McpElicitationBooleanSchema"; import type { McpElicitationEnumSchema } from "./McpElicitationEnumSchema"; -import type { McpElicitationIntegerSchema } from "./McpElicitationIntegerSchema"; import type { McpElicitationNumberSchema } from "./McpElicitationNumberSchema"; import type { McpElicitationStringSchema } from "./McpElicitationStringSchema"; -export type McpElicitationPrimitiveSchema = McpElicitationEnumSchema | McpElicitationStringSchema | McpElicitationNumberSchema | McpElicitationIntegerSchema | McpElicitationBooleanSchema; +export type McpElicitationPrimitiveSchema = McpElicitationEnumSchema | McpElicitationStringSchema | McpElicitationNumberSchema | McpElicitationBooleanSchema; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationSchema.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationSchema.ts index fe4f1a0718..1afa533342 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationSchema.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationSchema.ts @@ -1,14 +1,13 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { JsonValue } from "../serde_json/JsonValue"; import type { McpElicitationObjectType } from "./McpElicitationObjectType"; import type { McpElicitationPrimitiveSchema } from "./McpElicitationPrimitiveSchema"; /** * Typed form schema for MCP `elicitation/create` requests. * - * This mirrors the `requestedSchema` shape from the MCP `ElicitRequestFormParams` schema while - * preserving extension keywords such as Codex-specific `x-codex-*` metadata. + * This matches the `requestedSchema` shape from the MCP 2025-11-25 + * `ElicitRequestFormParams` schema. */ -export type McpElicitationSchema = { $schema?: string, type: McpElicitationObjectType, title?: string, properties: { [key in string]?: McpElicitationPrimitiveSchema }, required?: Array, description?: string, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); +export type McpElicitationSchema = { $schema?: string, type: McpElicitationObjectType, properties: { [key in string]?: McpElicitationPrimitiveSchema }, required?: Array, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationStringSchema.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationStringSchema.ts index 865f85cf1b..c2ca1eb83c 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationStringSchema.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationStringSchema.ts @@ -1,8 +1,7 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { JsonValue } from "../serde_json/JsonValue"; import type { McpElicitationStringFormat } from "./McpElicitationStringFormat"; import type { McpElicitationStringType } from "./McpElicitationStringType"; -export type McpElicitationStringSchema = { type: McpElicitationStringType, title?: string, description?: string, minLength?: number, maxLength?: number, format?: McpElicitationStringFormat, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); +export type McpElicitationStringSchema = { type: McpElicitationStringType, title?: string, description?: string, minLength?: number, maxLength?: number, format?: McpElicitationStringFormat, default?: string, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationTitledMultiSelectEnumSchema.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationTitledMultiSelectEnumSchema.ts index b1219c04e6..75274d34e1 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationTitledMultiSelectEnumSchema.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationTitledMultiSelectEnumSchema.ts @@ -1,8 +1,7 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { JsonValue } from "../serde_json/JsonValue"; import type { McpElicitationArrayType } from "./McpElicitationArrayType"; import type { McpElicitationTitledEnumItems } from "./McpElicitationTitledEnumItems"; -export type McpElicitationTitledMultiSelectEnumSchema = { type: McpElicitationArrayType, title?: string, description?: string, minItems?: bigint, maxItems?: bigint, items: McpElicitationTitledEnumItems, default?: Array, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); +export type McpElicitationTitledMultiSelectEnumSchema = { type: McpElicitationArrayType, title?: string, description?: string, minItems?: bigint, maxItems?: bigint, items: McpElicitationTitledEnumItems, default?: Array, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationTitledSingleSelectEnumSchema.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationTitledSingleSelectEnumSchema.ts index e119bf9f07..47b7319141 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationTitledSingleSelectEnumSchema.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationTitledSingleSelectEnumSchema.ts @@ -1,8 +1,7 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { JsonValue } from "../serde_json/JsonValue"; import type { McpElicitationConstOption } from "./McpElicitationConstOption"; import type { McpElicitationStringType } from "./McpElicitationStringType"; -export type McpElicitationTitledSingleSelectEnumSchema = { type: McpElicitationStringType, title?: string, description?: string, oneOf: Array, default?: string, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); +export type McpElicitationTitledSingleSelectEnumSchema = { type: McpElicitationStringType, title?: string, description?: string, oneOf: Array, default?: string, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationUntitledMultiSelectEnumSchema.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationUntitledMultiSelectEnumSchema.ts index 328633ba46..5acf9fee00 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationUntitledMultiSelectEnumSchema.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationUntitledMultiSelectEnumSchema.ts @@ -1,8 +1,7 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { JsonValue } from "../serde_json/JsonValue"; import type { McpElicitationArrayType } from "./McpElicitationArrayType"; import type { McpElicitationUntitledEnumItems } from "./McpElicitationUntitledEnumItems"; -export type McpElicitationUntitledMultiSelectEnumSchema = { type: McpElicitationArrayType, title?: string, description?: string, minItems?: bigint, maxItems?: bigint, items: McpElicitationUntitledEnumItems, default?: Array, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); +export type McpElicitationUntitledMultiSelectEnumSchema = { type: McpElicitationArrayType, title?: string, description?: string, minItems?: bigint, maxItems?: bigint, items: McpElicitationUntitledEnumItems, default?: Array, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationUntitledSingleSelectEnumSchema.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationUntitledSingleSelectEnumSchema.ts index 2a79aeb848..49be545d53 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationUntitledSingleSelectEnumSchema.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/McpElicitationUntitledSingleSelectEnumSchema.ts @@ -1,7 +1,6 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { JsonValue } from "../serde_json/JsonValue"; import type { McpElicitationStringType } from "./McpElicitationStringType"; -export type McpElicitationUntitledSingleSelectEnumSchema = { type: McpElicitationStringType, title?: string, description?: string, enum: Array, default?: string, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); +export type McpElicitationUntitledSingleSelectEnumSchema = { type: McpElicitationStringType, title?: string, description?: string, enum: Array, default?: string, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/McpServerElicitationRequestParams.ts b/codex-rs/app-server-protocol/schema/typescript/v2/McpServerElicitationRequestParams.ts index 474a37b307..13855adc3e 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/McpServerElicitationRequestParams.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/McpServerElicitationRequestParams.ts @@ -13,4 +13,4 @@ export type McpServerElicitationRequestParams = { threadId: string, * context is app-server correlation rather than part of the protocol identity of the * elicitation itself. */ -turnId: string | null, serverName: string, } & ({ "mode": "form", _meta: JsonValue | null, message: string, requestedSchema: McpElicitationSchema | null, } | { "mode": "url", _meta: JsonValue | null, message: string, url: string, elicitationId: string, }); +turnId: string | null, serverName: string, } & ({ "mode": "form", _meta: JsonValue | null, message: string, requestedSchema: McpElicitationSchema, } | { "mode": "url", _meta: JsonValue | null, message: string, url: string, elicitationId: string, }); diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts index 5f1aa17939..abe3674bf3 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts @@ -102,8 +102,7 @@ export type { McpElicitationBooleanSchema } from "./McpElicitationBooleanSchema" export type { McpElicitationBooleanType } from "./McpElicitationBooleanType"; export type { McpElicitationConstOption } from "./McpElicitationConstOption"; export type { McpElicitationEnumSchema } from "./McpElicitationEnumSchema"; -export type { McpElicitationIntegerSchema } from "./McpElicitationIntegerSchema"; -export type { McpElicitationIntegerType } from "./McpElicitationIntegerType"; +export type { McpElicitationLegacyTitledEnumSchema } from "./McpElicitationLegacyTitledEnumSchema"; export type { McpElicitationMultiSelectEnumSchema } from "./McpElicitationMultiSelectEnumSchema"; export type { McpElicitationNumberSchema } from "./McpElicitationNumberSchema"; export type { McpElicitationNumberType } from "./McpElicitationNumberType"; diff --git a/codex-rs/app-server-protocol/src/protocol/common.rs b/codex-rs/app-server-protocol/src/protocol/common.rs index 5aefbe7c52..e6d4f994d4 100644 --- a/codex-rs/app-server-protocol/src/protocol/common.rs +++ b/codex-rs/app-server-protocol/src/protocol/common.rs @@ -1070,7 +1070,7 @@ mod tests { request: v2::McpServerElicitationRequest::Form { meta: None, message: "Allow this request?".to_string(), - requested_schema: Some(requested_schema), + requested_schema, }, }; let request = ServerRequest::McpServerElicitationRequest { diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index 9b1b99321c..2236e0b5be 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -4144,10 +4144,10 @@ pub struct McpServerElicitationRequestParams { /// Typed form schema for MCP `elicitation/create` requests. /// -/// This mirrors the `requestedSchema` shape from the MCP `ElicitRequestFormParams` schema while -/// preserving extension keywords such as Codex-specific `x-codex-*` metadata. +/// This matches the `requestedSchema` shape from the MCP 2025-11-25 +/// `ElicitRequestFormParams` schema. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationSchema { #[serde(rename = "$schema", skip_serializing_if = "Option::is_none")] @@ -4156,18 +4156,10 @@ pub struct McpElicitationSchema { #[serde(rename = "type")] #[ts(rename = "type")] pub type_: McpElicitationObjectType, - #[serde(skip_serializing_if = "Option::is_none")] - #[ts(optional)] - pub title: Option, pub properties: BTreeMap, #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub required: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - #[ts(optional)] - pub description: Option, - #[serde(default, flatten)] - pub additional: HashMap, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] @@ -4184,12 +4176,11 @@ pub enum McpElicitationPrimitiveSchema { Enum(McpElicitationEnumSchema), String(McpElicitationStringSchema), Number(McpElicitationNumberSchema), - Integer(McpElicitationIntegerSchema), Boolean(McpElicitationBooleanSchema), } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationStringSchema { #[serde(rename = "type")] @@ -4210,8 +4201,9 @@ pub struct McpElicitationStringSchema { #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub format: Option, - #[serde(default, flatten)] - pub additional: HashMap, + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub default: Option, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] @@ -4232,7 +4224,7 @@ pub enum McpElicitationStringFormat { } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationNumberSchema { #[serde(rename = "type")] @@ -4250,8 +4242,9 @@ pub struct McpElicitationNumberSchema { #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub maximum: Option, - #[serde(default, flatten)] - pub additional: HashMap, + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub default: Option, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] @@ -4259,40 +4252,11 @@ pub struct McpElicitationNumberSchema { #[ts(export_to = "v2/")] pub enum McpElicitationNumberType { Number, -} - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] -#[serde(rename_all = "camelCase")] -#[ts(export_to = "v2/")] -pub struct McpElicitationIntegerSchema { - #[serde(rename = "type")] - #[ts(rename = "type")] - pub type_: McpElicitationIntegerType, - #[serde(skip_serializing_if = "Option::is_none")] - #[ts(optional)] - pub title: Option, - #[serde(skip_serializing_if = "Option::is_none")] - #[ts(optional)] - pub description: Option, - #[serde(skip_serializing_if = "Option::is_none")] - #[ts(optional)] - pub minimum: Option, - #[serde(skip_serializing_if = "Option::is_none")] - #[ts(optional)] - pub maximum: Option, - #[serde(default, flatten)] - pub additional: HashMap, -} - -#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] -#[serde(rename_all = "lowercase")] -#[ts(export_to = "v2/")] -pub enum McpElicitationIntegerType { Integer, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationBooleanSchema { #[serde(rename = "type")] @@ -4307,8 +4271,6 @@ pub struct McpElicitationBooleanSchema { #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub default: Option, - #[serde(default, flatten)] - pub additional: HashMap, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] @@ -4324,6 +4286,31 @@ pub enum McpElicitationBooleanType { pub enum McpElicitationEnumSchema { SingleSelect(McpElicitationSingleSelectEnumSchema), MultiSelect(McpElicitationMultiSelectEnumSchema), + Legacy(McpElicitationLegacyTitledEnumSchema), +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +#[ts(export_to = "v2/")] +pub struct McpElicitationLegacyTitledEnumSchema { + #[serde(rename = "type")] + #[ts(rename = "type")] + pub type_: McpElicitationStringType, + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub title: Option, + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub description: Option, + #[serde(rename = "enum")] + #[ts(rename = "enum")] + pub enum_: Vec, + #[serde(rename = "enumNames", skip_serializing_if = "Option::is_none")] + #[ts(optional, rename = "enumNames")] + pub enum_names: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub default: Option, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] @@ -4335,7 +4322,7 @@ pub enum McpElicitationSingleSelectEnumSchema { } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationUntitledSingleSelectEnumSchema { #[serde(rename = "type")] @@ -4353,12 +4340,10 @@ pub struct McpElicitationUntitledSingleSelectEnumSchema { #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub default: Option, - #[serde(default, flatten)] - pub additional: HashMap, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationTitledSingleSelectEnumSchema { #[serde(rename = "type")] @@ -4376,8 +4361,6 @@ pub struct McpElicitationTitledSingleSelectEnumSchema { #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub default: Option, - #[serde(default, flatten)] - pub additional: HashMap, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] @@ -4389,7 +4372,7 @@ pub enum McpElicitationMultiSelectEnumSchema { } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationUntitledMultiSelectEnumSchema { #[serde(rename = "type")] @@ -4411,12 +4394,10 @@ pub struct McpElicitationUntitledMultiSelectEnumSchema { #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub default: Option>, - #[serde(default, flatten)] - pub additional: HashMap, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationTitledMultiSelectEnumSchema { #[serde(rename = "type")] @@ -4438,8 +4419,6 @@ pub struct McpElicitationTitledMultiSelectEnumSchema { #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub default: Option>, - #[serde(default, flatten)] - pub additional: HashMap, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] @@ -4450,6 +4429,7 @@ pub enum McpElicitationArrayType { } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] +#[serde(deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationUntitledEnumItems { #[serde(rename = "type")] @@ -4461,6 +4441,7 @@ pub struct McpElicitationUntitledEnumItems { } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] +#[serde(deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationTitledEnumItems { #[serde(rename = "anyOf", alias = "oneOf")] @@ -4469,6 +4450,7 @@ pub struct McpElicitationTitledEnumItems { } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] +#[serde(deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationConstOption { #[serde(rename = "const")] @@ -4489,7 +4471,7 @@ pub enum McpServerElicitationRequest { #[ts(rename = "_meta")] meta: Option, message: String, - requested_schema: Option, + requested_schema: McpElicitationSchema, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] @@ -4982,32 +4964,119 @@ mod tests { McpServerElicitationRequest::Form { meta: None, message: "Allow this request?".to_string(), - requested_schema: Some(expected_schema), + requested_schema: expected_schema, } ); } #[test] - fn mcp_server_elicitation_request_accepts_null_core_form_schema() { - let request = McpServerElicitationRequest::try_from(CoreElicitationRequest::Form { + fn mcp_elicitation_schema_matches_mcp_2025_11_25_primitives() { + let schema: McpElicitationSchema = serde_json::from_value(json!({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "email": { + "type": "string", + "title": "Email", + "description": "Work email address", + "format": "email", + "default": "dev@example.com", + }, + "count": { + "type": "integer", + "title": "Count", + "description": "How many items to create", + "minimum": 1, + "maximum": 5, + "default": 3, + }, + "confirmed": { + "type": "boolean", + "title": "Confirm", + "description": "Approve the pending action", + "default": true, + }, + "legacyChoice": { + "type": "string", + "title": "Action", + "description": "Legacy titled enum form", + "enum": ["allow", "deny"], + "enumNames": ["Allow", "Deny"], + "default": "allow", + }, + }, + "required": ["email", "confirmed"], + })) + .expect("schema should deserialize"); + + assert_eq!( + schema, + McpElicitationSchema { + schema_uri: Some("https://json-schema.org/draft/2020-12/schema".to_string()), + type_: McpElicitationObjectType::Object, + properties: BTreeMap::from([ + ( + "confirmed".to_string(), + McpElicitationPrimitiveSchema::Boolean(McpElicitationBooleanSchema { + type_: McpElicitationBooleanType::Boolean, + title: Some("Confirm".to_string()), + description: Some("Approve the pending action".to_string()), + default: Some(true), + }), + ), + ( + "count".to_string(), + McpElicitationPrimitiveSchema::Number(McpElicitationNumberSchema { + type_: McpElicitationNumberType::Integer, + title: Some("Count".to_string()), + description: Some("How many items to create".to_string()), + minimum: Some(1.0), + maximum: Some(5.0), + default: Some(3.0), + }), + ), + ( + "email".to_string(), + McpElicitationPrimitiveSchema::String(McpElicitationStringSchema { + type_: McpElicitationStringType::String, + title: Some("Email".to_string()), + description: Some("Work email address".to_string()), + min_length: None, + max_length: None, + format: Some(McpElicitationStringFormat::Email), + default: Some("dev@example.com".to_string()), + }), + ), + ( + "legacyChoice".to_string(), + McpElicitationPrimitiveSchema::Enum(McpElicitationEnumSchema::Legacy( + McpElicitationLegacyTitledEnumSchema { + type_: McpElicitationStringType::String, + title: Some("Action".to_string()), + description: Some("Legacy titled enum form".to_string()), + enum_: vec!["allow".to_string(), "deny".to_string()], + enum_names: Some(vec!["Allow".to_string(), "Deny".to_string(),]), + default: Some("allow".to_string()), + }, + )), + ), + ]), + required: Some(vec!["email".to_string(), "confirmed".to_string()]), + } + ); + } + + #[test] + fn mcp_server_elicitation_request_rejects_null_core_form_schema() { + let result = McpServerElicitationRequest::try_from(CoreElicitationRequest::Form { meta: Some(json!({ "persist": "session", })), message: "Allow this request?".to_string(), requested_schema: JsonValue::Null, - }) - .expect("null form schema should convert"); + }); - assert_eq!( - request, - McpServerElicitationRequest::Form { - meta: Some(json!({ - "persist": "session", - })), - message: "Allow this request?".to_string(), - requested_schema: None, - } - ); + assert!(result.is_err()); } #[test] diff --git a/codex-rs/core/src/mcp_tool_call.rs b/codex-rs/core/src/mcp_tool_call.rs index 0d753adcf4..0aeafc55d4 100644 --- a/codex-rs/core/src/mcp_tool_call.rs +++ b/codex-rs/core/src/mcp_tool_call.rs @@ -1,6 +1,9 @@ +use std::collections::BTreeMap; use std::time::Duration; use std::time::Instant; +use codex_app_server_protocol::McpElicitationObjectType; +use codex_app_server_protocol::McpElicitationSchema; use codex_app_server_protocol::McpServerElicitationRequest; use codex_app_server_protocol::McpServerElicitationRequestParams; use tracing::error; @@ -351,6 +354,8 @@ const MCP_TOOL_APPROVAL_ACCEPT: &str = "Approve Once"; const MCP_TOOL_APPROVAL_ACCEPT_AND_REMEMBER: &str = "Approve this Session"; const MCP_TOOL_APPROVAL_DECLINE: &str = "Deny"; const MCP_TOOL_APPROVAL_CANCEL: &str = "Cancel"; +const MCP_TOOL_APPROVAL_KIND_KEY: &str = "codex_approval_kind"; +const MCP_TOOL_APPROVAL_KIND_MCP_TOOL_CALL: &str = "mcp_tool_call"; const MCP_TOOL_APPROVAL_PERSIST_KEY: &str = "persist"; const MCP_TOOL_APPROVAL_PERSIST_SESSION: &str = "session"; const MCP_TOOL_APPROVAL_SOURCE_KEY: &str = "source"; @@ -648,7 +653,12 @@ fn build_mcp_tool_approval_elicitation_request( allow_session_persist, ), message, - requested_schema: None, + requested_schema: McpElicitationSchema { + schema_uri: None, + type_: McpElicitationObjectType::Object, + properties: BTreeMap::new(), + required: None, + }, }, } } @@ -660,6 +670,10 @@ fn build_mcp_tool_approval_elicitation_meta( allow_session_persist: bool, ) -> Option { let mut meta = serde_json::Map::new(); + meta.insert( + MCP_TOOL_APPROVAL_KIND_KEY.to_string(), + serde_json::Value::String(MCP_TOOL_APPROVAL_KIND_MCP_TOOL_CALL.to_string()), + ); if allow_session_persist { meta.insert( MCP_TOOL_APPROVAL_PERSIST_KEY.to_string(), @@ -1053,6 +1067,16 @@ mod tests { ); } + #[test] + fn approval_elicitation_meta_marks_tool_approvals() { + assert_eq!( + build_mcp_tool_approval_elicitation_meta("custom_server", None, None, false), + Some(serde_json::json!({ + MCP_TOOL_APPROVAL_KIND_KEY: MCP_TOOL_APPROVAL_KIND_MCP_TOOL_CALL, + })) + ); + } + #[test] fn approval_elicitation_meta_keeps_session_persist_behavior() { assert_eq!( @@ -1069,6 +1093,7 @@ mod tests { true, ), Some(serde_json::json!({ + MCP_TOOL_APPROVAL_KIND_KEY: MCP_TOOL_APPROVAL_KIND_MCP_TOOL_CALL, MCP_TOOL_APPROVAL_PERSIST_KEY: MCP_TOOL_APPROVAL_PERSIST_SESSION, MCP_TOOL_APPROVAL_TOOL_TITLE_KEY: "Run Action", MCP_TOOL_APPROVAL_TOOL_DESCRIPTION_KEY: "Runs the selected action.", @@ -1097,6 +1122,7 @@ mod tests { false, ), Some(serde_json::json!({ + MCP_TOOL_APPROVAL_KIND_KEY: MCP_TOOL_APPROVAL_KIND_MCP_TOOL_CALL, MCP_TOOL_APPROVAL_SOURCE_KEY: MCP_TOOL_APPROVAL_SOURCE_CONNECTOR, MCP_TOOL_APPROVAL_CONNECTOR_ID_KEY: "calendar", MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY: "Calendar", @@ -1128,6 +1154,7 @@ mod tests { true, ), Some(serde_json::json!({ + MCP_TOOL_APPROVAL_KIND_KEY: MCP_TOOL_APPROVAL_KIND_MCP_TOOL_CALL, MCP_TOOL_APPROVAL_PERSIST_KEY: MCP_TOOL_APPROVAL_PERSIST_SESSION, MCP_TOOL_APPROVAL_SOURCE_KEY: MCP_TOOL_APPROVAL_SOURCE_CONNECTOR, MCP_TOOL_APPROVAL_CONNECTOR_ID_KEY: "calendar", diff --git a/codex-rs/tui/src/bottom_pane/mcp_server_elicitation.rs b/codex-rs/tui/src/bottom_pane/mcp_server_elicitation.rs index bf92327a93..341cc1f839 100644 --- a/codex-rs/tui/src/bottom_pane/mcp_server_elicitation.rs +++ b/codex-rs/tui/src/bottom_pane/mcp_server_elicitation.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::collections::HashSet; use std::collections::VecDeque; use std::path::PathBuf; @@ -12,7 +11,6 @@ use codex_protocol::approvals::ElicitationRequest; use codex_protocol::approvals::ElicitationRequestEvent; use codex_protocol::mcp::RequestId as McpRequestId; use codex_protocol::protocol::Op; -use codex_protocol::request_user_input::RequestUserInputQuestionOption; use codex_protocol::user_input::TextElement; use crossterm::event::KeyCode; use crossterm::event::KeyEvent; @@ -53,6 +51,8 @@ const APPROVAL_ACCEPT_ONCE_VALUE: &str = "accept"; const APPROVAL_ACCEPT_SESSION_VALUE: &str = "accept_session"; const APPROVAL_DECLINE_VALUE: &str = "decline"; const APPROVAL_CANCEL_VALUE: &str = "cancel"; +const APPROVAL_META_KIND_KEY: &str = "codex_approval_kind"; +const APPROVAL_META_KIND_MCP_TOOL_CALL: &str = "mcp_tool_call"; const APPROVAL_PERSIST_KEY: &str = "persist"; const APPROVAL_PERSIST_SESSION_VALUE: &str = "session"; @@ -132,12 +132,6 @@ struct McpServerElicitationAnswerState { answer_committed: bool, } -#[derive(Clone, Debug, PartialEq, Eq)] -struct CodexOptionMetadata { - label: String, - description: String, -} - #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct FooterTip { text: String, @@ -174,7 +168,22 @@ impl McpServerElicitationFormRequest { return None; }; - let (response_mode, fields) = if requested_schema.is_null() { + let is_tool_approval = meta + .as_ref() + .and_then(Value::as_object) + .and_then(|meta| meta.get(APPROVAL_META_KIND_KEY)) + .and_then(Value::as_str) + == Some(APPROVAL_META_KIND_MCP_TOOL_CALL); + let is_empty_object_schema = requested_schema.as_object().is_some_and(|schema| { + schema.get("type").and_then(Value::as_str) == Some("object") + && schema + .get("properties") + .and_then(Value::as_object) + .is_some_and(|properties| properties.is_empty()) + }); + + let (response_mode, fields) = + if requested_schema.is_null() || (is_tool_approval && is_empty_object_schema) { let mut options = vec![McpServerElicitationOption { label: "Approve Once".to_string(), description: Some("Run the tool and continue.".to_string()), @@ -279,37 +288,20 @@ fn parse_field( label, prompt, required, - input: McpServerElicitationFieldInput::Text { - secret: schema - .additional - .get("writeOnly") - .and_then(Value::as_bool) - .unwrap_or(false), - }, + input: McpServerElicitationFieldInput::Text { secret: false }, }) } McpElicitationPrimitiveSchema::Boolean(schema) => { let label = schema.title.unwrap_or_else(|| id.to_string()); let prompt = schema.description.unwrap_or_else(|| label.clone()); let default_idx = schema.default.map(|value| if value { 0 } else { 1 }); - let metadata = codex_option_metadata(&schema.additional); let options = [true, false] .into_iter() - .enumerate() - .map(|(idx, value)| { - let metadata = metadata.get(idx); + .map(|value| { + let label = if value { "True" } else { "False" }.to_string(); McpServerElicitationOption { - label: metadata.map_or_else( - || { - if value { - "True".to_string() - } else { - "False".to_string() - } - }, - |entry| entry.label.clone(), - ), - description: metadata.map(|entry| entry.description.clone()), + label, + description: None, value: Value::Bool(value), } }) @@ -325,11 +317,42 @@ fn parse_field( }, }) } + McpElicitationPrimitiveSchema::Enum(McpElicitationEnumSchema::Legacy(schema)) => { + let label = schema.title.unwrap_or_else(|| id.to_string()); + let prompt = schema.description.unwrap_or_else(|| label.clone()); + let default_idx = schema + .default + .as_ref() + .and_then(|value| schema.enum_.iter().position(|entry| entry == value)); + let enum_names = schema.enum_names.unwrap_or_default(); + let options = schema + .enum_ + .into_iter() + .enumerate() + .map(|(idx, value)| McpServerElicitationOption { + label: enum_names + .get(idx) + .cloned() + .unwrap_or_else(|| value.clone()), + description: None, + value: Value::String(value), + }) + .collect(); + Some(McpServerElicitationField { + id: id.to_string(), + label, + prompt, + required, + input: McpServerElicitationFieldInput::Select { + options, + default_idx, + }, + }) + } McpElicitationPrimitiveSchema::Enum(McpElicitationEnumSchema::SingleSelect(schema)) => { parse_single_select_field(id, schema, required) } McpElicitationPrimitiveSchema::Number(_) - | McpElicitationPrimitiveSchema::Integer(_) | McpElicitationPrimitiveSchema::Enum(McpElicitationEnumSchema::MultiSelect(_)) => None, } } @@ -343,7 +366,6 @@ fn parse_single_select_field( McpElicitationSingleSelectEnumSchema::Untitled(schema) => { let label = schema.title.unwrap_or_else(|| id.to_string()); let prompt = schema.description.unwrap_or_else(|| label.clone()); - let metadata = codex_option_metadata(&schema.additional); let default_idx = schema .default .as_ref() @@ -351,14 +373,10 @@ fn parse_single_select_field( let options = schema .enum_ .into_iter() - .enumerate() - .map(|(idx, value)| { - let metadata = metadata.get(idx); - McpServerElicitationOption { - label: metadata.map_or_else(|| value.clone(), |entry| entry.label.clone()), - description: metadata.map(|entry| entry.description.clone()), - value: Value::String(value), - } + .map(|value| McpServerElicitationOption { + label: value.clone(), + description: None, + value: Value::String(value), }) .collect(); Some(McpServerElicitationField { @@ -404,23 +422,6 @@ fn parse_single_select_field( } } -fn codex_option_metadata(additional: &HashMap) -> Vec { - additional - .get("x-codex-options") - .cloned() - .and_then(|value| serde_json::from_value::>(value).ok()) - .map(|options| { - options - .into_iter() - .map(|option| CodexOptionMetadata { - label: option.label, - description: option.description, - }) - .collect() - }) - .unwrap_or_default() -} - pub(crate) struct McpServerElicitationOverlay { app_event_tx: AppEventSender, request: McpServerElicitationFormRequest, @@ -1406,6 +1407,27 @@ mod tests { } } + fn empty_object_schema() -> Value { + serde_json::json!({ + "type": "object", + "properties": {}, + }) + } + + fn tool_approval_meta(include_session_persist: bool) -> Option { + let mut meta = serde_json::Map::from_iter([( + APPROVAL_META_KIND_KEY.to_string(), + Value::String(APPROVAL_META_KIND_MCP_TOOL_CALL.to_string()), + )]); + if include_session_persist { + meta.insert( + APPROVAL_PERSIST_KEY.to_string(), + Value::String(APPROVAL_PERSIST_SESSION_VALUE.to_string()), + ); + } + Some(Value::Object(meta)) + } + fn snapshot_buffer(buf: &Buffer) -> String { let mut lines = Vec::new(); for y in 0..buf.area().height { @@ -1551,6 +1573,69 @@ mod tests { ); } + #[test] + fn empty_tool_approval_schema_uses_approval_actions() { + let thread_id = ThreadId::default(); + let request = McpServerElicitationFormRequest::from_event( + thread_id, + form_request( + "Allow this request?", + empty_object_schema(), + tool_approval_meta(false), + ), + ) + .expect("expected approval fallback"); + + assert_eq!( + request, + McpServerElicitationFormRequest { + thread_id, + server_name: "server-1".to_string(), + request_id: McpRequestId::String("request-1".to_string()), + message: "Allow this request?".to_string(), + response_mode: McpServerElicitationResponseMode::ApprovalAction, + fields: vec![McpServerElicitationField { + id: APPROVAL_FIELD_ID.to_string(), + label: String::new(), + prompt: String::new(), + required: true, + input: McpServerElicitationFieldInput::Select { + options: vec![ + McpServerElicitationOption { + label: "Approve Once".to_string(), + description: Some("Run the tool and continue.".to_string()), + value: Value::String(APPROVAL_ACCEPT_ONCE_VALUE.to_string()), + }, + McpServerElicitationOption { + label: "Deny".to_string(), + description: Some( + "Decline this tool call and continue.".to_string(), + ), + value: Value::String(APPROVAL_DECLINE_VALUE.to_string()), + }, + McpServerElicitationOption { + label: "Cancel".to_string(), + description: Some("Cancel this tool call".to_string()), + value: Value::String(APPROVAL_CANCEL_VALUE.to_string()), + }, + ], + default_idx: Some(0), + }, + }], + } + ); + } + + #[test] + fn empty_unmarked_schema_falls_back() { + let request = McpServerElicitationFormRequest::from_event( + ThreadId::default(), + form_request("Empty form", empty_object_schema(), None), + ); + + assert_eq!(request, None); + } + #[test] fn submit_sends_accept_with_typed_content() { let (tx, mut rx) = test_sender(); @@ -1603,17 +1688,15 @@ mod tests { } #[test] - fn missing_schema_session_choice_sets_persist_meta() { + fn empty_tool_approval_schema_session_choice_sets_persist_meta() { let (tx, mut rx) = test_sender(); let thread_id = ThreadId::default(); let request = McpServerElicitationFormRequest::from_event( thread_id, form_request( "Allow this request?", - Value::Null, - Some(serde_json::json!({ - APPROVAL_PERSIST_KEY: APPROVAL_PERSIST_SESSION_VALUE, - })), + empty_object_schema(), + tool_approval_meta(true), ), ) .expect("expected approval fallback"); @@ -1796,11 +1879,15 @@ mod tests { } #[test] - fn approval_form_without_schema_snapshot() { + fn approval_form_tool_approval_snapshot() { let (tx, _rx) = test_sender(); let request = McpServerElicitationFormRequest::from_event( ThreadId::default(), - form_request("Allow this request?", Value::Null, None), + form_request( + "Allow this request?", + empty_object_schema(), + tool_approval_meta(false), + ), ) .expect("expected approval fallback"); let overlay = McpServerElicitationOverlay::new(request, tx, true, false, false); @@ -1812,16 +1899,14 @@ mod tests { } #[test] - fn approval_form_with_session_persist_snapshot() { + fn approval_form_tool_approval_with_session_persist_snapshot() { let (tx, _rx) = test_sender(); let request = McpServerElicitationFormRequest::from_event( ThreadId::default(), form_request( "Allow this request?", - Value::Null, - Some(serde_json::json!({ - APPROVAL_PERSIST_KEY: APPROVAL_PERSIST_SESSION_VALUE, - })), + empty_object_schema(), + tool_approval_meta(true), ), ) .expect("expected approval fallback");