mirror of
https://github.com/openai/codex.git
synced 2026-05-23 12:34:25 +00:00
Support OAuth options in codex mcp add (#24120)
## Summary - add `--oauth-client-id` and `--oauth-resource` options for streamable HTTP `codex mcp add` registrations - persist those options in MCP server config and use them during the immediate OAuth login flow - cover add-time serialization of both OAuth options in the CLI integration tests ## Testing - `just fmt` - `cargo test -p codex-cli` - `just fix -p codex-cli`
This commit is contained in:
@@ -8,6 +8,7 @@ use anyhow::bail;
|
||||
use clap::ArgGroup;
|
||||
use codex_config::types::AppToolApproval;
|
||||
use codex_config::types::McpServerConfig;
|
||||
use codex_config::types::McpServerOAuthConfig;
|
||||
use codex_config::types::McpServerTransportConfig;
|
||||
use codex_core::McpManager;
|
||||
use codex_core::config::Config;
|
||||
@@ -134,6 +135,14 @@ pub struct AddMcpStreamableHttpArgs {
|
||||
requires = "url"
|
||||
)]
|
||||
pub bearer_token_env_var: Option<String>,
|
||||
|
||||
/// Optional OAuth client identifier to use for this MCP server.
|
||||
#[arg(long = "oauth-client-id", value_name = "CLIENT_ID", requires = "url")]
|
||||
pub oauth_client_id: Option<String>,
|
||||
|
||||
/// Optional OAuth resource parameter to include during MCP login.
|
||||
#[arg(long = "oauth-resource", value_name = "RESOURCE", requires = "url")]
|
||||
pub oauth_resource: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, clap::Parser)]
|
||||
@@ -282,7 +291,7 @@ async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Re
|
||||
.await
|
||||
.with_context(|| format!("failed to load MCP servers from {}", codex_home.display()))?;
|
||||
|
||||
let transport = match transport_args {
|
||||
let (transport, oauth_client_id, oauth_resource) = match transport_args {
|
||||
AddMcpTransportArgs {
|
||||
stdio: Some(stdio), ..
|
||||
} => {
|
||||
@@ -297,27 +306,37 @@ async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Re
|
||||
} else {
|
||||
Some(stdio.env.into_iter().collect::<HashMap<_, _>>())
|
||||
};
|
||||
McpServerTransportConfig::Stdio {
|
||||
command: command_bin,
|
||||
args: command_args,
|
||||
env: env_map,
|
||||
env_vars: Vec::new(),
|
||||
cwd: None,
|
||||
}
|
||||
(
|
||||
McpServerTransportConfig::Stdio {
|
||||
command: command_bin,
|
||||
args: command_args,
|
||||
env: env_map,
|
||||
env_vars: Vec::new(),
|
||||
cwd: None,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
AddMcpTransportArgs {
|
||||
streamable_http:
|
||||
Some(AddMcpStreamableHttpArgs {
|
||||
url,
|
||||
bearer_token_env_var,
|
||||
oauth_client_id,
|
||||
oauth_resource,
|
||||
}),
|
||||
..
|
||||
} => McpServerTransportConfig::StreamableHttp {
|
||||
url,
|
||||
bearer_token_env_var,
|
||||
http_headers: None,
|
||||
env_http_headers: None,
|
||||
},
|
||||
} => (
|
||||
McpServerTransportConfig::StreamableHttp {
|
||||
url,
|
||||
bearer_token_env_var,
|
||||
http_headers: None,
|
||||
env_http_headers: None,
|
||||
},
|
||||
oauth_client_id,
|
||||
oauth_resource,
|
||||
),
|
||||
AddMcpTransportArgs { .. } => bail!("exactly one of --command or --url must be provided"),
|
||||
};
|
||||
|
||||
@@ -334,8 +353,12 @@ async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Re
|
||||
enabled_tools: None,
|
||||
disabled_tools: None,
|
||||
scopes: None,
|
||||
oauth: None,
|
||||
oauth_resource: None,
|
||||
oauth: oauth_client_id
|
||||
.clone()
|
||||
.map(|client_id| McpServerOAuthConfig {
|
||||
client_id: Some(client_id),
|
||||
}),
|
||||
oauth_resource: oauth_resource.clone(),
|
||||
tools: HashMap::new(),
|
||||
};
|
||||
|
||||
@@ -364,8 +387,8 @@ async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Re
|
||||
oauth_config.http_headers,
|
||||
oauth_config.env_http_headers,
|
||||
&resolved_scopes,
|
||||
/*oauth_client_id*/ None,
|
||||
/*oauth_resource*/ None,
|
||||
oauth_client_id.as_deref(),
|
||||
oauth_resource.as_deref(),
|
||||
config.mcp_oauth_callback_port,
|
||||
config.mcp_oauth_callback_url.as_deref(),
|
||||
)
|
||||
|
||||
@@ -198,6 +198,42 @@ async fn add_streamable_http_with_custom_env_var() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn add_streamable_http_with_oauth_options() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
let mut add_cmd = codex_command(codex_home.path())?;
|
||||
add_cmd
|
||||
.args([
|
||||
"mcp",
|
||||
"add",
|
||||
"oauth-server",
|
||||
"--url",
|
||||
"https://example.com/mcp",
|
||||
"--oauth-client-id",
|
||||
"eci-prd-pub-codex-123",
|
||||
"--oauth-resource",
|
||||
"https://resource.example.com",
|
||||
])
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
let servers = load_global_mcp_servers(codex_home.path()).await?;
|
||||
let oauth_server = servers
|
||||
.get("oauth-server")
|
||||
.expect("oauth server should exist");
|
||||
assert_eq!(
|
||||
oauth_server.oauth_client_id(),
|
||||
Some("eci-prd-pub-codex-123")
|
||||
);
|
||||
assert_eq!(
|
||||
oauth_server.oauth_resource.as_deref(),
|
||||
Some("https://resource.example.com")
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn add_streamable_http_rejects_removed_flag() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
Reference in New Issue
Block a user