Chore: remove rmcp feature and exp flag usages (#8087)

### Summary
With codesigning on Mac, Windows and Linux, we should be able to safely
remove `features.rmcp_client` and `use_experimental_use_rmcp_client`
check from the codebase now.
This commit is contained in:
Shijie Rao authored and GitHub committed 2025-12-20 14:18:00 -08:00
1 parent 63942b883c
commit 987dd7fde3
10 files changed
+12 -108

No files matched your search

@@ -1992,16 +1992,6 @@ impl CodexMessageProcessor {
} }
}; };
if !config.features.enabled(Feature::RmcpClient) {
let error = JSONRPCErrorError {
code: INVALID_REQUEST_ERROR_CODE,
message: "OAuth login is only supported when [features].rmcp_client is true in config.toml".to_string(),
data: None,
};
self.outgoing.send_error(request_id, error).await;
return;
}
let McpServerOauthLoginParams { let McpServerOauthLoginParams {
name, name,
scopes, scopes,
+11 -40
View File
@@ -13,15 +13,12 @@ use codex_core::config::find_codex_home;
use codex_core::config::load_global_mcp_servers; use codex_core::config::load_global_mcp_servers;
use codex_core::config::types::McpServerConfig; use codex_core::config::types::McpServerConfig;
use codex_core::config::types::McpServerTransportConfig; use codex_core::config::types::McpServerTransportConfig;
use codex_core::features::Feature;
use codex_core::mcp::auth::compute_auth_statuses; use codex_core::mcp::auth::compute_auth_statuses;
use codex_core::protocol::McpAuthStatus; use codex_core::protocol::McpAuthStatus;
use codex_rmcp_client::delete_oauth_tokens; use codex_rmcp_client::delete_oauth_tokens;
use codex_rmcp_client::perform_oauth_login; use codex_rmcp_client::perform_oauth_login;
use codex_rmcp_client::supports_oauth_login; use codex_rmcp_client::supports_oauth_login;
/// [experimental] Launch Codex as an MCP server or manage configured MCP servers.
///
/// Subcommands: /// Subcommands:
/// - `serve` — run the MCP server on stdio /// - `serve` — run the MCP server on stdio
/// - `list` — list configured servers (with `--json`) /// - `list` — list configured servers (with `--json`)
@@ -39,24 +36,11 @@ pub struct McpCli {
#[derive(Debug, clap::Subcommand)] #[derive(Debug, clap::Subcommand)]
pub enum McpSubcommand { pub enum McpSubcommand {
/// [experimental] List configured MCP servers.
List(ListArgs), List(ListArgs),
/// [experimental] Show details for a configured MCP server.
Get(GetArgs), Get(GetArgs),
/// [experimental] Add a global MCP server entry.
Add(AddArgs), Add(AddArgs),
/// [experimental] Remove a global MCP server entry.
Remove(RemoveArgs), Remove(RemoveArgs),
/// [experimental] Authenticate with a configured MCP server via OAuth.
/// Requires features.rmcp_client = true in config.toml.
Login(LoginArgs), Login(LoginArgs),
/// [experimental] Remove stored OAuth credentials for a server.
/// Requires features.rmcp_client = true in config.toml.
Logout(LogoutArgs), Logout(LogoutArgs),
} }
@@ -282,24 +266,17 @@ async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Re
{ {
match supports_oauth_login(&url).await { match supports_oauth_login(&url).await {
Ok(true) => { Ok(true) => {
if !config.features.enabled(Feature::RmcpClient) { println!("Detected OAuth support. Starting OAuth flow…");
println!( perform_oauth_login(
"MCP server supports login. Add `features.rmcp_client = true` \ &name,
to your config.toml and run `codex mcp login {name}` to login." &url,
); config.mcp_oauth_credentials_store_mode,
} else { http_headers.clone(),
println!("Detected OAuth support. Starting OAuth flow…"); env_http_headers.clone(),
perform_oauth_login( &Vec::new(),
&name, )
&url, .await?;
config.mcp_oauth_credentials_store_mode, println!("Successfully logged in.");
http_headers.clone(),
env_http_headers.clone(),
&Vec::new(),
)
.await?;
println!("Successfully logged in.");
}
} }
Ok(false) => {} Ok(false) => {}
Err(_) => println!( Err(_) => println!(
@@ -352,12 +329,6 @@ async fn run_login(config_overrides: &CliConfigOverrides, login_args: LoginArgs)
.await .await
.context("failed to load configuration")?; .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 LoginArgs { name, scopes } = login_args;
let Some(server) = config.mcp_servers.get(&name) else { let Some(server) = config.mcp_servers.get(&name) else {
-14
View File
@@ -322,10 +322,6 @@ pub struct Config {
/// If set to `true`, used only the experimental unified exec tool. /// If set to `true`, used only the experimental unified exec tool.
pub use_experimental_unified_exec_tool: bool, 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,
/// Settings for ghost snapshots (used for undo). /// Settings for ghost snapshots (used for undo).
pub ghost_snapshot: GhostSnapshotConfig, pub ghost_snapshot: GhostSnapshotConfig,
@@ -827,7 +823,6 @@ pub struct ConfigToml {
pub experimental_instructions_file: Option<AbsolutePathBuf>, pub experimental_instructions_file: Option<AbsolutePathBuf>,
pub experimental_compact_prompt_file: Option<AbsolutePathBuf>, pub experimental_compact_prompt_file: Option<AbsolutePathBuf>,
pub experimental_use_unified_exec_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_use_freeform_apply_patch: Option<bool>,
/// Preferred OSS provider for local models, e.g. "lmstudio" or "ollama". /// Preferred OSS provider for local models, e.g. "lmstudio" or "ollama".
pub oss_provider: Option<String>, pub oss_provider: Option<String>,
@@ -1243,7 +1238,6 @@ impl Config {
let include_apply_patch_tool_flag = features.enabled(Feature::ApplyPatchFreeform); let include_apply_patch_tool_flag = features.enabled(Feature::ApplyPatchFreeform);
let tools_web_search_request = features.enabled(Feature::WebSearchRequest); let tools_web_search_request = features.enabled(Feature::WebSearchRequest);
let use_experimental_unified_exec_tool = features.enabled(Feature::UnifiedExec); let use_experimental_unified_exec_tool = features.enabled(Feature::UnifiedExec);
let use_experimental_use_rmcp_client = features.enabled(Feature::RmcpClient);
let forced_chatgpt_workspace_id = let forced_chatgpt_workspace_id =
cfg.forced_chatgpt_workspace_id.as_ref().and_then(|value| { cfg.forced_chatgpt_workspace_id.as_ref().and_then(|value| {
@@ -1383,7 +1377,6 @@ impl Config {
include_apply_patch_tool: include_apply_patch_tool_flag, include_apply_patch_tool: include_apply_patch_tool_flag,
tools_web_search_request, tools_web_search_request,
use_experimental_unified_exec_tool, use_experimental_unified_exec_tool,
use_experimental_use_rmcp_client,
ghost_snapshot, ghost_snapshot,
features, features,
active_profile: active_profile_name, active_profile: active_profile_name,
@@ -1997,7 +1990,6 @@ trust_level = "trusted"
let codex_home = TempDir::new()?; let codex_home = TempDir::new()?;
let cfg = ConfigToml { let cfg = ConfigToml {
experimental_use_unified_exec_tool: Some(true), experimental_use_unified_exec_tool: Some(true),
experimental_use_rmcp_client: Some(true),
experimental_use_freeform_apply_patch: Some(true), experimental_use_freeform_apply_patch: Some(true),
..Default::default() ..Default::default()
}; };
@@ -2010,12 +2002,10 @@ trust_level = "trusted"
assert!(config.features.enabled(Feature::ApplyPatchFreeform)); assert!(config.features.enabled(Feature::ApplyPatchFreeform));
assert!(config.features.enabled(Feature::UnifiedExec)); assert!(config.features.enabled(Feature::UnifiedExec));
assert!(config.features.enabled(Feature::RmcpClient));
assert!(config.include_apply_patch_tool); assert!(config.include_apply_patch_tool);
assert!(config.use_experimental_unified_exec_tool); assert!(config.use_experimental_unified_exec_tool);
assert!(config.use_experimental_use_rmcp_client);
Ok(()) Ok(())
} }
@@ -3196,7 +3186,6 @@ model_verbosity = "high"
include_apply_patch_tool: false, include_apply_patch_tool: false,
tools_web_search_request: false, tools_web_search_request: false,
use_experimental_unified_exec_tool: false, use_experimental_unified_exec_tool: false,
use_experimental_use_rmcp_client: false,
ghost_snapshot: GhostSnapshotConfig::default(), ghost_snapshot: GhostSnapshotConfig::default(),
features: Features::with_defaults(), features: Features::with_defaults(),
active_profile: Some("o3".to_string()), active_profile: Some("o3".to_string()),
@@ -3280,7 +3269,6 @@ model_verbosity = "high"
include_apply_patch_tool: false, include_apply_patch_tool: false,
tools_web_search_request: false, tools_web_search_request: false,
use_experimental_unified_exec_tool: false, use_experimental_unified_exec_tool: false,
use_experimental_use_rmcp_client: false,
ghost_snapshot: GhostSnapshotConfig::default(), ghost_snapshot: GhostSnapshotConfig::default(),
features: Features::with_defaults(), features: Features::with_defaults(),
active_profile: Some("gpt3".to_string()), active_profile: Some("gpt3".to_string()),
@@ -3379,7 +3367,6 @@ model_verbosity = "high"
include_apply_patch_tool: false, include_apply_patch_tool: false,
tools_web_search_request: false, tools_web_search_request: false,
use_experimental_unified_exec_tool: false, use_experimental_unified_exec_tool: false,
use_experimental_use_rmcp_client: false,
ghost_snapshot: GhostSnapshotConfig::default(), ghost_snapshot: GhostSnapshotConfig::default(),
features: Features::with_defaults(), features: Features::with_defaults(),
active_profile: Some("zdr".to_string()), active_profile: Some("zdr".to_string()),
@@ -3464,7 +3451,6 @@ model_verbosity = "high"
include_apply_patch_tool: false, include_apply_patch_tool: false,
tools_web_search_request: false, tools_web_search_request: false,
use_experimental_unified_exec_tool: false, use_experimental_unified_exec_tool: false,
use_experimental_use_rmcp_client: false,
ghost_snapshot: GhostSnapshotConfig::default(), ghost_snapshot: GhostSnapshotConfig::default(),
features: Features::with_defaults(), features: Features::with_defaults(),
active_profile: Some("gpt5".to_string()), active_profile: Some("gpt5".to_string()),
-1
View File
@@ -25,7 +25,6 @@ pub struct ConfigProfile {
pub experimental_compact_prompt_file: Option<AbsolutePathBuf>, pub experimental_compact_prompt_file: Option<AbsolutePathBuf>,
pub include_apply_patch_tool: Option<bool>, pub include_apply_patch_tool: Option<bool>,
pub experimental_use_unified_exec_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_use_freeform_apply_patch: Option<bool>,
pub tools_web_search: Option<bool>, pub tools_web_search: Option<bool>,
pub tools_view_image: Option<bool>, pub tools_view_image: Option<bool>,
-11
View File
@@ -69,8 +69,6 @@ pub enum Feature {
// Experimental // Experimental
/// Use the single unified PTY-backed exec tool. /// Use the single unified PTY-backed exec tool.
UnifiedExec, UnifiedExec,
/// Enable experimental RMCP features such as OAuth login.
RmcpClient,
/// Include the freeform apply_patch tool. /// Include the freeform apply_patch tool.
ApplyPatchFreeform, ApplyPatchFreeform,
/// Allow the model to request web searches. /// Allow the model to request web searches.
@@ -226,7 +224,6 @@ impl Features {
let base_legacy = LegacyFeatureToggles { let base_legacy = LegacyFeatureToggles {
experimental_use_freeform_apply_patch: cfg.experimental_use_freeform_apply_patch, experimental_use_freeform_apply_patch: cfg.experimental_use_freeform_apply_patch,
experimental_use_unified_exec_tool: cfg.experimental_use_unified_exec_tool, 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_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), tools_view_image: cfg.tools.as_ref().and_then(|t| t.view_image),
..Default::default() ..Default::default()
@@ -243,7 +240,6 @@ impl Features {
.experimental_use_freeform_apply_patch, .experimental_use_freeform_apply_patch,
experimental_use_unified_exec_tool: config_profile.experimental_use_unified_exec_tool, 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_web_search: config_profile.tools_web_search,
tools_view_image: config_profile.tools_view_image, tools_view_image: config_profile.tools_view_image,
}; };
@@ -348,13 +344,6 @@ pub const FEATURES: &[FeatureSpec] = &[
}, },
default_enabled: false, default_enabled: false,
}, },
// Unstable features.
FeatureSpec {
id: Feature::RmcpClient,
key: "rmcp_client",
stage: Stage::Experimental,
default_enabled: false,
},
FeatureSpec { FeatureSpec {
id: Feature::ApplyPatchFreeform, id: Feature::ApplyPatchFreeform,
key: "apply_patch_freeform", key: "apply_patch_freeform",
-11
View File
@@ -17,10 +17,6 @@ const ALIASES: &[Alias] = &[
legacy_key: "experimental_use_unified_exec_tool", legacy_key: "experimental_use_unified_exec_tool",
feature: Feature::UnifiedExec, feature: Feature::UnifiedExec,
}, },
Alias {
legacy_key: "experimental_use_rmcp_client",
feature: Feature::RmcpClient,
},
Alias { Alias {
legacy_key: "experimental_use_freeform_apply_patch", legacy_key: "experimental_use_freeform_apply_patch",
feature: Feature::ApplyPatchFreeform, feature: Feature::ApplyPatchFreeform,
@@ -50,7 +46,6 @@ pub struct LegacyFeatureToggles {
pub include_apply_patch_tool: Option<bool>, pub include_apply_patch_tool: Option<bool>,
pub experimental_use_freeform_apply_patch: Option<bool>, pub experimental_use_freeform_apply_patch: Option<bool>,
pub experimental_use_unified_exec_tool: 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_web_search: Option<bool>,
pub tools_view_image: Option<bool>, pub tools_view_image: Option<bool>,
} }
@@ -75,12 +70,6 @@ impl LegacyFeatureToggles {
self.experimental_use_unified_exec_tool, self.experimental_use_unified_exec_tool,
"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( set_if_some(
features, features,
Feature::WebSearchRequest, Feature::WebSearchRequest,
-7
View File
@@ -10,7 +10,6 @@ use std::time::UNIX_EPOCH;
use codex_core::config::types::McpServerConfig; use codex_core::config::types::McpServerConfig;
use codex_core::config::types::McpServerTransportConfig; use codex_core::config::types::McpServerTransportConfig;
use codex_core::features::Feature;
use codex_core::protocol::AskForApproval; use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg; use codex_core::protocol::EventMsg;
@@ -79,7 +78,6 @@ async fn stdio_server_round_trip() -> anyhow::Result<()> {
let fixture = test_codex() let fixture = test_codex()
.with_config(move |config| { .with_config(move |config| {
config.features.enable(Feature::RmcpClient);
config.mcp_servers.insert( config.mcp_servers.insert(
server_name.to_string(), server_name.to_string(),
McpServerConfig { McpServerConfig {
@@ -217,7 +215,6 @@ async fn stdio_image_responses_round_trip() -> anyhow::Result<()> {
let fixture = test_codex() let fixture = test_codex()
.with_config(move |config| { .with_config(move |config| {
config.features.enable(Feature::RmcpClient);
config.mcp_servers.insert( config.mcp_servers.insert(
server_name.to_string(), server_name.to_string(),
McpServerConfig { McpServerConfig {
@@ -413,7 +410,6 @@ async fn stdio_image_completions_round_trip() -> anyhow::Result<()> {
let fixture = test_codex() let fixture = test_codex()
.with_config(move |config| { .with_config(move |config| {
config.model_provider.wire_api = codex_core::WireApi::Chat; config.model_provider.wire_api = codex_core::WireApi::Chat;
config.features.enable(Feature::RmcpClient);
config.mcp_servers.insert( config.mcp_servers.insert(
server_name.to_string(), server_name.to_string(),
McpServerConfig { McpServerConfig {
@@ -560,7 +556,6 @@ async fn stdio_server_propagates_whitelisted_env_vars() -> anyhow::Result<()> {
let fixture = test_codex() let fixture = test_codex()
.with_config(move |config| { .with_config(move |config| {
config.features.enable(Feature::RmcpClient);
config.mcp_servers.insert( config.mcp_servers.insert(
server_name.to_string(), server_name.to_string(),
McpServerConfig { McpServerConfig {
@@ -710,7 +705,6 @@ async fn streamable_http_tool_call_round_trip() -> anyhow::Result<()> {
let fixture = test_codex() let fixture = test_codex()
.with_config(move |config| { .with_config(move |config| {
config.features.enable(Feature::RmcpClient);
config.mcp_servers.insert( config.mcp_servers.insert(
server_name.to_string(), server_name.to_string(),
McpServerConfig { McpServerConfig {
@@ -891,7 +885,6 @@ async fn streamable_http_with_oauth_round_trip() -> anyhow::Result<()> {
let fixture = test_codex() let fixture = test_codex()
.with_config(move |config| { .with_config(move |config| {
config.features.enable(Feature::RmcpClient);
config.mcp_servers.insert( config.mcp_servers.insert(
server_name.to_string(), server_name.to_string(),
McpServerConfig { McpServerConfig {
-4
View File
@@ -5,7 +5,6 @@ use anyhow::Context;
use anyhow::Result; use anyhow::Result;
use codex_core::config::types::McpServerConfig; use codex_core::config::types::McpServerConfig;
use codex_core::config::types::McpServerTransportConfig; use codex_core::config::types::McpServerTransportConfig;
use codex_core::features::Feature;
use codex_core::protocol::AskForApproval; use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg; use codex_core::protocol::EventMsg;
use codex_core::protocol::Op; use codex_core::protocol::Op;
@@ -421,7 +420,6 @@ async fn mcp_tool_call_output_exceeds_limit_truncated_for_model() -> Result<()>
.into_owned(); .into_owned();
let mut builder = test_codex().with_config(move |config| { let mut builder = test_codex().with_config(move |config| {
config.features.enable(Feature::RmcpClient);
config.mcp_servers.insert( config.mcp_servers.insert(
server_name.to_string(), server_name.to_string(),
codex_core::config::types::McpServerConfig { codex_core::config::types::McpServerConfig {
@@ -511,7 +509,6 @@ async fn mcp_image_output_preserves_image_and_no_text_summary() -> Result<()> {
let openai_png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMB/ee9bQAAAABJRU5ErkJggg=="; let openai_png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMB/ee9bQAAAABJRU5ErkJggg==";
let mut builder = test_codex().with_config(move |config| { let mut builder = test_codex().with_config(move |config| {
config.features.enable(Feature::RmcpClient);
config.mcp_servers.insert( config.mcp_servers.insert(
server_name.to_string(), server_name.to_string(),
McpServerConfig { McpServerConfig {
@@ -774,7 +771,6 @@ async fn mcp_tool_call_output_not_truncated_with_custom_limit() -> Result<()> {
.into_owned(); .into_owned();
let mut builder = test_codex().with_config(move |config| { let mut builder = test_codex().with_config(move |config| {
config.features.enable(Feature::RmcpClient);
config.tool_output_token_limit = Some(50_000); config.tool_output_token_limit = Some(50_000);
config.mcp_servers.insert( config.mcp_servers.insert(
server_name.to_string(), server_name.to_string(),
+1 -9
View File
@@ -42,7 +42,6 @@ Supported features:
| Key | Default | Stage | Description | | Key | Default | Stage | Description |
| ------------------------------------- | :-----: | ------------ | ----------------------------------------------------- | | ------------------------------------- | :-----: | ------------ | ----------------------------------------------------- |
| `unified_exec` | false | Experimental | Use the unified PTY-backed exec tool | | `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 | | `apply_patch_freeform` | false | Beta | Include the freeform `apply_patch` tool |
| `view_image_tool` | true | Stable | Include the `view_image` tool | | `view_image_tool` | true | Stable | Include the `view_image` tool |
| `web_search_request` | false | Stable | Allow the model to issue web searches | | `web_search_request` | false | Stable | Allow the model to issue web searches |
@@ -465,14 +464,7 @@ http_headers = { "HEADER_NAME" = "HEADER_VALUE" }
env_http_headers = { "HEADER_NAME" = "ENV_VAR" } 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 `rmcp_client = true` flag: Streamable HTTP connections always use the Rust MCP client under the hood. Run `codex mcp login <server-name>` to authenticate for servers supporting OAuth.
```toml
[features]
rmcp_client = true
```
After enabling it, run `codex mcp login <server-name>` when the server supports OAuth.
#### Other configuration options #### Other configuration options
-1
View File
@@ -214,7 +214,6 @@ view_image = true
[features] [features]
# Leave this table empty to accept defaults. Set explicit booleans to opt in/out. # Leave this table empty to accept defaults. Set explicit booleans to opt in/out.
unified_exec = false unified_exec = false
rmcp_client = false
apply_patch_freeform = false apply_patch_freeform = false
view_image_tool = true view_image_tool = true
web_search_request = false web_search_request = false