This commit is contained in:
Matthew Zeng
2026-03-05 23:09:20 -08:00
parent 84f66b3309
commit b699ec52b3
23 changed files with 512 additions and 343 deletions

View File

@@ -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"
},

View File

@@ -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"
},

View File

@@ -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"
},

View File

@@ -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<JsonValue> | { [key in string]?: JsonValue } | null });
export type McpElicitationBooleanSchema = { type: McpElicitationBooleanType, title?: string, description?: string, default?: boolean, };

View File

@@ -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;

View File

@@ -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<JsonValue> | { [key in string]?: JsonValue } | null });

View File

@@ -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";

View File

@@ -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<string>, enumNames?: Array<string>, default?: string, };

View File

@@ -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<JsonValue> | { [key in string]?: JsonValue } | null });
export type McpElicitationNumberSchema = { type: McpElicitationNumberType, title?: string, description?: string, minimum?: number, maximum?: number, default?: number, };

View File

@@ -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";

View File

@@ -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;

View File

@@ -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<string>, description?: string, } & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });
export type McpElicitationSchema = { $schema?: string, type: McpElicitationObjectType, properties: { [key in string]?: McpElicitationPrimitiveSchema }, required?: Array<string>, };

View File

@@ -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<JsonValue> | { [key in string]?: JsonValue } | null });
export type McpElicitationStringSchema = { type: McpElicitationStringType, title?: string, description?: string, minLength?: number, maxLength?: number, format?: McpElicitationStringFormat, default?: string, };

View File

@@ -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<string>, } & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });
export type McpElicitationTitledMultiSelectEnumSchema = { type: McpElicitationArrayType, title?: string, description?: string, minItems?: bigint, maxItems?: bigint, items: McpElicitationTitledEnumItems, default?: Array<string>, };

View File

@@ -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<McpElicitationConstOption>, default?: string, } & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });
export type McpElicitationTitledSingleSelectEnumSchema = { type: McpElicitationStringType, title?: string, description?: string, oneOf: Array<McpElicitationConstOption>, default?: string, };

View File

@@ -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<string>, } & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });
export type McpElicitationUntitledMultiSelectEnumSchema = { type: McpElicitationArrayType, title?: string, description?: string, minItems?: bigint, maxItems?: bigint, items: McpElicitationUntitledEnumItems, default?: Array<string>, };

View File

@@ -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<string>, default?: string, } & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });
export type McpElicitationUntitledSingleSelectEnumSchema = { type: McpElicitationStringType, title?: string, description?: string, enum: Array<string>, default?: string, };

View File

@@ -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, });

View File

@@ -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";

View File

@@ -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 {

View File

@@ -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<String>,
pub properties: BTreeMap<String, McpElicitationPrimitiveSchema>,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub required: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub description: Option<String>,
#[serde(default, flatten)]
pub additional: HashMap<String, JsonValue>,
}
#[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<McpElicitationStringFormat>,
#[serde(default, flatten)]
pub additional: HashMap<String, JsonValue>,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub default: Option<String>,
}
#[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<f64>,
#[serde(default, flatten)]
pub additional: HashMap<String, JsonValue>,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub default: Option<f64>,
}
#[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<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub minimum: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub maximum: Option<i64>,
#[serde(default, flatten)]
pub additional: HashMap<String, JsonValue>,
}
#[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<bool>,
#[serde(default, flatten)]
pub additional: HashMap<String, JsonValue>,
}
#[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<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub description: Option<String>,
#[serde(rename = "enum")]
#[ts(rename = "enum")]
pub enum_: Vec<String>,
#[serde(rename = "enumNames", skip_serializing_if = "Option::is_none")]
#[ts(optional, rename = "enumNames")]
pub enum_names: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub default: Option<String>,
}
#[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<String>,
#[serde(default, flatten)]
pub additional: HashMap<String, JsonValue>,
}
#[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<String>,
#[serde(default, flatten)]
pub additional: HashMap<String, JsonValue>,
}
#[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<Vec<String>>,
#[serde(default, flatten)]
pub additional: HashMap<String, JsonValue>,
}
#[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<Vec<String>>,
#[serde(default, flatten)]
pub additional: HashMap<String, JsonValue>,
}
#[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<JsonValue>,
message: String,
requested_schema: Option<McpElicitationSchema>,
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]

View File

@@ -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<serde_json::Value> {
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",

View File

@@ -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<String, Value>) -> Vec<CodexOptionMetadata> {
additional
.get("x-codex-options")
.cloned()
.and_then(|value| serde_json::from_value::<Vec<RequestUserInputQuestionOption>>(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<Value> {
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");