Skip to content

Commit 873b0dd

Browse files
Merge remote-tracking branch 'origin/main' into nornagon/xterm-colors
2 parents ceaaf69 + 687a13b commit 873b0dd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2027
-490
lines changed

codex-rs/Cargo.lock

Lines changed: 21 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

codex-rs/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,9 @@ tracing = "0.1.41"
175175
tracing-appender = "0.2.3"
176176
tracing-subscriber = "0.3.20"
177177
tracing-test = "0.2.5"
178-
tree-sitter = "0.25.9"
179-
tree-sitter-bash = "0.25.0"
178+
tree-sitter = "0.25.10"
179+
tree-sitter-bash = "0.25"
180+
tree-sitter-highlight = "0.25.10"
180181
ts-rs = "11"
181182
unicode-segmentation = "1.12.0"
182183
unicode-width = "0.2"

codex-rs/cli/src/main.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,7 @@ struct LoginCommand {
157157
)]
158158
api_key: Option<String>,
159159

160-
/// EXPERIMENTAL: Use device code flow (not yet supported)
161-
/// This feature is experimental and may changed in future releases.
162-
#[arg(long = "experimental_use-device-code", hide = true)]
160+
#[arg(long = "use-device-code")]
163161
use_device_code: bool,
164162

165163
/// EXPERIMENTAL: Use custom OAuth issuer base URL (advanced)

codex-rs/cli/src/mcp_cmd.rs

Lines changed: 118 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use anyhow::Context;
44
use anyhow::Result;
55
use anyhow::anyhow;
66
use anyhow::bail;
7+
use clap::ArgGroup;
78
use codex_common::CliConfigOverrides;
89
use codex_core::config::Config;
910
use codex_core::config::ConfigOverrides;
@@ -77,13 +78,61 @@ pub struct AddArgs {
7778
/// Name for the MCP server configuration.
7879
pub name: String,
7980

80-
/// Environment variables to set when launching the server.
81-
#[arg(long, value_parser = parse_env_pair, value_name = "KEY=VALUE")]
82-
pub env: Vec<(String, String)>,
81+
#[command(flatten)]
82+
pub transport_args: AddMcpTransportArgs,
83+
}
8384

85+
#[derive(Debug, clap::Args)]
86+
#[command(
87+
group(
88+
ArgGroup::new("transport")
89+
.args(["command", "url"])
90+
.required(true)
91+
.multiple(false)
92+
)
93+
)]
94+
pub struct AddMcpTransportArgs {
95+
#[command(flatten)]
96+
pub stdio: Option<AddMcpStdioArgs>,
97+
98+
#[command(flatten)]
99+
pub streamable_http: Option<AddMcpStreamableHttpArgs>,
100+
}
101+
102+
#[derive(Debug, clap::Args)]
103+
pub struct AddMcpStdioArgs {
84104
/// Command to launch the MCP server.
85-
#[arg(trailing_var_arg = true, num_args = 1..)]
105+
/// Use --url for a streamable HTTP server.
106+
#[arg(
107+
trailing_var_arg = true,
108+
num_args = 0..,
109+
)]
86110
pub command: Vec<String>,
111+
112+
/// Environment variables to set when launching the server.
113+
/// Only valid with stdio servers.
114+
#[arg(
115+
long,
116+
value_parser = parse_env_pair,
117+
value_name = "KEY=VALUE",
118+
)]
119+
pub env: Vec<(String, String)>,
120+
}
121+
122+
#[derive(Debug, clap::Args)]
123+
pub struct AddMcpStreamableHttpArgs {
124+
/// URL for a streamable HTTP MCP server.
125+
#[arg(long)]
126+
pub url: String,
127+
128+
/// Optional environment variable to read for a bearer token.
129+
/// Only valid with streamable HTTP servers.
130+
#[arg(
131+
long = "bearer-token-env-var",
132+
value_name = "ENV_VAR",
133+
requires = "url"
134+
)]
135+
pub bearer_token_env_var: Option<String>,
87136
}
88137

