mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
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:
@@ -81,6 +81,7 @@ use codex_rollout::state_db;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_output_truncation::TruncationPolicy;
|
||||
use codex_utils_output_truncation::truncate_text;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use codex_utils_pty::DEFAULT_OUTPUT_BYTES_CAP;
|
||||
use rmcp::model::ToolAnnotations;
|
||||
use serde::Deserialize;
|
||||
@@ -713,10 +714,8 @@ async fn augment_mcp_tool_request_meta_with_sandbox_state(
|
||||
server: &str,
|
||||
mut meta: Option<serde_json::Value>,
|
||||
) -> anyhow::Result<Option<serde_json::Value>> {
|
||||
let supports_sandbox_state_meta = sess
|
||||
.services
|
||||
.mcp_connection_manager
|
||||
.load_full()
|
||||
let mcp_connection_manager = sess.services.mcp_connection_manager.load_full();
|
||||
let supports_sandbox_state_meta = mcp_connection_manager
|
||||
.server_supports_sandbox_state_meta_capability(server)
|
||||
.await
|
||||
.unwrap_or(false);
|
||||
@@ -724,12 +723,17 @@ async fn augment_mcp_tool_request_meta_with_sandbox_state(
|
||||
return Ok(meta);
|
||||
}
|
||||
|
||||
let server_environment_id = mcp_connection_manager
|
||||
.server_environment_id(server)
|
||||
.unwrap_or(codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID);
|
||||
let Some(sandbox_cwd) = sandbox_cwd_for_mcp_server(turn_context, server_environment_id) else {
|
||||
return Ok(meta);
|
||||
};
|
||||
let permission_profile = turn_context.permission_profile();
|
||||
let sandbox_state = serde_json::to_value(SandboxState {
|
||||
permission_profile: Some(turn_context.permission_profile()),
|
||||
sandbox_policy: turn_context.sandbox_policy(),
|
||||
permission_profile: Some(permission_profile),
|
||||
codex_linux_sandbox_exe: turn_context.config.codex_linux_sandbox_exe.clone(),
|
||||
#[allow(deprecated)]
|
||||
sandbox_cwd: turn_context.cwd.to_path_buf(),
|
||||
sandbox_cwd,
|
||||
use_legacy_landlock: turn_context.config.features.use_legacy_landlock(),
|
||||
})?;
|
||||
|
||||
@@ -754,6 +758,24 @@ async fn augment_mcp_tool_request_meta_with_sandbox_state(
|
||||
Ok(meta)
|
||||
}
|
||||
|
||||
fn sandbox_cwd_for_mcp_server(turn_context: &TurnContext, environment_id: &str) -> Option<PathUri> {
|
||||
if let Some(environment) = turn_context
|
||||
.environments
|
||||
.turn_environments
|
||||
.iter()
|
||||
.find(|environment| environment.environment_id == environment_id)
|
||||
{
|
||||
return Some(environment.cwd().clone());
|
||||
}
|
||||
|
||||
if environment_id == codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID {
|
||||
#[allow(deprecated)]
|
||||
return Some(PathUri::from_abs_path(&turn_context.cwd));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
async fn maybe_mark_thread_memory_mode_polluted(
|
||||
sess: &Session,
|
||||
turn_context: &TurnContext,
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::config::ConfigBuilder;
|
||||
use crate::config::ManagedFeatures;
|
||||
use crate::session::tests::make_session_and_context;
|
||||
use crate::session::tests::make_session_and_context_with_rx;
|
||||
use crate::session::turn_context::TurnEnvironment;
|
||||
use crate::state::ActiveTurn;
|
||||
use crate::test_support::models_manager_with_provider;
|
||||
use crate::tools::hook_names::HookToolName;
|
||||
@@ -31,6 +32,7 @@ use codex_rollout_trace::ToolDispatchInvocation;
|
||||
use codex_rollout_trace::ToolDispatchPayload;
|
||||
use codex_rollout_trace::ToolDispatchRequester;
|
||||
use codex_rollout_trace::replay_bundle;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use core_test_support::hooks::trusted_config_layer_stack;
|
||||
use core_test_support::responses::ev_assistant_message;
|
||||
use core_test_support::responses::ev_completed;
|
||||
@@ -1091,6 +1093,39 @@ async fn mcp_tool_call_request_meta_includes_turn_started_at_unix_ms() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mcp_sandbox_cwd_uses_matching_server_environment_uri() -> anyhow::Result<()> {
|
||||
let (_, mut turn_context) = make_session_and_context().await;
|
||||
let secondary_cwd = PathUri::parse("file:///C:/remote/project")?;
|
||||
let environment = turn_context.environments.turn_environments[0]
|
||||
.environment
|
||||
.clone();
|
||||
turn_context
|
||||
.environments
|
||||
.turn_environments
|
||||
.push(TurnEnvironment::new(
|
||||
"remote".to_string(),
|
||||
environment,
|
||||
secondary_cwd.clone(),
|
||||
/*shell*/ None,
|
||||
));
|
||||
|
||||
let sandbox_cwd = sandbox_cwd_for_mcp_server(&turn_context, "remote");
|
||||
|
||||
assert_eq!(sandbox_cwd, Some(secondary_cwd));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mcp_sandbox_cwd_is_none_for_unselected_server_environment() -> anyhow::Result<()> {
|
||||
let (_, turn_context) = make_session_and_context().await;
|
||||
|
||||
let sandbox_cwd = sandbox_cwd_for_mcp_server(&turn_context, "remote");
|
||||
|
||||
assert_eq!(sandbox_cwd, None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn plugin_mcp_tool_call_request_meta_includes_plugin_id() {
|
||||
let (_, turn_context) = make_session_and_context().await;
|
||||
|
||||
Reference in New Issue
Block a user