Files
codex/codex-rs/ext/mcp/src/lib_tests.rs
T
raquel-openai b58952b0fa fix: forward originator to Codex Apps MCP (#31481)
## Summary
- Forward Codexs canonical `originator` header on ChatGPT-hosted Apps
and plugin-runtime MCP requests.
- Preserve the configured `X-OpenAI-Product-Sku` header.
- Cover originator-only and originator-plus-SKU configurations.

## Why
Sites project creation is logged downstream of Apps MCP. Production
validation found `CODEX_UNKNOWN_DEFAULT` project-created threads that
matched `codex_surface=desktop_app` and `originator=Codex Desktop` in
`fact_codex_cli`
([query](https://kepler.gateway.data-1.internal.api.openai.org/permalink/H_mVoVPqLQ0)).

The hosted Apps MCP configuration forwarded the product SKU but not
Codexs canonical originator, so codex-backend could not derive
`CODEX_DESKTOP_APP` for those tool calls.

## Validation
- `just fmt`
- `CARGO_HOME=/private/tmp/codex-cargo-home
CARGO_TARGET_DIR=/private/tmp/codex-target just test -p codex-mcp` (106
passed)
- `CARGO_HOME=/private/tmp/codex-cargo-home
CARGO_TARGET_DIR=/private/tmp/codex-target just fix -p codex-mcp`
2026-07-09 16:51:44 +00:00

49 lines
1.6 KiB
Rust

use super::*;
use codex_config::McpServerTransportConfig;
use codex_core::config::ConfigBuilder;
use codex_extension_api::ExtensionData;
use codex_extension_api::ExtensionDataInit;
use pretty_assertions::assert_eq;
#[tokio::test]
async fn hosted_plugin_runtime_forwards_thread_originator() -> Result<(), Box<dyn std::error::Error>>
{
let codex_home = tempfile::tempdir()?;
let config = ConfigBuilder::default()
.codex_home(codex_home.path().to_path_buf())
.fallback_cwd(Some(codex_home.path().to_path_buf()))
.cli_overrides(vec![
("features.apps".to_string(), true.into()),
("chatgpt_base_url".to_string(), "https://chatgpt.com".into()),
])
.build()
.await?;
let thread_init = ExtensionDataInit::new();
let thread_store = ExtensionData::new("thread");
let contributions = HostedPluginRuntimeExtension
.contribute(McpServerContributionContext::for_step(
&config,
&thread_init,
&thread_store,
"codex_work_desktop",
/*available_environment_ids*/ &[],
))
.await;
let [McpServerContribution::Set { config: server, .. }] = contributions.as_slice() else {
panic!("hosted plugin runtime should contribute one server");
};
let McpServerTransportConfig::StreamableHttp { http_headers, .. } = &server.transport else {
panic!("hosted plugin runtime should use streamable HTTP");
};
assert_eq!(
http_headers
.as_ref()
.and_then(|headers| headers.get("originator")),
Some(&"codex_work_desktop".to_string())
);
Ok(())
}