mirror of
https://github.com/openai/codex.git
synced 2026-09-05 15:18:41 +00:00
feat(mcp): support local apps endpoint override
This commit is contained in:
@@ -5,10 +5,33 @@ use codex_extension_api::McpServerContribution;
|
||||
use codex_extension_api::McpServerContributionContext;
|
||||
use codex_extension_api::McpServerContributor;
|
||||
use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;
|
||||
use codex_mcp::codex_apps_mcp_server_config;
|
||||
use codex_mcp::hosted_plugin_runtime_mcp_server_config;
|
||||
|
||||
mod executor_plugin;
|
||||
|
||||
const CODEX_APPS_MCP_BASE_URL_ENV_VAR: &str = "CODEX_APPS_MCP_BASE_URL";
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
enum AppsMcpServerTarget<'a> {
|
||||
HostedPluginRuntime(&'a str),
|
||||
CodexApps(&'a str),
|
||||
}
|
||||
|
||||
fn apps_mcp_server_target<'a>(
|
||||
chatgpt_base_url: &'a str,
|
||||
apps_mcp_base_url_override: Option<&'a str>,
|
||||
) -> AppsMcpServerTarget<'a> {
|
||||
if let Some(apps_mcp_base_url) = apps_mcp_base_url_override
|
||||
.map(str::trim)
|
||||
.filter(|url| !url.is_empty())
|
||||
{
|
||||
return AppsMcpServerTarget::CodexApps(apps_mcp_base_url);
|
||||
}
|
||||
|
||||
AppsMcpServerTarget::HostedPluginRuntime(chatgpt_base_url)
|
||||
}
|
||||
|
||||
struct HostedPluginRuntimeExtension;
|
||||
|
||||
impl McpServerContributor<Config> for HostedPluginRuntimeExtension {
|
||||
@@ -27,17 +50,32 @@ impl McpServerContributor<Config> for HostedPluginRuntimeExtension {
|
||||
return vec![McpServerContribution::Remove { name }];
|
||||
}
|
||||
|
||||
let apps_mcp_base_url_override = std::env::var(CODEX_APPS_MCP_BASE_URL_ENV_VAR).ok();
|
||||
let apps_mcp_product_sku = config.apps_mcp_product_sku.as_deref();
|
||||
let server_config = match apps_mcp_server_target(
|
||||
&config.chatgpt_base_url,
|
||||
apps_mcp_base_url_override.as_deref(),
|
||||
) {
|
||||
AppsMcpServerTarget::HostedPluginRuntime(base_url) => {
|
||||
hosted_plugin_runtime_mcp_server_config(base_url, apps_mcp_product_sku)
|
||||
}
|
||||
AppsMcpServerTarget::CodexApps(base_url) => {
|
||||
codex_apps_mcp_server_config(base_url, apps_mcp_product_sku)
|
||||
}
|
||||
};
|
||||
|
||||
vec![McpServerContribution::Set {
|
||||
name,
|
||||
config: Box::new(hosted_plugin_runtime_mcp_server_config(
|
||||
&config.chatgpt_base_url,
|
||||
config.apps_mcp_product_sku.as_deref(),
|
||||
)),
|
||||
config: Box::new(server_config),
|
||||
}]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "lib_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
pub fn install(builder: &mut ExtensionRegistryBuilder<Config>) {
|
||||
builder.mcp_server_contributor(std::sync::Arc::new(HostedPluginRuntimeExtension));
|
||||
}
|
||||
|
||||
31
codex-rs/ext/mcp/src/lib_tests.rs
Normal file
31
codex-rs/ext/mcp/src/lib_tests.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::AppsMcpServerTarget;
|
||||
use super::apps_mcp_server_target;
|
||||
|
||||
#[test]
|
||||
fn default_uses_hosted_plugin_runtime() {
|
||||
assert_eq!(
|
||||
apps_mcp_server_target(
|
||||
"https://chatgpt.com",
|
||||
/*apps_mcp_base_url_override*/ None,
|
||||
),
|
||||
AppsMcpServerTarget::HostedPluginRuntime("https://chatgpt.com"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_override_uses_hosted_plugin_runtime() {
|
||||
assert_eq!(
|
||||
apps_mcp_server_target("https://chatgpt.com", Some(" ")),
|
||||
AppsMcpServerTarget::HostedPluginRuntime("https://chatgpt.com"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_override_uses_local_codex_apps_endpoint() {
|
||||
assert_eq!(
|
||||
apps_mcp_server_target("https://chatgpt.com", Some("http://127.0.0.1:8061"),),
|
||||
AppsMcpServerTarget::CodexApps("http://127.0.0.1:8061"),
|
||||
);
|
||||
}
|
||||
@@ -11,12 +11,14 @@ use codex_extension_api::McpServerContributionContext;
|
||||
use codex_extension_api::McpServerContributor;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;
|
||||
use codex_mcp::codex_apps_mcp_server_config;
|
||||
use codex_mcp::hosted_plugin_runtime_mcp_server_config;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
type TestResult = Result<(), Box<dyn std::error::Error>>;
|
||||
|
||||
#[tokio::test]
|
||||
async fn contributes_hosted_plugin_runtime_without_an_executor() -> TestResult {
|
||||
async fn contributes_configured_apps_mcp_without_an_executor() -> TestResult {
|
||||
let codex_home = tempfile::tempdir()?;
|
||||
let config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
@@ -34,11 +36,31 @@ async fn contributes_hosted_plugin_runtime_without_an_executor() -> TestResult {
|
||||
let server = servers
|
||||
.get(CODEX_APPS_MCP_SERVER_NAME)
|
||||
.and_then(|server| server.configured_config())
|
||||
.ok_or("hosted plugin runtime should be contributed as a configured server")?;
|
||||
.ok_or("Apps MCP should be contributed as a configured server")?;
|
||||
let McpServerTransportConfig::StreamableHttp { url, .. } = &server.transport else {
|
||||
panic!("hosted plugin runtime should use streamable HTTP");
|
||||
panic!("Apps MCP should use streamable HTTP");
|
||||
};
|
||||
assert_eq!(url, "https://chatgpt.com/backend-api/ps/mcp");
|
||||
let apps_mcp_base_url_override = std::env::var("CODEX_APPS_MCP_BASE_URL").ok();
|
||||
let expected_config = match apps_mcp_base_url_override
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|url| !url.is_empty())
|
||||
{
|
||||
Some(base_url) => {
|
||||
codex_apps_mcp_server_config(base_url, /*apps_mcp_product_sku*/ None)
|
||||
}
|
||||
None => hosted_plugin_runtime_mcp_server_config(
|
||||
"https://chatgpt.com",
|
||||
/*apps_mcp_product_sku*/ None,
|
||||
),
|
||||
};
|
||||
let McpServerTransportConfig::StreamableHttp {
|
||||
url: expected_url, ..
|
||||
} = expected_config.transport
|
||||
else {
|
||||
panic!("expected Apps MCP config should use streamable HTTP");
|
||||
};
|
||||
assert_eq!(url, &expected_url);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user