Files
Adam Perry @ OpenAI 67009bc53f mcp: accept foreign absolute cwd for remote stdio (#29493)
## Why

Remote stdio MCP servers can run in an environment whose path convention
differs from the Codex host. A Windows cwd such as
`C:\Users\openai\share` is absolute for the executor but was rejected by
a POSIX orchestrator.

Built on #29501, now merged, which only clarifies the host-native
`PathUri` constructor name.

## What changed

- Deserialize MCP cwd values as `LegacyAppPathString` so config does not
apply host path rules.
- Interpret that spelling as host-native for local launches and convert
it to `PathUri` at executor launch.
- Skip host filesystem and command resolution checks for remote stdio in
`codex doctor`.
- Add host-independent config and executor-boundary coverage using the
foreign path convention for each test platform.

## Validation

- `just test -p codex-utils-path-uri -p codex-config -p codex-mcp -p
codex-rmcp-client` (408 passed)
- `just test -p codex-cli -p codex-rmcp-client` (372 passed)
- `cargo check --workspace --tests`
- `just test` (11,311 passed; 43 unrelated environment/timing failures)
- `just fix -p codex-cli -p codex-config -p codex-core -p codex-mcp -p
codex-mcp-extension -p codex-rmcp-client -p codex-tui`
2026-06-23 01:33:51 +00:00

68 lines
2.0 KiB
Rust

use std::ffi::OsString;
use std::sync::Arc;
use std::sync::Mutex;
use codex_exec_server::ExecBackend;
use codex_exec_server::ExecBackendFuture;
use codex_exec_server::ExecParams;
use codex_exec_server::ExecServerError;
use codex_rmcp_client::ExecutorStdioServerLauncher;
use codex_rmcp_client::RmcpClient;
use codex_utils_path_uri::PathUri;
use pretty_assertions::assert_eq;
#[derive(Default)]
struct RecordingExecBackend {
params: Mutex<Option<ExecParams>>,
}
impl ExecBackend for RecordingExecBackend {
fn start(&self, params: ExecParams) -> ExecBackendFuture<'_> {
let mut recorded_params = match self.params.lock() {
Ok(recorded_params) => recorded_params,
Err(poisoned) => poisoned.into_inner(),
};
*recorded_params = Some(params);
Box::pin(async {
Err(ExecServerError::Protocol(
"stop after recording executor request".to_string(),
))
})
}
}
#[tokio::test]
async fn executor_stdio_forwards_foreign_absolute_cwd_as_path_uri() {
#[cfg(not(windows))]
let cwd = r"C:\Users\openai\share";
#[cfg(windows)]
let cwd = "/home/openai/share";
#[cfg(not(windows))]
let expected_cwd: PathUri = "file:///C:/Users/openai/share"
.parse()
.expect("expected cwd should be a path URI");
#[cfg(windows)]
let expected_cwd: PathUri = "file:///home/openai/share"
.parse()
.expect("expected cwd should be a path URI");
let backend = Arc::new(RecordingExecBackend::default());
let launcher = Arc::new(ExecutorStdioServerLauncher::new(backend.clone()));
let _ = RmcpClient::new_stdio_client(
OsString::from("echo"),
Vec::new(),
/*env*/ None,
&[],
Some(cwd.to_string()),
launcher,
)
.await;
let params = backend
.params
.lock()
.expect("recorded params lock should not be poisoned")
.take()
.expect("executor start request should be recorded");
assert_eq!(params.cwd, expected_cwd);
}