89138
#[derive(Debug, clap::Parser)]
@@ -140,37 +189,51 @@ async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Re
140189
// Validate any provided overrides even though they are not currently applied.
141190
config_overrides.parse_overrides().map_err(|e| anyhow!(e))?;
142191

143-
let AddArgs { name, env, command } = add_args;
192+
let AddArgs {
193+
name,
194+
transport_args,
195+
} = add_args;
144196

145197
validate_server_name(&name)?;
146198

147-
let mut command_parts = command.into_iter();
148-
let command_bin = command_parts
149-
.next()
150-
.ok_or_else(|| anyhow!("command is required"))?;
151-
let command_args: Vec<String> = command_parts.collect();
152-
153-
let env_map = if env.is_empty() {
154-
None
155-
} else {
156-
let mut map = HashMap::new();
157-
for (key, value) in env {
158-
map.insert(key, value);
159-
}
160-
Some(map)
161-
};
162-
163199
let codex_home = find_codex_home().context("failed to resolve CODEX_HOME")?;
164200
let mut servers = load_global_mcp_servers(&codex_home)
165201
.await
166202
.with_context(|| format!("failed to load MCP servers from {}", codex_home.display()))?;
167203

168-
let new_entry = McpServerConfig {
169-
transport: McpServerTransportConfig::Stdio {
170-
command: command_bin,
171-
args: command_args,
172-
env: env_map,
204+
let transport = match transport_args {
205+
AddMcpTransportArgs {
206+
stdio: Some(stdio), ..
207+
} => {
208+
let mut command_parts = stdio.command.into_iter();
209+
let command_bin = command_parts
210+
.next()
211+
.ok_or_else(|| anyhow!("command is required"))?;
212+
let command_args: Vec<String> = command_parts.collect();
213+
214+
let env_map = if stdio.env.is_empty() {
215+
None
216+
} else {
217+
Some(stdio.env.into_iter().collect::<HashMap<_, _>>())
218+
};
219+
McpServerTransportConfig::Stdio {
220+
command: command_bin,
221+
args: command_args,
222+
env: env_map,
223+
}
224+
}
225+
AddMcpTransportArgs {
226+
streamable_http: Some(streamable_http),
227+
..
228+
} => McpServerTransportConfig::StreamableHttp {
229+
url: streamable_http.url,
230+
bearer_token_env_var: streamable_http.bearer_token_env_var,
173231
},
232+
AddMcpTransportArgs { .. } => bail!("exactly one of --command or --url must be provided"),
233+
};
234+
235+
let new_entry = McpServerConfig {
236+
transport,
174237
startup_timeout_sec: None,
175238
tool_timeout_sec: None,
176239
};
@@ -236,7 +299,7 @@ async fn run_login(config_overrides: &CliConfigOverrides, login_args: LoginArgs)
236299
_ => bail!("OAuth login is only supported for streamable HTTP servers."),
237300
};
238301

239-
perform_oauth_login(&name, &url).await?;
302+
perform_oauth_login(&name, &url, config.mcp_oauth_credentials_store_mode).await?;
240303
println!("Successfully logged in to MCP server '{name}'.");
241304
Ok(())
242305
}
@@ -259,7 +322,7 @@ async fn run_logout(config_overrides: &CliConfigOverrides, logout_args: LogoutAr
259322
_ => bail!("OAuth logout is only supported for streamable_http transports."),
260323
};
261324

