Compare commits

...

1 Commits

Author SHA1 Message Date
Michael Bolin
c614258961 fix: retire Feature.RmcpClient 2025-12-03 16:58:20 -08:00
7 changed files with 61 additions and 83 deletions

View File

@@ -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 {

View File

@@ -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 },

View File

@@ -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>,

View File

@@ -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));
}
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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