Compare commits

...

2 Commits

Author SHA1 Message Date
Casey Chow
4ad5e765ed chore(core): set default apps mcp endpoint to gateway 2026-02-07 11:32:18 -05:00
Casey Chow
61cc4da4dc feat(core): allow configurable apps MCP URL 2026-02-07 11:32:10 -05:00
3 changed files with 48 additions and 17 deletions

View File

@@ -1141,6 +1141,10 @@
"description": "Base URL for requests to ChatGPT (as opposed to the OpenAI API).",
"type": "string"
},
"connectors_mcp_url": {
"description": "Optional override for the Codex Apps MCP endpoint URL.",
"type": "string"
},
"check_for_update_on_startup": {
"description": "When `true`, checks for Codex updates on startup and surfaces update prompts. Set to `false` only if your Codex updates are centrally managed. Defaults to `true`.",
"type": "boolean"
@@ -1588,4 +1592,4 @@
},
"title": "ConfigToml",
"type": "object"
}
}

View File

@@ -311,6 +311,8 @@ pub struct Config {
/// Base URL for requests to ChatGPT (as opposed to the OpenAI API).
pub chatgpt_base_url: String,
/// Optional override for the Codex Apps MCP endpoint URL.
pub connectors_mcp_url: Option<String>,
/// When set, restricts ChatGPT login to a specific workspace identifier.
pub forced_chatgpt_workspace_id: Option<String>,
@@ -934,6 +936,8 @@ pub struct ConfigToml {
/// Base URL for requests to ChatGPT (as opposed to the OpenAI API).
pub chatgpt_base_url: Option<String>,
/// Optional override for the Codex Apps MCP endpoint URL.
pub connectors_mcp_url: Option<String>,
pub projects: Option<HashMap<String, ProjectConfig>>,
@@ -1667,6 +1671,11 @@ impl Config {
.chatgpt_base_url
.or(cfg.chatgpt_base_url)
.unwrap_or("https://chatgpt.com/backend-api/".to_string()),
connectors_mcp_url: cfg
.connectors_mcp_url
.map(str::trim)
.map(str::to_string)
.filter(|url| !url.is_empty()),
forced_chatgpt_workspace_id,
forced_login_method,
include_apply_patch_tool: include_apply_patch_tool_flag,
@@ -3865,6 +3874,7 @@ model_verbosity = "high"
model_verbosity: None,
personality: Some(Personality::Pragmatic),
chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(),
connectors_mcp_url: None,
base_instructions: None,
developer_instructions: None,
compact_prompt: None,
@@ -3952,6 +3962,7 @@ model_verbosity = "high"
model_verbosity: None,
personality: Some(Personality::Pragmatic),
chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(),
connectors_mcp_url: None,
base_instructions: None,
developer_instructions: None,
compact_prompt: None,
@@ -4054,6 +4065,7 @@ model_verbosity = "high"
model_verbosity: None,
personality: Some(Personality::Pragmatic),
chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(),
connectors_mcp_url: None,
base_instructions: None,
developer_instructions: None,
compact_prompt: None,
@@ -4142,6 +4154,7 @@ model_verbosity = "high"
model_verbosity: Some(Verbosity::High),
personality: Some(Personality::Pragmatic),
chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(),
connectors_mcp_url: None,
base_instructions: None,
developer_instructions: None,
compact_prompt: None,

View File

@@ -30,6 +30,8 @@ const MCP_TOOL_NAME_PREFIX: &str = "mcp";
const MCP_TOOL_NAME_DELIMITER: &str = "__";
pub(crate) const CODEX_APPS_MCP_SERVER_NAME: &str = "codex_apps";
const CODEX_CONNECTORS_TOKEN_ENV_VAR: &str = "CODEX_CONNECTORS_TOKEN";
const DEFAULT_CODEX_APPS_MCP_URL: &str =
"https://connectorsapi.gateway.unified-0.api.openai.com/v1/connectors/mcp/";
fn codex_apps_mcp_bearer_token_env_var() -> Option<String> {
match env::var(CODEX_CONNECTORS_TOKEN_ENV_VAR) {
@@ -65,21 +67,13 @@ fn codex_apps_mcp_http_headers(auth: Option<&CodexAuth>) -> Option<HashMap<Strin
}
}
fn codex_apps_mcp_url(base_url: &str) -> String {
let mut base_url = base_url.trim_end_matches('/').to_string();
if (base_url.starts_with("https://chatgpt.com")
|| base_url.starts_with("https://chat.openai.com"))
&& !base_url.contains("/backend-api")
{
base_url = format!("{base_url}/backend-api");
}
if base_url.contains("/backend-api") {
format!("{base_url}/wham/apps")
} else if base_url.contains("/api/codex") {
format!("{base_url}/apps")
} else {
format!("{base_url}/api/codex/apps")
}
fn codex_apps_mcp_url(configured_url: Option<&str>) -> String {
let base_url = configured_url
.map(str::trim)
.filter(|url| !url.is_empty())
.unwrap_or(DEFAULT_CODEX_APPS_MCP_URL)
.trim_end_matches('/');
format!("{base_url}/")
}
fn codex_apps_mcp_server_config(config: &Config, auth: Option<&CodexAuth>) -> McpServerConfig {
@@ -89,7 +83,7 @@ fn codex_apps_mcp_server_config(config: &Config, auth: Option<&CodexAuth>) -> Mc
} else {
codex_apps_mcp_http_headers(auth)
};
let url = codex_apps_mcp_url(&config.chatgpt_base_url);
let url = codex_apps_mcp_url(config.connectors_mcp_url.as_deref());
McpServerConfig {
transport: McpServerTransportConfig::StreamableHttp {
@@ -384,4 +378,24 @@ mod tests {
assert_eq!(group_tools_by_server(&tools), expected);
}
#[test]
fn codex_apps_mcp_url_defaults_to_internal_endpoint() {
assert_eq!(
codex_apps_mcp_url(None),
"https://connectorsapi.gateway.unified-0.api.openai.com/v1/connectors/mcp/"
);
}
#[test]
fn codex_apps_mcp_url_normalizes_custom_value() {
assert_eq!(
codex_apps_mcp_url(Some("https://example.com/custom/path")),
"https://example.com/custom/path/"
);
assert_eq!(
codex_apps_mcp_url(Some("https://example.com/custom/path/")),
"https://example.com/custom/path/"
);
}
}