mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Support Codex Apps auth elicitations (#19193)
## Summary - request URL-mode MCP elicitations when Codex Apps tool calls fail with connector auth metadata - route Codex Apps auth URL elicitations into the TUI app-link flow ## Test plan - `just fmt` - `cargo test -p codex-core mcp_tool_call::tests` - `cargo test -p codex-mcp` - `cargo test -p codex-tui bottom_pane::app_link_view::tests` - `just fix -p codex-core` - `just fix -p codex-mcp` - `just fix -p codex-tui` Also attempted broader local runs: - `cargo test -p codex-core` fails in unrelated config/request-permission/proxy-sensitive tests under the current Codex Desktop environment. - `cargo test -p codex-tui` fails in unrelated status snapshots/trust-default tests because the ambient environment renders workspace-write/network permission defaults.
This commit is contained in:
committed by
GitHub
Unverified
parent
22326e263c
commit
f9a907aebe
@@ -45,6 +45,7 @@ use codex_mcp::ToolInfo;
|
||||
use codex_mcp::ToolPluginProvenance;
|
||||
use codex_mcp::codex_apps_tools_cache_key;
|
||||
use codex_mcp::compute_auth_statuses;
|
||||
use codex_mcp::host_owned_codex_apps_enabled;
|
||||
use codex_mcp::with_codex_apps_mcp;
|
||||
|
||||
const CONNECTORS_READY_TIMEOUT_ON_EMPTY_TOOLS: Duration = Duration::from_secs(30);
|
||||
@@ -246,6 +247,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_environment_manager(
|
||||
|
||||
let mcp_config = config.to_mcp_config(plugins_manager.as_ref()).await;
|
||||
let mcp_servers = with_codex_apps_mcp(HashMap::new(), auth.as_ref(), &mcp_config);
|
||||
let host_owned_codex_apps_enabled = host_owned_codex_apps_enabled(&mcp_config, auth.as_ref());
|
||||
if mcp_servers.is_empty() {
|
||||
return Ok(AccessibleConnectorsStatus {
|
||||
connectors: Vec::new(),
|
||||
@@ -278,6 +280,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_environment_manager(
|
||||
McpRuntimeEnvironment::new(environment, config.cwd.to_path_buf()),
|
||||
config.codex_home.to_path_buf(),
|
||||
codex_apps_tools_cache_key(auth.as_ref()),
|
||||
host_owned_codex_apps_enabled,
|
||||
ToolPluginProvenance::default(),
|
||||
auth.as_ref(),
|
||||
)
|
||||
|
||||
@@ -41,8 +41,11 @@ use codex_config::types::AppToolApproval;
|
||||
use codex_features::Feature;
|
||||
use codex_hooks::PermissionRequestDecision;
|
||||
use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;
|
||||
use codex_mcp::MCP_TOOL_CODEX_APPS_META_KEY;
|
||||
use codex_mcp::McpPermissionPromptAutoApproveContext;
|
||||
use codex_mcp::SandboxState;
|
||||
use codex_mcp::auth_elicitation_completed_result;
|
||||
use codex_mcp::build_auth_elicitation_plan;
|
||||
use codex_mcp::declared_openai_file_input_param_names;
|
||||
use codex_mcp::mcp_permission_prompt_is_auto_approved;
|
||||
use codex_otel::sanitize_metric_tag_value;
|
||||
@@ -52,6 +55,7 @@ use codex_protocol::items::McpToolCallStatus;
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::mcp::CallToolResult;
|
||||
use codex_protocol::openai_models::InputModality;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::McpInvocation;
|
||||
use codex_protocol::protocol::ReviewDecision;
|
||||
use codex_protocol::request_user_input::RequestUserInputAnswer;
|
||||
@@ -340,9 +344,10 @@ async fn handle_approved_mcp_tool_call(
|
||||
let result = execute_mcp_tool_call(
|
||||
sess,
|
||||
turn_context,
|
||||
&server,
|
||||
&tool_name,
|
||||
call_id,
|
||||
&invocation,
|
||||
rewritten_arguments,
|
||||
metadata,
|
||||
request_meta,
|
||||
)
|
||||
.await;
|
||||
@@ -541,28 +546,145 @@ fn truncate_str_to_char_boundary(value: &str, max_chars: usize) -> &str {
|
||||
async fn execute_mcp_tool_call(
|
||||
sess: &Session,
|
||||
turn_context: &TurnContext,
|
||||
server: &str,
|
||||
tool_name: &str,
|
||||
call_id: &str,
|
||||
invocation: &McpInvocation,
|
||||
rewritten_arguments: Option<JsonValue>,
|
||||
metadata: Option<&McpToolApprovalMetadata>,
|
||||
request_meta: Option<JsonValue>,
|
||||
) -> Result<CallToolResult, String> {
|
||||
let request_meta =
|
||||
with_mcp_tool_call_thread_id_meta(request_meta, &sess.conversation_id.to_string());
|
||||
let request_meta =
|
||||
augment_mcp_tool_request_meta_with_sandbox_state(sess, turn_context, server, request_meta)
|
||||
.await
|
||||
.map_err(|e| format!("failed to build MCP tool request metadata: {e:#}"))?;
|
||||
let request_meta = augment_mcp_tool_request_meta_with_sandbox_state(
|
||||
sess,
|
||||
turn_context,
|
||||
&invocation.server,
|
||||
request_meta,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| format!("failed to build MCP tool request metadata: {e:#}"))?;
|
||||
let result = sess
|
||||
.call_tool(server, tool_name, rewritten_arguments, request_meta)
|
||||
.call_tool(
|
||||
&invocation.server,
|
||||
&invocation.tool,
|
||||
rewritten_arguments,
|
||||
request_meta,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| format!("tool call error: {e:?}"))?;
|
||||
sanitize_mcp_tool_result_for_model(
|
||||
let result = sanitize_mcp_tool_result_for_model(
|
||||
turn_context
|
||||
.model_info
|
||||
.input_modalities
|
||||
.contains(&InputModality::Image),
|
||||
Ok(result),
|
||||
)?;
|
||||
Ok(maybe_request_codex_apps_auth_elicitation(
|
||||
sess,
|
||||
turn_context,
|
||||
call_id,
|
||||
&invocation.server,
|
||||
metadata,
|
||||
result,
|
||||
)
|
||||
.await)
|
||||
}
|
||||
|
||||
async fn maybe_request_codex_apps_auth_elicitation(
|
||||
sess: &Session,
|
||||
turn_context: &TurnContext,
|
||||
call_id: &str,
|
||||
server: &str,
|
||||
metadata: Option<&McpToolApprovalMetadata>,
|
||||
result: CallToolResult,
|
||||
) -> CallToolResult {
|
||||
if !sess
|
||||
.services
|
||||
.mcp_connection_manager
|
||||
.read()
|
||||
.await
|
||||
.is_host_owned_codex_apps_server(server)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if !turn_context.features.enabled(Feature::AuthElicitation) {
|
||||
return result;
|
||||
}
|
||||
|
||||
match turn_context.approval_policy.value() {
|
||||
AskForApproval::Never => return result,
|
||||
AskForApproval::Granular(granular_config) if !granular_config.allows_mcp_elicitations() => {
|
||||
return result;
|
||||
}
|
||||
AskForApproval::OnFailure
|
||||
| AskForApproval::OnRequest
|
||||
| AskForApproval::UnlessTrusted
|
||||
| AskForApproval::Granular(_) => {}
|
||||
}
|
||||
|
||||
let connector_id = metadata.and_then(|metadata| metadata.connector_id.as_deref());
|
||||
let connector_name = metadata.and_then(|metadata| metadata.connector_name.as_deref());
|
||||
let install_url = connector_id.map(|connector_id| {
|
||||
codex_connectors::metadata::connector_install_url(
|
||||
connector_name.unwrap_or(connector_id),
|
||||
connector_id,
|
||||
)
|
||||
});
|
||||
let Some(plan) =
|
||||
build_auth_elicitation_plan(call_id, &result, connector_id, connector_name, install_url)
|
||||
else {
|
||||
return result;
|
||||
};
|
||||
|
||||
let request_id = rmcp::model::RequestId::String(plan.elicitation.elicitation_id.clone().into());
|
||||
let params = McpServerElicitationRequestParams {
|
||||
thread_id: sess.conversation_id.to_string(),
|
||||
turn_id: Some(turn_context.sub_id.clone()),
|
||||
server_name: CODEX_APPS_MCP_SERVER_NAME.to_string(),
|
||||
request: McpServerElicitationRequest::Url {
|
||||
meta: Some(plan.elicitation.meta),
|
||||
message: plan.elicitation.message,
|
||||
url: plan.elicitation.url,
|
||||
elicitation_id: plan.elicitation.elicitation_id,
|
||||
},
|
||||
};
|
||||
let response = sess
|
||||
.request_mcp_server_elicitation(turn_context, request_id, params)
|
||||
.await;
|
||||
if !response
|
||||
.as_ref()
|
||||
.is_some_and(|response| response.action == ElicitationAction::Accept)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
refresh_codex_apps_after_connector_auth(sess, turn_context).await;
|
||||
auth_elicitation_completed_result(&plan.auth_failure, result.meta)
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::await_holding_invalid_type,
|
||||
reason = "Codex Apps cache refresh reads through the session-owned manager guard"
|
||||
)]
|
||||
async fn refresh_codex_apps_after_connector_auth(sess: &Session, turn_context: &TurnContext) {
|
||||
let mcp_tools_result = {
|
||||
let manager = sess.services.mcp_connection_manager.read().await;
|
||||
manager.hard_refresh_codex_apps_tools_cache().await
|
||||
};
|
||||
|
||||
match mcp_tools_result {
|
||||
Ok(mcp_tools) => {
|
||||
let auth = sess.services.auth_manager.auth().await;
|
||||
connectors::refresh_accessible_connectors_cache_from_mcp_tools(
|
||||
&turn_context.config,
|
||||
auth.as_ref(),
|
||||
&mcp_tools,
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!("failed to refresh Codex Apps tools after connector auth: {err:#}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(
|
||||
@@ -834,7 +956,6 @@ pub(crate) struct McpToolApprovalMetadata {
|
||||
openai_file_input_params: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
const MCP_TOOL_CODEX_APPS_META_KEY: &str = "_codex_apps";
|
||||
const MCP_TOOL_OPENAI_OUTPUT_TEMPLATE_META_KEY: &str = "openai/outputTemplate";
|
||||
const MCP_TOOL_UI_RESOURCE_URI_META_KEY: &str = "ui/resourceUri";
|
||||
const MCP_TOOL_THREAD_ID_META_KEY: &str = "threadId";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::*;
|
||||
use crate::config::ConfigBuilder;
|
||||
use crate::config::ManagedFeatures;
|
||||
use crate::session::tests::make_session_and_context;
|
||||
use crate::session::tests::make_session_and_context_with_rx;
|
||||
use crate::state::ActiveTurn;
|
||||
@@ -14,11 +15,13 @@ use codex_config::types::ApprovalsReviewer;
|
||||
use codex_config::types::AppsConfigToml;
|
||||
use codex_config::types::McpServerConfig;
|
||||
use codex_config::types::McpServerToolConfig;
|
||||
use codex_features::Features;
|
||||
use codex_hooks::Hooks;
|
||||
use codex_hooks::HooksConfig;
|
||||
use codex_model_provider::create_model_provider;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::GranularApprovalConfig;
|
||||
use core_test_support::PathExt;
|
||||
use core_test_support::hooks::trusted_config_layer_stack;
|
||||
@@ -1065,6 +1068,250 @@ async fn codex_apps_tool_call_request_meta_includes_call_id_without_existing_cod
|
||||
);
|
||||
}
|
||||
|
||||
fn codex_apps_auth_failure_result() -> CallToolResult {
|
||||
CallToolResult {
|
||||
content: vec![serde_json::json!({
|
||||
"type": "text",
|
||||
"text": "Connector reauthentication required",
|
||||
})],
|
||||
structured_content: None,
|
||||
is_error: Some(true),
|
||||
meta: Some(serde_json::json!({
|
||||
MCP_TOOL_CODEX_APPS_META_KEY: {
|
||||
"connector_auth_failure": {
|
||||
"is_auth_failure": true,
|
||||
"auth_reason": "reauthentication_required",
|
||||
"connector_id": "connector_calendar",
|
||||
"connector_name": "Untrusted Calendar",
|
||||
"link_id": "link_123",
|
||||
"error_code": "UNAUTHORIZED",
|
||||
"error_http_status_code": 401,
|
||||
"error_action": "TRIGGER_REAUTHENTICATION",
|
||||
},
|
||||
},
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
fn codex_apps_auth_failure_metadata() -> McpToolApprovalMetadata {
|
||||
approval_metadata(
|
||||
Some("connector_calendar"),
|
||||
Some("Google Calendar"),
|
||||
Some("Manage events and schedules."),
|
||||
Some("Create Event"),
|
||||
Some("Create a calendar event."),
|
||||
)
|
||||
}
|
||||
|
||||
async fn install_host_owned_codex_apps_manager(session: &Session, turn_context: &TurnContext) {
|
||||
let auth = session.services.auth_manager.auth().await;
|
||||
let environment = session
|
||||
.services
|
||||
.environment_manager
|
||||
.default_environment()
|
||||
.unwrap_or_else(|| session.services.environment_manager.local_environment());
|
||||
let (manager, _cancel_token) = codex_mcp::McpConnectionManager::new(
|
||||
&HashMap::new(),
|
||||
turn_context.config.mcp_oauth_credentials_store_mode,
|
||||
HashMap::new(),
|
||||
&turn_context.approval_policy,
|
||||
turn_context.sub_id.clone(),
|
||||
session.get_tx_event(),
|
||||
turn_context.permission_profile(),
|
||||
codex_mcp::McpRuntimeEnvironment::new(environment, turn_context.cwd.to_path_buf()),
|
||||
turn_context.config.codex_home.to_path_buf(),
|
||||
codex_mcp::codex_apps_tools_cache_key(auth.as_ref()),
|
||||
/*host_owned_codex_apps_enabled*/ true,
|
||||
codex_mcp::ToolPluginProvenance::default(),
|
||||
auth.as_ref(),
|
||||
)
|
||||
.await;
|
||||
*session.services.mcp_connection_manager.write().await = manager;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn codex_apps_auth_elicitation_feature_disabled_returns_original_result() {
|
||||
let (session, turn_context, rx_event) = make_session_and_context_with_rx().await;
|
||||
install_host_owned_codex_apps_manager(&session, &turn_context).await;
|
||||
let result = codex_apps_auth_failure_result();
|
||||
let metadata = codex_apps_auth_failure_metadata();
|
||||
|
||||
let returned = maybe_request_codex_apps_auth_elicitation(
|
||||
&session,
|
||||
&turn_context,
|
||||
"call_123",
|
||||
CODEX_APPS_MCP_SERVER_NAME,
|
||||
Some(&metadata),
|
||||
result.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(returned, result);
|
||||
assert!(rx_event.try_recv().is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn codex_apps_auth_elicitation_non_host_owned_server_returns_original_result() {
|
||||
let (session, mut turn_context, rx_event) = make_session_and_context_with_rx().await;
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::AuthElicitation);
|
||||
Arc::get_mut(&mut turn_context)
|
||||
.expect("single turn context ref")
|
||||
.features = ManagedFeatures::from(features);
|
||||
let result = codex_apps_auth_failure_result();
|
||||
let metadata = codex_apps_auth_failure_metadata();
|
||||
|
||||
let returned = maybe_request_codex_apps_auth_elicitation(
|
||||
&session,
|
||||
&turn_context,
|
||||
"call_123",
|
||||
CODEX_APPS_MCP_SERVER_NAME,
|
||||
Some(&metadata),
|
||||
result.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(returned, result);
|
||||
assert!(rx_event.try_recv().is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn codex_apps_auth_elicitation_disallowed_by_policy_returns_original_result() {
|
||||
let (session, mut turn_context, rx_event) = make_session_and_context_with_rx().await;
|
||||
install_host_owned_codex_apps_manager(&session, &turn_context).await;
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::AuthElicitation);
|
||||
let turn_context = Arc::get_mut(&mut turn_context).expect("single turn context ref");
|
||||
turn_context.features = ManagedFeatures::from(features);
|
||||
turn_context
|
||||
.approval_policy
|
||||
.set(AskForApproval::Never)
|
||||
.expect("test setup should allow updating approval policy");
|
||||
let result = codex_apps_auth_failure_result();
|
||||
let metadata = codex_apps_auth_failure_metadata();
|
||||
|
||||
let returned = maybe_request_codex_apps_auth_elicitation(
|
||||
&session,
|
||||
turn_context,
|
||||
"call_123",
|
||||
CODEX_APPS_MCP_SERVER_NAME,
|
||||
Some(&metadata),
|
||||
result.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(returned, result);
|
||||
assert!(rx_event.try_recv().is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn codex_apps_auth_elicitation_granular_mcp_disabled_returns_original_result() {
|
||||
let (session, mut turn_context, rx_event) = make_session_and_context_with_rx().await;
|
||||
install_host_owned_codex_apps_manager(&session, &turn_context).await;
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::AuthElicitation);
|
||||
let turn_context = Arc::get_mut(&mut turn_context).expect("single turn context ref");
|
||||
turn_context.features = ManagedFeatures::from(features);
|
||||
turn_context
|
||||
.approval_policy
|
||||
.set(AskForApproval::Granular(GranularApprovalConfig {
|
||||
sandbox_approval: true,
|
||||
rules: true,
|
||||
skill_approval: true,
|
||||
request_permissions: true,
|
||||
mcp_elicitations: false,
|
||||
}))
|
||||
.expect("test setup should allow updating approval policy");
|
||||
let result = codex_apps_auth_failure_result();
|
||||
let metadata = codex_apps_auth_failure_metadata();
|
||||
|
||||
let returned = maybe_request_codex_apps_auth_elicitation(
|
||||
&session,
|
||||
turn_context,
|
||||
"call_123",
|
||||
CODEX_APPS_MCP_SERVER_NAME,
|
||||
Some(&metadata),
|
||||
result.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(returned, result);
|
||||
assert!(rx_event.try_recv().is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn codex_apps_auth_elicitation_feature_enabled_requests_elicitation() {
|
||||
let (session, mut turn_context, rx_event) = make_session_and_context_with_rx().await;
|
||||
install_host_owned_codex_apps_manager(&session, &turn_context).await;
|
||||
*session.active_turn.lock().await = Some(ActiveTurn::default());
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::AuthElicitation);
|
||||
Arc::get_mut(&mut turn_context)
|
||||
.expect("single turn context ref")
|
||||
.features = ManagedFeatures::from(features);
|
||||
let result = codex_apps_auth_failure_result();
|
||||
let metadata = codex_apps_auth_failure_metadata();
|
||||
|
||||
let request_task = tokio::spawn({
|
||||
let session = Arc::clone(&session);
|
||||
let turn_context = Arc::clone(&turn_context);
|
||||
async move {
|
||||
maybe_request_codex_apps_auth_elicitation(
|
||||
&session,
|
||||
&turn_context,
|
||||
"call_123",
|
||||
CODEX_APPS_MCP_SERVER_NAME,
|
||||
Some(&metadata),
|
||||
result,
|
||||
)
|
||||
.await
|
||||
}
|
||||
});
|
||||
|
||||
let request = loop {
|
||||
let event = tokio::time::timeout(std::time::Duration::from_secs(1), rx_event.recv())
|
||||
.await
|
||||
.expect("elicitation event timed out")
|
||||
.expect("expected elicitation event");
|
||||
if let EventMsg::ElicitationRequest(request) = event.msg {
|
||||
break request;
|
||||
}
|
||||
};
|
||||
assert_eq!(request.server_name, CODEX_APPS_MCP_SERVER_NAME);
|
||||
assert_eq!(
|
||||
request.id,
|
||||
codex_protocol::mcp::RequestId::String("codex_apps_auth_call_123".to_string())
|
||||
);
|
||||
assert!(matches!(
|
||||
request.request,
|
||||
codex_protocol::approvals::ElicitationRequest::Url { .. }
|
||||
));
|
||||
|
||||
session
|
||||
.resolve_elicitation(
|
||||
CODEX_APPS_MCP_SERVER_NAME.to_string(),
|
||||
rmcp::model::RequestId::String("codex_apps_auth_call_123".into()),
|
||||
ElicitationResponse {
|
||||
action: ElicitationAction::Accept,
|
||||
content: None,
|
||||
meta: None,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("elicitation should resolve");
|
||||
let returned = tokio::time::timeout(std::time::Duration::from_secs(1), request_task)
|
||||
.await
|
||||
.expect("auth elicitation task timed out")
|
||||
.expect("auth elicitation task failed");
|
||||
assert_eq!(
|
||||
returned.content,
|
||||
vec![serde_json::json!({
|
||||
"type": "text",
|
||||
"text": "Authentication for Google Calendar was requested and accepted. Retry this tool call now.",
|
||||
})]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mcp_tool_call_thread_id_meta_is_added_to_request_meta() {
|
||||
assert_eq!(
|
||||
|
||||
@@ -233,6 +233,8 @@ impl Session {
|
||||
.tool_plugin_provenance(config.as_ref())
|
||||
.await;
|
||||
let mcp_servers = with_codex_apps_mcp(mcp_servers, auth.as_ref(), &mcp_config);
|
||||
let host_owned_codex_apps_enabled =
|
||||
host_owned_codex_apps_enabled(&mcp_config, auth.as_ref());
|
||||
let auth_statuses =
|
||||
compute_auth_statuses(mcp_servers.iter(), store_mode, auth.as_ref()).await;
|
||||
let mcp_runtime_environment = match turn_context.environments.primary() {
|
||||
@@ -264,6 +266,7 @@ impl Session {
|
||||
mcp_runtime_environment,
|
||||
config.codex_home.to_path_buf(),
|
||||
codex_apps_tools_cache_key(auth.as_ref()),
|
||||
host_owned_codex_apps_enabled,
|
||||
tool_plugin_provenance,
|
||||
auth.as_ref(),
|
||||
)
|
||||
|
||||
@@ -307,6 +307,7 @@ use crate::windows_sandbox::WindowsSandboxLevelExt;
|
||||
use codex_core_plugins::PluginsManager;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_mcp::compute_auth_statuses;
|
||||
use codex_mcp::host_owned_codex_apps_enabled;
|
||||
use codex_mcp::with_codex_apps_mcp;
|
||||
use codex_otel::SessionTelemetry;
|
||||
use codex_otel::THREAD_STARTED_METRIC;
|
||||
|
||||
@@ -918,6 +918,9 @@ impl Session {
|
||||
let enabled_mcp_server_count = mcp_servers.values().filter(|server| server.enabled).count();
|
||||
let required_mcp_server_count = required_mcp_servers.len();
|
||||
let tool_plugin_provenance = mcp_manager.tool_plugin_provenance(config.as_ref()).await;
|
||||
let host_owned_codex_apps_enabled = config
|
||||
.features
|
||||
.apps_enabled_for_auth(auth.as_ref().is_some_and(|auth| auth.uses_codex_backend()));
|
||||
{
|
||||
let mut cancel_guard = sess.services.mcp_startup_cancellation_token.lock().await;
|
||||
cancel_guard.cancel();
|
||||
@@ -959,6 +962,7 @@ impl Session {
|
||||
mcp_runtime_environment,
|
||||
config.codex_home.to_path_buf(),
|
||||
codex_apps_tools_cache_key(auth),
|
||||
host_owned_codex_apps_enabled,
|
||||
tool_plugin_provenance,
|
||||
auth,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user