262-
match delete_oauth_tokens(&name, &url) {
325+
match delete_oauth_tokens(&name, &url, config.mcp_oauth_credentials_store_mode) {
263326
Ok(true) => println!("Removed OAuth credentials for '{name}'."),
264327
Ok(false) => println!("No OAuth credentials stored for '{name}'."),
265328
Err(err) => return Err(anyhow!("failed to delete OAuth credentials: {err}")),
@@ -288,11 +351,14 @@ async fn run_list(config_overrides: &CliConfigOverrides, list_args: ListArgs) ->
288351
"args": args,
289352
"env": env,
290353
}),
291-
McpServerTransportConfig::StreamableHttp { url, bearer_token } => {
354+
McpServerTransportConfig::StreamableHttp {
355+
url,
356+
bearer_token_env_var,
357+
} => {
292358
serde_json::json!({
293359
"type": "streamable_http",
294360
"url": url,
295-
"bearer_token": bearer_token,
361+
"bearer_token_env_var": bearer_token_env_var,
296362
})
297363
}
298364
};
@@ -345,13 +411,15 @@ async fn run_list(config_overrides: &CliConfigOverrides, list_args: ListArgs) ->
345411
};
346412
stdio_rows.push([name.clone(), command.clone(), args_display, env_display]);
347413
}
348-
McpServerTransportConfig::StreamableHttp { url, bearer_token } => {
349-
let has_bearer = if bearer_token.is_some() {
350-
"True"
351-
} else {
352-
"False"
353-
};
354-
http_rows.push([name.clone(), url.clone(), has_bearer.into()]);
414+
McpServerTransportConfig::StreamableHttp {
415+
url,
416+
bearer_token_env_var,
417+
} => {
418+
http_rows.push([
419+
name.clone(),
420+
url.clone(),
421+
bearer_token_env_var.clone().unwrap_or("-".to_string()),
422+
]);
355423
}
356424
}
357425
}
@@ -396,7 +464,7 @@ async fn run_list(config_overrides: &CliConfigOverrides, list_args: ListArgs) ->
396464
}
397465

398466
if !http_rows.is_empty() {
399-
let mut widths = ["Name".len(), "Url".len(), "Has Bearer Token".len()];
467+
let mut widths = ["Name".len(), "Url".len(), "Bearer Token Env Var".len()];
400468
for row in &http_rows {
401469
for (i, cell) in row.iter().enumerate() {
402470
widths[i] = widths[i].max(cell.len());
@@ -407,7 +475,7 @@ async fn run_list(config_overrides: &CliConfigOverrides, list_args: ListArgs) ->
407475
"{:<name_w$} {:<url_w$} {:<token_w$}",
408476
"Name",
409477
"Url",
410-
"Has Bearer Token",
478+
"Bearer Token Env Var",
411479
name_w = widths[0],
412480
url_w = widths[1],
413481
token_w = widths[2],
@@ -447,10 +515,13 @@ async fn run_get(config_overrides: &CliConfigOverrides, get_args: GetArgs) -> Re
447515
"args": args,
448516
"env": env,
449517
}),
450-
McpServerTransportConfig::StreamableHttp { url, bearer_token } => serde_json::json!({
518+
McpServerTransportConfig::StreamableHttp {
519+
url,
520+
bearer_token_env_var,
521+
} => serde_json::json!({
451522
"type": "streamable_http",
452523
"url": url,
453-
"bearer_token": bearer_token,
524+
"bearer_token_env_var": bearer_token_env_var,
454525
}),
455526
};
456527
let output = serde_json::to_string_pretty(&serde_json::json!({
@@ -493,11 +564,14 @@ async fn run_get(config_overrides: &CliConfigOverrides, get_args: GetArgs) -> Re
493564
};
494565
println!(" env: {env_display}");
495566
}
496-
McpServerTransportConfig::StreamableHttp { url, bearer_token } => {
567+
McpServerTransportConfig::StreamableHttp {
568+
url,
569+
bearer_token_env_var,
570+
} => {
497571
println!(" transport: streamable_http");
498572
println!(" url: {url}");
499-
let bearer = bearer_token.as_deref().unwrap_or("-");
500-
println!(" bearer_token: {bearer}");
573+
let env_var = bearer_token_env_var.as_deref().unwrap_or("-");
574+
println!(" bearer_token_env_var: {env_var}");
501575
}
502576
}
503577
if let Some(timeout) = server.startup_timeout_sec {

0 commit comments

Comments
 (0)