use super::shared::v2_enum_from_core; use codex_protocol::approvals::ElicitationRequest as CoreElicitationRequest; use codex_protocol::items::McpToolCallError as CoreMcpToolCallError; use codex_protocol::mcp::CallToolResult as CoreMcpCallToolResult; use codex_protocol::mcp::McpServerInfo; use codex_protocol::mcp::Resource as McpResource; pub use codex_protocol::mcp::ResourceContent as McpResourceContent; use codex_protocol::mcp::ResourceTemplate as McpResourceTemplate; use codex_protocol::mcp::Tool as McpTool; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; use serde_json::Value as JsonValue; use std::collections::BTreeMap; use ts_rs::TS; v2_enum_from_core!( pub enum McpAuthStatus from codex_protocol::protocol::McpAuthStatus { Unsupported, NotLoggedIn, BearerToken, OAuth } ); #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct ListMcpServerStatusParams { /// Opaque pagination cursor returned by a previous call. #[ts(optional = nullable)] pub cursor: Option, /// Optional page size; defaults to a server-defined value. #[ts(optional = nullable)] pub limit: Option, /// Controls how much MCP inventory data to fetch for each server. /// Defaults to `Full` when omitted. #[ts(optional = nullable)] pub detail: Option, #[ts(optional = nullable)] pub thread_id: Option, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase", export_to = "v2/")] pub enum McpServerStatusDetail { Full, ToolsAndAuthOnly, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpServerStatus { pub name: String, pub server_info: Option, pub tools: std::collections::HashMap, pub resources: Vec, pub resource_templates: Vec, pub auth_status: McpAuthStatus, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct ListMcpServerStatusResponse { pub data: Vec, /// Opaque cursor to pass to the next call to continue after the last item. /// If None, there are no more items to return. pub next_cursor: Option, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpResourceReadParams { #[ts(optional = nullable)] pub thread_id: Option, pub server: String, pub uri: String, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpResourceReadResponse { pub contents: Vec, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpServerToolCallParams { pub thread_id: String, pub server: String, pub tool: String, #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional)] pub arguments: Option, #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")] #[ts(optional)] pub meta: Option, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpServerToolCallResponse { pub content: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional)] pub structured_content: Option, #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional)] pub is_error: Option, #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")] #[ts(optional)] pub meta: Option, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpToolCallResult { // NOTE: `rmcp::model::Content` (and its `RawContent` variants) would be a more precise Rust // representation of MCP content blocks. We intentionally use `serde_json::Value` here because // this crate exports JSON schema + TS types (`schemars`/`ts-rs`), and the rmcp model types // aren't set up to be schema/TS friendly (and would introduce heavier coupling to rmcp's Rust // representations). Using `JsonValue` keeps the payload wire-shaped and easy to export. pub content: Vec, pub structured_content: Option, #[serde(rename = "_meta")] #[ts(rename = "_meta")] pub meta: Option, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpToolCallError { pub message: String, } impl From for McpServerToolCallResponse { fn from(result: CoreMcpCallToolResult) -> Self { Self { content: result.content, structured_content: result.structured_content, is_error: result.is_error, meta: result.meta, } } } impl From for McpToolCallResult { fn from(result: CoreMcpCallToolResult) -> Self { Self { content: result.content, structured_content: result.structured_content, meta: result.meta, } } } impl From for McpToolCallError { fn from(error: CoreMcpToolCallError) -> Self { Self { message: error.message, } } } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpServerRefreshParams {} #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpServerRefreshResponse {} #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpServerOauthLoginParams { pub name: String, #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional = nullable)] pub scopes: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional = nullable)] pub timeout_secs: Option, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpServerOauthLoginResponse { pub authorization_url: String, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpToolCallProgressNotification { pub thread_id: String, pub turn_id: String, pub item_id: String, pub message: String, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpServerOauthLoginCompletedNotification { pub name: String, pub success: bool, #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional)] pub error: Option, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub enum McpServerStartupState { Starting, Ready, Failed, Cancelled, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpServerStatusUpdatedNotification { pub thread_id: Option, pub name: String, pub status: McpServerStartupState, pub error: Option, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub enum McpServerElicitationAction { Accept, Decline, Cancel, } impl McpServerElicitationAction { pub fn to_core(self) -> codex_protocol::approvals::ElicitationAction { match self { Self::Accept => codex_protocol::approvals::ElicitationAction::Accept, Self::Decline => codex_protocol::approvals::ElicitationAction::Decline, Self::Cancel => codex_protocol::approvals::ElicitationAction::Cancel, } } } impl From for rmcp::model::ElicitationAction { fn from(value: McpServerElicitationAction) -> Self { match value { McpServerElicitationAction::Accept => Self::Accept, McpServerElicitationAction::Decline => Self::Decline, McpServerElicitationAction::Cancel => Self::Cancel, } } } impl From for McpServerElicitationAction { fn from(value: rmcp::model::ElicitationAction) -> Self { match value { rmcp::model::ElicitationAction::Accept => Self::Accept, rmcp::model::ElicitationAction::Decline => Self::Decline, rmcp::model::ElicitationAction::Cancel => Self::Cancel, } } } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpServerElicitationRequestParams { pub thread_id: String, /// Active Codex turn when this elicitation was observed, if app-server could correlate one. /// /// This is nullable because MCP models elicitation as a standalone server-to-client request /// identified by the MCP server request id. It may be triggered during a turn, but turn /// context is app-server correlation rather than part of the protocol identity of the /// elicitation itself. pub turn_id: Option, pub server_name: String, #[serde(flatten)] pub request: McpServerElicitationRequest, // TODO: When core can correlate an elicitation with an MCP tool call, expose the associated // McpToolCall item id here as an optional field. The current core event does not carry that // association. } /// Typed form schema for MCP `elicitation/create` requests. /// /// 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", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationSchema { #[serde(rename = "$schema", skip_serializing_if = "Option::is_none")] #[ts(optional, rename = "$schema")] pub schema_uri: Option, #[serde(rename = "type")] #[ts(rename = "type")] pub type_: McpElicitationObjectType, pub properties: BTreeMap, #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub required: Option>, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "lowercase")] #[ts(export_to = "v2/")] pub enum McpElicitationObjectType { Object, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(untagged)] #[ts(export_to = "v2/")] pub enum McpElicitationPrimitiveSchema { Enum(McpElicitationEnumSchema), String(McpElicitationStringSchema), Number(McpElicitationNumberSchema), Boolean(McpElicitationBooleanSchema), } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationStringSchema { #[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(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub min_length: Option, #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub max_length: Option, #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub format: Option, #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub default: Option, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "lowercase")] #[ts(export_to = "v2/")] pub enum McpElicitationStringType { String, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "kebab-case")] #[ts(rename_all = "kebab-case", export_to = "v2/")] pub enum McpElicitationStringFormat { Email, Uri, Date, DateTime, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationNumberSchema { #[serde(rename = "type")] #[ts(rename = "type")] pub type_: McpElicitationNumberType, #[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(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub default: Option, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "lowercase")] #[ts(export_to = "v2/")] pub enum McpElicitationNumberType { Number, Integer, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationBooleanSchema { #[serde(rename = "type")] #[ts(rename = "type")] pub type_: McpElicitationBooleanType, #[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 default: Option, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "lowercase")] #[ts(export_to = "v2/")] pub enum McpElicitationBooleanType { Boolean, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(untagged)] #[ts(export_to = "v2/")] 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)] #[serde(untagged)] #[ts(export_to = "v2/")] pub enum McpElicitationSingleSelectEnumSchema { Untitled(McpElicitationUntitledSingleSelectEnumSchema), Titled(McpElicitationTitledSingleSelectEnumSchema), } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationUntitledSingleSelectEnumSchema { #[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(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub default: Option, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationTitledSingleSelectEnumSchema { #[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 = "oneOf")] #[ts(rename = "oneOf")] pub one_of: Vec, #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub default: Option, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(untagged)] #[ts(export_to = "v2/")] pub enum McpElicitationMultiSelectEnumSchema { Untitled(McpElicitationUntitledMultiSelectEnumSchema), Titled(McpElicitationTitledMultiSelectEnumSchema), } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationUntitledMultiSelectEnumSchema { #[serde(rename = "type")] #[ts(rename = "type")] pub type_: McpElicitationArrayType, #[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 min_items: Option, #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub max_items: Option, pub items: McpElicitationUntitledEnumItems, #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub default: Option>, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationTitledMultiSelectEnumSchema { #[serde(rename = "type")] #[ts(rename = "type")] pub type_: McpElicitationArrayType, #[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 min_items: Option, #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub max_items: Option, pub items: McpElicitationTitledEnumItems, #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub default: Option>, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "lowercase")] #[ts(export_to = "v2/")] pub enum McpElicitationArrayType { Array, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationUntitledEnumItems { #[serde(rename = "type")] #[ts(rename = "type")] pub type_: McpElicitationStringType, #[serde(rename = "enum")] #[ts(rename = "enum")] pub enum_: Vec, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationTitledEnumItems { #[serde(rename = "anyOf", alias = "oneOf")] #[ts(rename = "anyOf")] pub any_of: Vec, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(deny_unknown_fields)] #[ts(export_to = "v2/")] pub struct McpElicitationConstOption { #[serde(rename = "const")] #[ts(rename = "const")] pub const_: String, pub title: String, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(tag = "mode", rename_all = "camelCase")] #[ts(tag = "mode")] #[ts(export_to = "v2/")] pub enum McpServerElicitationRequest { #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] Form { #[serde(rename = "_meta")] #[ts(rename = "_meta")] meta: Option, message: String, requested_schema: McpElicitationSchema, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] Url { #[serde(rename = "_meta")] #[ts(rename = "_meta")] meta: Option, message: String, url: String, elicitation_id: String, }, } impl TryFrom for McpServerElicitationRequest { type Error = serde_json::Error; fn try_from(value: CoreElicitationRequest) -> Result { match value { CoreElicitationRequest::Form { meta, message, requested_schema, } => Ok(Self::Form { meta, message, requested_schema: serde_json::from_value(requested_schema)?, }), CoreElicitationRequest::Url { meta, message, url, elicitation_id, } => Ok(Self::Url { meta, message, url, elicitation_id, }), } } } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct McpServerElicitationRequestResponse { pub action: McpServerElicitationAction, /// Structured user input for accepted elicitations, mirroring RMCP `CreateElicitationResult`. /// /// This is nullable because decline/cancel responses have no content. pub content: Option, /// Optional client metadata for form-mode action handling. #[serde(rename = "_meta")] #[ts(rename = "_meta")] pub meta: Option, } impl From for rmcp::model::CreateElicitationResult { fn from(value: McpServerElicitationRequestResponse) -> Self { Self { action: value.action.into(), content: value.content, meta: None, } } } impl From for McpServerElicitationRequestResponse { fn from(value: rmcp::model::CreateElicitationResult) -> Self { Self { action: value.action.into(), content: value.content, meta: None, } } }