Scope MCP sandbox metadata to server environment (#28914)

Scope MCP sandbox metadata to the MCP server's owning environment.

Previously, `codex/sandbox-state-meta` always used the turn's primary
cwd and rebuilt a legacy sandbox policy from that cwd. That can be wrong
for MCP servers owned by a different execution environment.

This now sends the owning environment cwd as a `file:` URI in
`sandboxCwd`, keeps `permissionProfile` as the permission source of
truth, and omits sandbox-state metadata when a non-default server
environment is not selected for the turn. Local/default MCP servers keep
the existing fallback cwd behavior.

Tests:
- `just fmt`
- `just bazel-lock-update`
- `just bazel-lock-check`
- `just test -p codex-mcp`
- `just test -p codex-core mcp_sandbox_cwd`
- `cargo build -p codex-rmcp-client --bin test_stdio_server`
- `just test -p codex-core
stdio_mcp_tool_call_includes_sandbox_state_meta`
This commit is contained in:
jif authored and GitHub committed 2026-06-18 19:31:07 +02:00
1 parent 47ab51470b
commit 790213ded0
9 files changed
+83 -19

No files matched your search

@@ -1127,6 +1127,7 @@ async fn list_all_tools_adds_server_metadata_to_cached_tools() {
manager.server_metadata.insert(
server_name.to_string(),
McpServerMetadata {
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
pollutes_memory: true,
origin: Some(McpServerOrigin::StreamableHttp(
"https://docs.example".to_string(),
@@ -1162,6 +1163,7 @@ fn server_metadata_preserves_tool_approval_policy() {
"https://docs.example",
/*apps_mcp_product_sku*/ None,
);
config.environment_id = "remote".to_string();
config.default_tools_approval_mode = Some(AppToolApproval::Prompt);
config.tools.insert(
"search".to_string(),
@@ -1171,6 +1173,7 @@ fn server_metadata_preserves_tool_approval_policy() {
);
let metadata = McpServerMetadata::from(&EffectiveMcpServer::configured(config));
assert_eq!(metadata.environment_id, "remote");
assert_eq!(metadata.tool_approval_mode("read"), AppToolApproval::Prompt);
assert_eq!(
metadata.tool_approval_mode("search"),