File size: 4,818 Bytes
17f328f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | use std::collections::HashMap;
use anyhow::Result;
use async_channel::Sender;
use codex_api::SharedAuthProvider;
use codex_config::McpServerAuth;
use codex_config::McpServerConfig;
use codex_config::McpServerTransportConfig;
use codex_protocol::protocol::Event;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::McpStartupFailureReason;
use codex_protocol::protocol::McpStartupUpdateEvent;
use codex_rmcp_client::McpAuthState;
use codex_rmcp_client::McpLoginRequirement;
use crate::mcp::CODEX_APPS_MCP_SERVER_NAME;
use crate::rmcp_client::DEFAULT_STARTUP_TIMEOUT;
use crate::rmcp_client::StartupOutcomeError;
use crate::server::EffectiveMcpServer;
/// Makes ChatGPT authentication available to servers that explicitly opt in.
pub(super) fn chatgpt_auth_provider_for_server(
server: &EffectiveMcpServer,
chatgpt_auth_provider: Option<SharedAuthProvider>,
) -> Option<SharedAuthProvider> {
if !matches!(&server.config().auth, McpServerAuth::ChatGpt)
|| !server.config().is_local_environment()
{
return None;
}
chatgpt_auth_provider
}
pub(super) fn should_share_codex_apps_tools_cache(
server_name: &str,
uses_env_bearer_token: bool,
) -> bool {
server_name == CODEX_APPS_MCP_SERVER_NAME && !uses_env_bearer_token
}
pub(super) async fn emit_update(
submit_id: &str,
tx_event: &Sender<Event>,
update: McpStartupUpdateEvent,
) -> Result<(), async_channel::SendError<Event>> {
tx_event
.send(Event {
id: submit_id.to_string(),
msg: EventMsg::McpStartupUpdate(update),
})
.await
}
pub(super) fn mcp_startup_failure_reason(
auth_state: Option<McpAuthState>,
error: &StartupOutcomeError,
) -> Option<McpStartupFailureReason> {
if !error.is_authentication_required() {
return None;
}
match auth_state {
Some(
McpAuthState::LoggedOut(McpLoginRequirement::Reauthentication) | McpAuthState::OAuth,
) => Some(McpStartupFailureReason::ReauthenticationRequired),
Some(
McpAuthState::Unsupported
| McpAuthState::Unknown
| McpAuthState::LoggedOut(McpLoginRequirement::Login)
| McpAuthState::BearerToken,
)
| None => None,
}
}
pub(super) fn mcp_init_error_display(
server_name: &str,
config: Option<&McpServerConfig>,
error: &StartupOutcomeError,
reason: Option<McpStartupFailureReason>,
) -> String {
let server_key = if !server_name.is_empty()
&& server_name
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
{
server_name.to_string()
} else {
serde_json::Value::String(server_name.to_string()).to_string()
};
if let Some(McpServerTransportConfig::StreamableHttp {
url,
bearer_token_env_var,
http_headers,
..
}) = config.map(|config| &config.transport)
&& url == "https://api.githubcopilot.com/mcp/"
&& bearer_token_env_var.is_none()
&& http_headers.as_ref().map(HashMap::is_empty).unwrap_or(true)
{
format!(
"GitHub MCP does not support OAuth. Log in by adding a personal access token (https://github.com/settings/personal-access-tokens) to your environment and config.toml:\n[mcp_servers.{server_key}]\nbearer_token_env_var = CODEX_GITHUB_PERSONAL_ACCESS_TOKEN"
)
} else if error.is_authentication_required() {
let recovery_hint = if config.is_some_and(|config| !config.is_local_environment()) {
"Use your client's MCP OAuth sign-in flow.".to_string()
} else {
format!("Run `codex mcp login {server_name}`.")
};
let auth_status = match reason {
Some(McpStartupFailureReason::ReauthenticationRequired) => {
"requires OAuth reauthentication"
}
None => "is not logged in",
};
format!("The {server_name} MCP server {auth_status}. {recovery_hint}")
} else if matches!(
error,
StartupOutcomeError::Failed { error, .. }
if error.contains("request timed out")
|| error.contains("timed out handshaking with MCP server")
|| error.contains("MCP client startup timed out")
) {
let startup_timeout_secs = config
.and_then(|config| config.startup_timeout_sec)
.unwrap_or(DEFAULT_STARTUP_TIMEOUT)
.as_secs();
format!(
"MCP client for `{server_name}` timed out after {startup_timeout_secs} seconds. Add or adjust `startup_timeout_sec` in your config.toml:\n[mcp_servers.{server_key}]\nstartup_timeout_sec = XX"
)
} else {
format!("MCP client for `{server_name}` failed to start: {error:#}")
}
}
|