Support threadless MCP connections without event channels (#34408)

## What changed

- Make the MCP connection manager's event sender optional.
- Skip startup notifications when a caller has no session event stream.
- Decline interactive MCP elicitations in that mode while allowing the
  underlying operation to continue.
- Update threadless resource reads, status snapshots, and connector discovery
  to use the channel-free path.

## Testing

- Extend the app-server MCP resource test to verify that a threadless resource
  read declines an elicitation and still returns the resource contents.

GitOrigin-RevId: 65fea152adbc02a78e09a40cae4134b6dccffd34
This commit is contained in:
stevenlee-oai authored and copyberry committed 2026-07-20 20:43:36 +00:00
1 parent b7e39aa316
commit 19940967bd
14 files changed
+185 -124

No files matched your search

@@ -260,7 +260,7 @@ async fn disabled_permissions_auto_accept_elicitation_with_empty_form_schema() {
ElicitationRequestRouter::default(),
);
let (tx_event, _rx_event) = async_channel::bounded(1);
let sender = manager.make_sender("server".to_string(), tx_event);
let sender = manager.make_sender("server".to_string(), Some(tx_event));
let response = sender(
NumberOrString::Number(1),
@@ -297,7 +297,7 @@ async fn disabled_permissions_do_not_auto_accept_elicitation_with_requested_fiel
ElicitationRequestRouter::default(),
);
let (tx_event, _rx_event) = async_channel::bounded(1);
let sender = manager.make_sender("server".to_string(), tx_event);
let sender = manager.make_sender("server".to_string(), Some(tx_event));
let response = sender(
NumberOrString::Number(1),
@@ -362,8 +362,8 @@ async fn shared_elicitation_router_targets_the_exact_pending_request() {
router,
);
let (tx_event, rx_event) = async_channel::bounded(2);
let sender_a = manager_a.make_sender("server".to_string(), tx_event.clone());
let sender_b = manager_b.make_sender("server".to_string(), tx_event);
let sender_a = manager_a.make_sender("server".to_string(), Some(tx_event.clone()));
let sender_b = manager_b.make_sender("server".to_string(), Some(tx_event));
let elicitation = codex_rmcp_client::Elicitation::Mcp(
CreateElicitationRequestParams::FormElicitationParams {
meta: None,
@@ -1539,8 +1539,6 @@ fn host_owned_codex_apps_matches_reserved_name_with_server_metadata() {
#[tokio::test]
async fn no_local_runtime_fails_local_stdio_but_keeps_local_http_server() {
let approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
let (tx_event, rx_event) = async_channel::unbounded();
drop(rx_event);
let codex_home = tempdir().expect("tempdir");
let mcp_servers = HashMap::from([
(
@@ -1605,7 +1603,7 @@ async fn no_local_runtime_fails_local_stdio_but_keeps_local_http_server() {
AuthKeyringBackendKind::default(),
&approval_policy,
String::new(),
tx_event,
/*tx_event*/ None,
cancel_token.clone(),
PermissionProfile::default(),
McpRuntimeContext::new(