From da9f0adf755fe6e1b6859e1eae2d7b077c7fff00 Mon Sep 17 00:00:00 2001 From: blevy-oai Date: Wed, 21 Jan 2026 19:35:55 -0800 Subject: [PATCH 1/2] Add MCP server scopes config --- codex-rs/app-server/src/codex_message_processor.rs | 2 ++ codex-rs/cli/src/mcp_cmd.rs | 6 ++++++ codex-rs/core/config.schema.json | 7 +++++++ codex-rs/core/src/config/edit.rs | 11 +++++++++++ codex-rs/core/src/config/mod.rs | 14 ++++++++++++++ codex-rs/core/src/config/types.rs | 8 ++++++++ codex-rs/core/src/mcp_connection_manager.rs | 2 ++ codex-rs/core/tests/suite/rmcp_client.rs | 6 ++++++ codex-rs/core/tests/suite/truncation.rs | 3 +++ 9 files changed, 59 insertions(+) diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 57a13bdf1f0c..aea283973b27 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -2490,6 +2490,8 @@ impl CodexMessageProcessor { } }; + let scopes = scopes.or_else(|| server.scopes.clone()); + match perform_oauth_login_return_url( &name, &url, diff --git a/codex-rs/cli/src/mcp_cmd.rs b/codex-rs/cli/src/mcp_cmd.rs index 22cd18dde3b1..7cc42c4c492f 100644 --- a/codex-rs/cli/src/mcp_cmd.rs +++ b/codex-rs/cli/src/mcp_cmd.rs @@ -247,6 +247,7 @@ async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Re tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }; servers.insert(name.clone(), new_entry); @@ -348,6 +349,11 @@ async fn run_login(config_overrides: &CliConfigOverrides, login_args: LoginArgs) _ => bail!("OAuth login is only supported for streamable HTTP servers."), }; + let mut scopes = scopes; + if scopes.is_empty() { + scopes = server.scopes.clone().unwrap_or_default(); + } + perform_oauth_login( &name, &url, diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index e70841c7d298..ce6fdc665927 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -736,6 +736,13 @@ }, "type": "object" }, + "scopes": { + "default": null, + "items": { + "type": "string" + }, + "type": "array" + }, "startup_timeout_ms": { "default": null, "format": "uint64", diff --git a/codex-rs/core/src/config/edit.rs b/codex-rs/core/src/config/edit.rs index 9c12272d943f..eb4104032bf0 100644 --- a/codex-rs/core/src/config/edit.rs +++ b/codex-rs/core/src/config/edit.rs @@ -164,6 +164,11 @@ mod document_helpers { { entry["disabled_tools"] = array_from_iter(disabled_tools.iter().cloned()); } + if let Some(scopes) = &config.scopes + && !scopes.is_empty() + { + entry["scopes"] = array_from_iter(scopes.iter().cloned()); + } entry } @@ -1360,6 +1365,7 @@ gpt-5 = "gpt-5.1" tool_timeout_sec: None, enabled_tools: Some(vec!["one".to_string(), "two".to_string()]), disabled_tools: None, + scopes: None, }, ); @@ -1382,6 +1388,7 @@ gpt-5 = "gpt-5.1" tool_timeout_sec: None, enabled_tools: None, disabled_tools: Some(vec!["forbidden".to_string()]), + scopes: None, }, ); @@ -1447,6 +1454,7 @@ foo = { command = "cmd" } tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ); @@ -1491,6 +1499,7 @@ foo = { command = "cmd" } # keep me tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ); @@ -1534,6 +1543,7 @@ foo = { command = "cmd", args = ["--flag"] } # keep me tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ); @@ -1578,6 +1588,7 @@ foo = { command = "cmd" } tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ); diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index 4dde7e1ec559..02fc34ed3c74 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -1744,6 +1744,7 @@ mod tests { tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, } } @@ -1761,6 +1762,7 @@ mod tests { tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, } } @@ -2585,6 +2587,7 @@ profile = "project" tool_timeout_sec: Some(Duration::from_secs(5)), enabled_tools: None, disabled_tools: None, + scopes: None, }, ); @@ -2739,6 +2742,7 @@ bearer_token = "secret" tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, )]); @@ -2808,6 +2812,7 @@ ZIG_VAR = "3" tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, )]); @@ -2857,6 +2862,7 @@ ZIG_VAR = "3" tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, )]); @@ -2904,6 +2910,7 @@ ZIG_VAR = "3" tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, )]); @@ -2967,6 +2974,7 @@ startup_timeout_sec = 2.0 tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, )]); apply_blocking( @@ -3042,6 +3050,7 @@ X-Auth = "DOCS_AUTH" tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, )]); @@ -3070,6 +3079,7 @@ X-Auth = "DOCS_AUTH" tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ); apply_blocking( @@ -3136,6 +3146,7 @@ url = "https://example.com/mcp" tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ), ( @@ -3154,6 +3165,7 @@ url = "https://example.com/mcp" tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ), ]); @@ -3235,6 +3247,7 @@ url = "https://example.com/mcp" tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, )]); @@ -3278,6 +3291,7 @@ url = "https://example.com/mcp" tool_timeout_sec: None, enabled_tools: Some(vec!["allowed".to_string()]), disabled_tools: Some(vec!["blocked".to_string()]), + scopes: None, }, )]); diff --git a/codex-rs/core/src/config/types.rs b/codex-rs/core/src/config/types.rs index 350ae49a5bac..ecd306f96ba2 100644 --- a/codex-rs/core/src/config/types.rs +++ b/codex-rs/core/src/config/types.rs @@ -72,6 +72,10 @@ pub struct McpServerConfig { /// Explicit deny-list of tools. These tools will be removed after applying `enabled_tools`. #[serde(default, skip_serializing_if = "Option::is_none")] pub disabled_tools: Option>, + + /// Optional OAuth scopes to request during MCP login. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub scopes: Option>, } // Raw MCP config shape used for deserialization and JSON Schema generation. @@ -112,6 +116,8 @@ pub(crate) struct RawMcpServerConfig { pub enabled_tools: Option>, #[serde(default)] pub disabled_tools: Option>, + #[serde(default)] + pub scopes: Option>, } impl<'de> Deserialize<'de> for McpServerConfig { @@ -133,6 +139,7 @@ impl<'de> Deserialize<'de> for McpServerConfig { let enabled = raw.enabled.unwrap_or_else(default_enabled); let enabled_tools = raw.enabled_tools.clone(); let disabled_tools = raw.disabled_tools.clone(); + let scopes = raw.scopes.clone(); fn throw_if_set(transport: &str, field: &str, value: Option<&T>) -> Result<(), E> where @@ -187,6 +194,7 @@ impl<'de> Deserialize<'de> for McpServerConfig { disabled_reason: None, enabled_tools, disabled_tools, + scopes, }) } } diff --git a/codex-rs/core/src/mcp_connection_manager.rs b/codex-rs/core/src/mcp_connection_manager.rs index 0d638760f556..6a70304b2268 100644 --- a/codex-rs/core/src/mcp_connection_manager.rs +++ b/codex-rs/core/src/mcp_connection_manager.rs @@ -1176,6 +1176,7 @@ mod tests { tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, auth_status: McpAuthStatus::Unsupported, }; @@ -1221,6 +1222,7 @@ mod tests { tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, auth_status: McpAuthStatus::Unsupported, }; diff --git a/codex-rs/core/tests/suite/rmcp_client.rs b/codex-rs/core/tests/suite/rmcp_client.rs index cba1ed622560..fa44699a5257 100644 --- a/codex-rs/core/tests/suite/rmcp_client.rs +++ b/codex-rs/core/tests/suite/rmcp_client.rs @@ -93,6 +93,7 @@ async fn stdio_server_round_trip() -> anyhow::Result<()> { tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ); config @@ -232,6 +233,7 @@ async fn stdio_image_responses_round_trip() -> anyhow::Result<()> { tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ); config @@ -429,6 +431,7 @@ async fn stdio_image_completions_round_trip() -> anyhow::Result<()> { tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ); config @@ -574,6 +577,7 @@ async fn stdio_server_propagates_whitelisted_env_vars() -> anyhow::Result<()> { tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ); config @@ -730,6 +734,7 @@ async fn streamable_http_tool_call_round_trip() -> anyhow::Result<()> { tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ); config @@ -918,6 +923,7 @@ async fn streamable_http_with_oauth_round_trip() -> anyhow::Result<()> { tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ); config diff --git a/codex-rs/core/tests/suite/truncation.rs b/codex-rs/core/tests/suite/truncation.rs index bd4a9a306169..87d76c42fa02 100644 --- a/codex-rs/core/tests/suite/truncation.rs +++ b/codex-rs/core/tests/suite/truncation.rs @@ -431,6 +431,7 @@ async fn mcp_tool_call_output_exceeds_limit_truncated_for_model() -> Result<()> tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ); config @@ -523,6 +524,7 @@ async fn mcp_image_output_preserves_image_and_no_text_summary() -> Result<()> { tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ); config @@ -785,6 +787,7 @@ async fn mcp_tool_call_output_not_truncated_with_custom_limit() -> Result<()> { tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }, ); config From ea8618378b632dc0856c911de9ac3a23695bf898 Mon Sep 17 00:00:00 2001 From: Ben Levy Date: Wed, 21 Jan 2026 19:50:57 -0800 Subject: [PATCH 2/2] Add scopes in initializer where necessary --- codex-rs/tui/src/history_cell.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs index 9ad40e3afefb..1bf43b1f089c 100644 --- a/codex-rs/tui/src/history_cell.rs +++ b/codex-rs/tui/src/history_cell.rs @@ -2001,6 +2001,7 @@ mod tests { tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }; let mut servers = config.mcp_servers.get().clone(); servers.insert("docs".to_string(), stdio_config); @@ -2022,6 +2023,7 @@ mod tests { tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, }; servers.insert("http".to_string(), http_config); config