mcp: keep elicitation requests below app wire types (#29724)

## Why

Core and tools need to request MCP elicitation without constructing
app-server wire payloads. The request should remain a neutral protocol
concept until app-server serializes it for a client.

## What changed

- Switched core and tools to
`codex_protocol::approvals::ElicitationRequest`.
- Derived turn and server context inside core instead of carrying
app-server request types through lower layers.
- Kept the app-server payload unchanged through an explicit boundary
conversion.
- Removed the remaining production app-server-protocol dependency from
tools.

## Stack

This is PR 5 of 6, stacked on [PR
#29723](https://github.com/openai/codex/pull/29723). Review only the
delta from `codex/split-connector-metadata-types`. Next: [PR
#29725](https://github.com/openai/codex/pull/29725).

## Validation

- `codex-core` MCP coverage passed: 87 tests.
- Tools elicitation and app-server round-trip coverage passed.
This commit is contained in:
Adam Perry @ OpenAI
2026-06-24 13:53:27 -07:00
committed by GitHub
Unverified
parent a33ad93996
commit df1ee09ec5
10 changed files with 173 additions and 288 deletions
+3 -49
View File
@@ -99,8 +99,9 @@ impl Session {
pub async fn request_mcp_server_elicitation(
&self,
turn_context: &TurnContext,
server_name: String,
request_id: RequestId,
params: McpServerElicitationRequestParams,
request: ElicitationRequest,
) -> McpServerElicitationOutcome {
if self
.services
@@ -118,53 +119,6 @@ impl Session {
};
}
let server_name = params.server_name.clone();
let request = match params.request {
McpServerElicitationRequest::Form {
meta,
message,
requested_schema,
} => {
let requested_schema = match serde_json::to_value(requested_schema) {
Ok(requested_schema) => requested_schema,
Err(err) => {
warn!(
"failed to serialize MCP elicitation schema for server_name: {server_name}, request_id: {request_id}: {err:#}"
);
return McpServerElicitationOutcome {
response: None,
sent: false,
};
}
};
codex_protocol::approvals::ElicitationRequest::Form {
meta,
message,
requested_schema,
}
}
McpServerElicitationRequest::OpenAiForm {
meta,
message,
requested_schema,
} => codex_protocol::approvals::ElicitationRequest::OpenAiForm {
meta,
message,
requested_schema,
},
McpServerElicitationRequest::Url {
meta,
message,
url,
elicitation_id,
} => codex_protocol::approvals::ElicitationRequest::Url {
meta,
message,
url,
elicitation_id,
},
};
let (tx_response, rx_response) = oneshot::channel();
let prev_entry = {
let mut active = self.active_turn.lock().await;
@@ -194,7 +148,7 @@ impl Session {
}
};
let event = EventMsg::ElicitationRequest(ElicitationRequestEvent {
turn_id: params.turn_id,
turn_id: Some(turn_context.sub_id.clone()),
server_name,
id,
request,
+1 -2
View File
@@ -54,8 +54,6 @@ use chrono::Utc;
use codex_analytics::AnalyticsEventsClient;
use codex_analytics::SubAgentThreadStartedInput;
use codex_analytics::TurnCodexErrorFact;
use codex_app_server_protocol::McpServerElicitationRequest;
use codex_app_server_protocol::McpServerElicitationRequestParams;
use codex_config::types::AuthKeyringBackendKind;
use codex_config::types::OAuthCredentialsStoreMode;
use codex_exec_server::Environment;
@@ -87,6 +85,7 @@ use codex_otel::current_span_w3c_trace_context;
use codex_otel::set_parent_from_w3c_trace_context;
use codex_protocol::SessionId;
use codex_protocol::ThreadId;
use codex_protocol::approvals::ElicitationRequest;
use codex_protocol::approvals::ElicitationRequestEvent;
use codex_protocol::approvals::ExecPolicyAmendment;
use codex_protocol::approvals::NetworkPolicyAmendment;
+8 -15
View File
@@ -84,7 +84,6 @@ use crate::tools::handlers::ShellCommandHandler;
use crate::tools::registry::ToolExecutor;
use crate::tools::router::ToolCallSource;
use crate::turn_diff_tracker::TurnDiffTracker;
use codex_app_server_protocol::McpElicitationSchema;
use codex_config::config_toml::ConfigToml;
use codex_config::config_toml::ProjectConfig;
use codex_config::permissions_toml::FilesystemPermissionToml;
@@ -377,24 +376,18 @@ async fn request_mcp_server_elicitation_auto_accepts_when_auto_deny_is_enabled()
.load_full()
.set_elicitations_auto_deny(/*auto_deny*/ true);
let requested_schema: McpElicitationSchema = serde_json::from_value(json!({
"type": "object",
"properties": {},
}))
.expect("schema should deserialize");
let response = session
.request_mcp_server_elicitation(
turn_context.as_ref(),
"codex_apps".to_string(),
RequestId::String("request-1".into()),
McpServerElicitationRequestParams {
thread_id: session.thread_id.to_string(),
turn_id: Some(turn_context.sub_id.clone()),
server_name: "codex_apps".to_string(),
request: McpServerElicitationRequest::Form {
meta: None,
message: "Allow this request?".to_string(),
requested_schema,
},
ElicitationRequest::Form {
meta: None,
message: "Allow this request?".to_string(),
requested_schema: json!({
"type": "object",
"properties": {},
}),
},
)
.await;