mirror of
https://github.com/openai/codex.git
synced 2026-02-02 06:57:03 +00:00
Compare commits
1 Commits
bazelversi
...
pr7559
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c614258961 |
@@ -14,7 +14,6 @@ use codex_core::config::find_codex_home;
|
||||
use codex_core::config::load_global_mcp_servers;
|
||||
use codex_core::config::types::McpServerConfig;
|
||||
use codex_core::config::types::McpServerTransportConfig;
|
||||
use codex_core::features::Feature;
|
||||
use codex_core::mcp::auth::compute_auth_statuses;
|
||||
use codex_core::protocol::McpAuthStatus;
|
||||
use codex_rmcp_client::delete_oauth_tokens;
|
||||
@@ -53,11 +52,9 @@ pub enum McpSubcommand {
|
||||
Remove(RemoveArgs),
|
||||
|
||||
/// [experimental] Authenticate with a configured MCP server via OAuth.
|
||||
/// Requires experimental_use_rmcp_client = true in config.toml.
|
||||
Login(LoginArgs),
|
||||
|
||||
/// [experimental] Remove stored OAuth credentials for a server.
|
||||
/// Requires experimental_use_rmcp_client = true in config.toml.
|
||||
Logout(LogoutArgs),
|
||||
}
|
||||
|
||||
@@ -283,24 +280,17 @@ async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Re
|
||||
{
|
||||
match supports_oauth_login(&url).await {
|
||||
Ok(true) => {
|
||||
if !config.features.enabled(Feature::RmcpClient) {
|
||||
println!(
|
||||
"MCP server supports login. Add `experimental_use_rmcp_client = true` \
|
||||
to your config.toml and run `codex mcp login {name}` to login."
|
||||
);
|
||||
} else {
|
||||
println!("Detected OAuth support. Starting OAuth flow…");
|
||||
perform_oauth_login(
|
||||
&name,
|
||||
&url,
|
||||
config.mcp_oauth_credentials_store_mode,
|
||||
http_headers.clone(),
|
||||
env_http_headers.clone(),
|
||||
&Vec::new(),
|
||||
)
|
||||
.await?;
|
||||
println!("Successfully logged in.");
|
||||
}
|
||||
println!("Detected OAuth support. Starting OAuth flow…");
|
||||
perform_oauth_login(
|
||||
&name,
|
||||
&url,
|
||||
config.mcp_oauth_credentials_store_mode,
|
||||
http_headers.clone(),
|
||||
env_http_headers.clone(),
|
||||
&Vec::new(),
|
||||
)
|
||||
.await?;
|
||||
println!("Successfully logged in.");
|
||||
}
|
||||
Ok(false) => {}
|
||||
Err(_) => println!(
|
||||
@@ -353,12 +343,6 @@ async fn run_login(config_overrides: &CliConfigOverrides, login_args: LoginArgs)
|
||||
.await
|
||||
.context("failed to load configuration")?;
|
||||
|
||||
if !config.features.enabled(Feature::RmcpClient) {
|
||||
bail!(
|
||||
"OAuth login is only supported when [features].rmcp_client is true in config.toml. See https://github.com/openai/codex/blob/main/docs/config.md#feature-flags for details."
|
||||
);
|
||||
}
|
||||
|
||||
let LoginArgs { name, scopes } = login_args;
|
||||
|
||||
let Some(server) = config.mcp_servers.get(&name) else {
|
||||
|
||||
@@ -251,10 +251,6 @@ pub struct Config {
|
||||
/// If set to `true`, used only the experimental unified exec tool.
|
||||
pub use_experimental_unified_exec_tool: bool,
|
||||
|
||||
/// If set to `true`, use the experimental official Rust MCP client.
|
||||
/// https://github.com/modelcontextprotocol/rust-sdk
|
||||
pub use_experimental_use_rmcp_client: bool,
|
||||
|
||||
/// Centralized feature flags; source of truth for feature gating.
|
||||
pub features: Features,
|
||||
|
||||
@@ -718,7 +714,6 @@ pub struct ConfigToml {
|
||||
pub experimental_instructions_file: Option<PathBuf>,
|
||||
pub experimental_compact_prompt_file: Option<PathBuf>,
|
||||
pub experimental_use_unified_exec_tool: Option<bool>,
|
||||
pub experimental_use_rmcp_client: Option<bool>,
|
||||
pub experimental_use_freeform_apply_patch: Option<bool>,
|
||||
pub experimental_sandbox_command_assessment: Option<bool>,
|
||||
/// Preferred OSS provider for local models, e.g. "lmstudio" or "ollama".
|
||||
@@ -1088,7 +1083,6 @@ impl Config {
|
||||
let include_apply_patch_tool_flag = features.enabled(Feature::ApplyPatchFreeform);
|
||||
let tools_web_search_request = features.enabled(Feature::WebSearchRequest);
|
||||
let use_experimental_unified_exec_tool = features.enabled(Feature::UnifiedExec);
|
||||
let use_experimental_use_rmcp_client = features.enabled(Feature::RmcpClient);
|
||||
let experimental_sandbox_command_assessment =
|
||||
features.enabled(Feature::SandboxCommandAssessment);
|
||||
|
||||
@@ -1241,7 +1235,6 @@ impl Config {
|
||||
tools_web_search_request,
|
||||
experimental_sandbox_command_assessment,
|
||||
use_experimental_unified_exec_tool,
|
||||
use_experimental_use_rmcp_client,
|
||||
features,
|
||||
active_profile: active_profile_name,
|
||||
active_project,
|
||||
@@ -1809,7 +1802,6 @@ trust_level = "trusted"
|
||||
let codex_home = TempDir::new()?;
|
||||
let cfg = ConfigToml {
|
||||
experimental_use_unified_exec_tool: Some(true),
|
||||
experimental_use_rmcp_client: Some(true),
|
||||
experimental_use_freeform_apply_patch: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
@@ -1822,12 +1814,10 @@ trust_level = "trusted"
|
||||
|
||||
assert!(config.features.enabled(Feature::ApplyPatchFreeform));
|
||||
assert!(config.features.enabled(Feature::UnifiedExec));
|
||||
assert!(config.features.enabled(Feature::RmcpClient));
|
||||
|
||||
assert!(config.include_apply_patch_tool);
|
||||
|
||||
assert!(config.use_experimental_unified_exec_tool);
|
||||
assert!(config.use_experimental_use_rmcp_client);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -2994,7 +2984,6 @@ model_verbosity = "high"
|
||||
tools_web_search_request: false,
|
||||
experimental_sandbox_command_assessment: false,
|
||||
use_experimental_unified_exec_tool: false,
|
||||
use_experimental_use_rmcp_client: false,
|
||||
features: Features::with_defaults(),
|
||||
active_profile: Some("o3".to_string()),
|
||||
active_project: ProjectConfig { trust_level: None },
|
||||
@@ -3068,7 +3057,6 @@ model_verbosity = "high"
|
||||
tools_web_search_request: false,
|
||||
experimental_sandbox_command_assessment: false,
|
||||
use_experimental_unified_exec_tool: false,
|
||||
use_experimental_use_rmcp_client: false,
|
||||
features: Features::with_defaults(),
|
||||
active_profile: Some("gpt3".to_string()),
|
||||
active_project: ProjectConfig { trust_level: None },
|
||||
@@ -3157,7 +3145,6 @@ model_verbosity = "high"
|
||||
tools_web_search_request: false,
|
||||
experimental_sandbox_command_assessment: false,
|
||||
use_experimental_unified_exec_tool: false,
|
||||
use_experimental_use_rmcp_client: false,
|
||||
features: Features::with_defaults(),
|
||||
active_profile: Some("zdr".to_string()),
|
||||
active_project: ProjectConfig { trust_level: None },
|
||||
@@ -3232,7 +3219,6 @@ model_verbosity = "high"
|
||||
tools_web_search_request: false,
|
||||
experimental_sandbox_command_assessment: false,
|
||||
use_experimental_unified_exec_tool: false,
|
||||
use_experimental_use_rmcp_client: false,
|
||||
features: Features::with_defaults(),
|
||||
active_profile: Some("gpt5".to_string()),
|
||||
active_project: ProjectConfig { trust_level: None },
|
||||
|
||||
@@ -25,7 +25,6 @@ pub struct ConfigProfile {
|
||||
pub experimental_compact_prompt_file: Option<PathBuf>,
|
||||
pub include_apply_patch_tool: Option<bool>,
|
||||
pub experimental_use_unified_exec_tool: Option<bool>,
|
||||
pub experimental_use_rmcp_client: Option<bool>,
|
||||
pub experimental_use_freeform_apply_patch: Option<bool>,
|
||||
pub experimental_sandbox_command_assessment: Option<bool>,
|
||||
pub tools_web_search: Option<bool>,
|
||||
|
||||
@@ -125,7 +125,7 @@ impl Features {
|
||||
}
|
||||
|
||||
pub fn enabled(&self, f: Feature) -> bool {
|
||||
self.enabled.contains(&f)
|
||||
f == Feature::RmcpClient || self.enabled.contains(&f)
|
||||
}
|
||||
|
||||
pub fn enable(&mut self, f: Feature) -> &mut Self {
|
||||
@@ -134,7 +134,9 @@ impl Features {
|
||||
}
|
||||
|
||||
pub fn disable(&mut self, f: Feature) -> &mut Self {
|
||||
self.enabled.remove(&f);
|
||||
if f != Feature::RmcpClient {
|
||||
self.enabled.remove(&f);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
@@ -162,6 +164,18 @@ impl Features {
|
||||
pub fn apply_map(&mut self, m: &BTreeMap<String, bool>) {
|
||||
for (k, v) in m {
|
||||
match feature_for_key(k) {
|
||||
Some(Feature::RmcpClient) => {
|
||||
if k != Feature::RmcpClient.key() {
|
||||
self.record_legacy_usage(k.as_str(), Feature::RmcpClient);
|
||||
}
|
||||
if !*v {
|
||||
tracing::warn!(
|
||||
"[features].{k} is ignored; the RMCP client is always enabled"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
self.enable(Feature::RmcpClient);
|
||||
}
|
||||
Some(feat) => {
|
||||
if k != feat.key() {
|
||||
self.record_legacy_usage(k.as_str(), feat);
|
||||
@@ -190,7 +204,6 @@ impl Features {
|
||||
experimental_sandbox_command_assessment: cfg.experimental_sandbox_command_assessment,
|
||||
experimental_use_freeform_apply_patch: cfg.experimental_use_freeform_apply_patch,
|
||||
experimental_use_unified_exec_tool: cfg.experimental_use_unified_exec_tool,
|
||||
experimental_use_rmcp_client: cfg.experimental_use_rmcp_client,
|
||||
tools_web_search: cfg.tools.as_ref().and_then(|t| t.web_search),
|
||||
tools_view_image: cfg.tools.as_ref().and_then(|t| t.view_image),
|
||||
..Default::default()
|
||||
@@ -209,7 +222,6 @@ impl Features {
|
||||
.experimental_use_freeform_apply_patch,
|
||||
|
||||
experimental_use_unified_exec_tool: config_profile.experimental_use_unified_exec_tool,
|
||||
experimental_use_rmcp_client: config_profile.experimental_use_rmcp_client,
|
||||
tools_web_search: config_profile.tools_web_search,
|
||||
tools_view_image: config_profile.tools_view_image,
|
||||
};
|
||||
@@ -275,6 +287,12 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
stage: Stage::Stable,
|
||||
default_enabled: true,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::RmcpClient,
|
||||
key: "rmcp_client",
|
||||
stage: Stage::Stable,
|
||||
default_enabled: true,
|
||||
},
|
||||
// Unstable features.
|
||||
FeatureSpec {
|
||||
id: Feature::UnifiedExec,
|
||||
@@ -282,12 +300,6 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
stage: Stage::Experimental,
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::RmcpClient,
|
||||
key: "rmcp_client",
|
||||
stage: Stage::Experimental,
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::ApplyPatchFreeform,
|
||||
key: "apply_patch_freeform",
|
||||
@@ -343,3 +355,25 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
default_enabled: false,
|
||||
},
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[test]
|
||||
fn rmcp_client_feature_is_always_enabled() {
|
||||
let mut features = Features::with_defaults();
|
||||
assert!(features.enabled(Feature::RmcpClient));
|
||||
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert("rmcp_client".to_string(), false);
|
||||
features.apply_map(&map);
|
||||
|
||||
assert!(features.enabled(Feature::RmcpClient));
|
||||
|
||||
features.disable(Feature::RmcpClient);
|
||||
|
||||
assert!(features.enabled(Feature::RmcpClient));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,6 @@ pub struct LegacyFeatureToggles {
|
||||
pub experimental_sandbox_command_assessment: Option<bool>,
|
||||
pub experimental_use_freeform_apply_patch: Option<bool>,
|
||||
pub experimental_use_unified_exec_tool: Option<bool>,
|
||||
pub experimental_use_rmcp_client: Option<bool>,
|
||||
pub tools_web_search: Option<bool>,
|
||||
pub tools_view_image: Option<bool>,
|
||||
}
|
||||
@@ -82,12 +81,6 @@ impl LegacyFeatureToggles {
|
||||
self.experimental_use_unified_exec_tool,
|
||||
"experimental_use_unified_exec_tool",
|
||||
);
|
||||
set_if_some(
|
||||
features,
|
||||
Feature::RmcpClient,
|
||||
self.experimental_use_rmcp_client,
|
||||
"experimental_use_rmcp_client",
|
||||
);
|
||||
set_if_some(
|
||||
features,
|
||||
Feature::WebSearchRequest,
|
||||
|
||||
@@ -42,7 +42,6 @@ Supported features:
|
||||
| Key | Default | Stage | Description |
|
||||
| ----------------------------------------- | :-----: | ------------ | ---------------------------------------------------- |
|
||||
| `unified_exec` | false | Experimental | Use the unified PTY-backed exec tool |
|
||||
| `rmcp_client` | false | Experimental | Enable oauth support for streamable HTTP MCP servers |
|
||||
| `apply_patch_freeform` | false | Beta | Include the freeform `apply_patch` tool |
|
||||
| `view_image_tool` | true | Stable | Include the `view_image` tool |
|
||||
| `web_search_request` | false | Stable | Allow the model to issue web searches |
|
||||
@@ -53,6 +52,8 @@ Supported features:
|
||||
Notes:
|
||||
|
||||
- Omit a key to accept its default.
|
||||
- The RMCP client is always enabled; legacy `rmcp_client` and `experimental_use_rmcp_client`
|
||||
toggles are ignored.
|
||||
- Legacy booleans such as `experimental_use_exec_command_tool`, `experimental_use_unified_exec_tool`, `include_apply_patch_tool`, and similar `experimental_use_*` keys are deprecated; setting the corresponding `[features].<key>` avoids repeated warnings.
|
||||
|
||||
## Model selection
|
||||
@@ -464,13 +465,9 @@ http_headers = { "HEADER_NAME" = "HEADER_VALUE" }
|
||||
env_http_headers = { "HEADER_NAME" = "ENV_VAR" }
|
||||
```
|
||||
|
||||
Streamable HTTP connections always use the experimental Rust MCP client under the hood, so expect occasional rough edges. OAuth login flows are gated on the `experimental_use_rmcp_client = true` flag:
|
||||
|
||||
```toml
|
||||
experimental_use_rmcp_client = true
|
||||
```
|
||||
|
||||
After enabling it, run `codex mcp login <server-name>` when the server supports OAuth.
|
||||
Streamable HTTP connections always use the Rust MCP client under the hood. OAuth login
|
||||
flows are supported by default; run `codex mcp login <server-name>` when the server
|
||||
supports OAuth to store credentials.
|
||||
|
||||
#### Other configuration options
|
||||
|
||||
@@ -489,17 +486,6 @@ disabled_tools = ["search"]
|
||||
|
||||
When both `enabled_tools` and `disabled_tools` are specified, Codex first restricts the server to the allow-list and then removes any tools that appear in the deny-list.
|
||||
|
||||
#### Experimental RMCP client
|
||||
|
||||
This flag enables OAuth support for streamable HTTP servers.
|
||||
|
||||
```toml
|
||||
experimental_use_rmcp_client = true
|
||||
|
||||
[mcp_servers.server_name]
|
||||
…
|
||||
```
|
||||
|
||||
#### MCP CLI commands
|
||||
|
||||
```shell
|
||||
|
||||
@@ -214,13 +214,13 @@ view_image = true
|
||||
[features]
|
||||
# Leave this table empty to accept defaults. Set explicit booleans to opt in/out.
|
||||
unified_exec = false
|
||||
rmcp_client = false
|
||||
apply_patch_freeform = false
|
||||
view_image_tool = true
|
||||
web_search_request = false
|
||||
experimental_sandbox_command_assessment = false
|
||||
ghost_commit = false
|
||||
enable_experimental_windows_sandbox = false
|
||||
# The RMCP client is always enabled; legacy toggles are ignored.
|
||||
|
||||
################################################################################
|
||||
# Experimental toggles (legacy; prefer [features])
|
||||
@@ -229,9 +229,6 @@ enable_experimental_windows_sandbox = false
|
||||
# Use experimental unified exec tool. Default: false
|
||||
experimental_use_unified_exec_tool = false
|
||||
|
||||
# Use experimental Rust MCP client (enables OAuth for HTTP MCP). Default: false
|
||||
experimental_use_rmcp_client = false
|
||||
|
||||
# Include apply_patch via freeform editing path (affects default tool set). Default: false
|
||||
experimental_use_freeform_apply_patch = false
|
||||
|
||||
@@ -320,7 +317,6 @@ experimental_use_freeform_apply_patch = false
|
||||
# experimental_compact_prompt_file = "compact_prompt.txt"
|
||||
# include_apply_patch_tool = false
|
||||
# experimental_use_unified_exec_tool = false
|
||||
# experimental_use_rmcp_client = false
|
||||
# experimental_use_freeform_apply_patch = false
|
||||
# experimental_sandbox_command_assessment = false
|
||||
# tools_web_search = false
|
||||
|
||||
Reference in New Issue
Block a user