fix: respect account selection in MCP server and CLI --account flag (#223)

* fix: respect account selection in MCP server and CLI --account flag (#221, #181)

MCP server now reads GOOGLE_WORKSPACE_CLI_ACCOUNT env var and passes it
to get_token instead of always using None (default account).

CLI filter_args_for_subcommand now dynamically locates the service name
instead of hardcoding skip(2), fixing --account before service name
causing unrecognized subcommand errors.

* fix: skip --api-version in first_arg detection

The first_arg loop only skipped --account but not --api-version,
so `gws --api-version v3 drive ...` would misidentify --api-version
as the service name. Now both global flags are consistently skipped.
This commit is contained in:
Frank
2026-03-06 08:31:57 +08:00
committed by GitHub
parent 322529d8a9
commit c86b964de4
3 changed files with 52 additions and 13 deletions
@@ -0,0 +1,5 @@
---
"@googleworkspace/cli": patch
---
Fix multi-account selection: MCP server now respects `GOOGLE_WORKSPACE_CLI_ACCOUNT` env var (#221), and `--account` flag before service name no longer causes parse errors (#181)
+45 -12
View File
@@ -71,7 +71,7 @@ async fn run() -> Result<(), GwsError> {
));
}
// Find the first non-flag arg (skip --account and its value)
// Find the first non-flag arg (skip --account/--api-version and their values)
let mut first_arg: Option<String> = None;
{
let mut skip_next = false;
@@ -80,11 +80,11 @@ async fn run() -> Result<(), GwsError> {
skip_next = false;
continue;
}
if a == "--account" {
if a == "--account" || a == "--api-version" {
skip_next = true;
continue;
}
if a.starts_with("--account=") {
if a.starts_with("--account=") || a.starts_with("--api-version=") {
continue;
}
if !a.starts_with("--") || a.as_str() == "--help" || a.as_str() == "--version" {
@@ -165,7 +165,7 @@ async fn run() -> Result<(), GwsError> {
// Re-parse args (skip argv[0] which is the binary, and argv[1] which is the service name)
// Filter out --api-version and its value
// Prepend "gws" as the program name since try_get_matches_from expects argv[0]
let sub_args = filter_args_for_subcommand(&args);
let sub_args = filter_args_for_subcommand(&args, &first_arg);
let matches = cli.try_get_matches_from(&sub_args).map_err(|e| {
// If it's a help or version display, print it and exit cleanly
@@ -321,10 +321,11 @@ pub fn parse_service_and_version(
Ok((api_name, version))
}
pub fn filter_args_for_subcommand(args: &[String]) -> Vec<String> {
pub fn filter_args_for_subcommand(args: &[String], service_name: &str) -> Vec<String> {
let mut sub_args: Vec<String> = vec!["gws".to_string()];
let mut skip_next = false;
for arg in args.iter().skip(2) {
let mut service_skipped = false;
for arg in args.iter().skip(1) {
if skip_next {
skip_next = false;
continue;
@@ -336,6 +337,10 @@ pub fn filter_args_for_subcommand(args: &[String]) -> Vec<String> {
if arg.starts_with("--account=") || arg.starts_with("--api-version=") {
continue;
}
if !service_skipped && arg == service_name {
service_skipped = true;
continue;
}
sub_args.push(arg.clone());
}
sub_args
@@ -698,7 +703,7 @@ mod tests {
"files".into(),
"list".into(),
];
let filtered = filter_args_for_subcommand(&args);
let filtered = filter_args_for_subcommand(&args, "drive");
assert_eq!(filtered, vec!["gws", "files", "list"]);
assert!(!filtered.contains(&"--account".to_string()));
assert!(!filtered.contains(&"user@corp.com".to_string()));
@@ -714,7 +719,7 @@ mod tests {
"files".into(),
"list".into(),
];
let filtered = filter_args_for_subcommand(&args);
let filtered = filter_args_for_subcommand(&args, "drive");
assert_eq!(filtered, vec!["gws", "files", "list"]);
}
@@ -730,7 +735,7 @@ mod tests {
"files".into(),
"list".into(),
];
let filtered = filter_args_for_subcommand(&args);
let filtered = filter_args_for_subcommand(&args, "drive");
assert_eq!(filtered, vec!["gws", "files", "list"]);
}
@@ -744,7 +749,7 @@ mod tests {
"--format".into(),
"table".into(),
];
let filtered = filter_args_for_subcommand(&args);
let filtered = filter_args_for_subcommand(&args, "drive");
assert_eq!(filtered, vec!["gws", "files", "list", "--format", "table"]);
}
@@ -777,7 +782,7 @@ mod tests {
"files".into(),
"list".into(),
];
let filtered = filter_args_for_subcommand(&args);
let filtered = filter_args_for_subcommand(&args, "drive");
assert!(!filtered.contains(&"--account".to_string()));
assert!(!filtered.contains(&"work@corp.com".to_string()));
assert!(filtered.contains(&"files".to_string()));
@@ -796,6 +801,34 @@ mod tests {
);
}
#[test]
fn test_filter_args_account_before_service() {
// --account appears BEFORE the service name (issue #181)
let args: Vec<String> = vec![
"gws".into(),
"--account".into(),
"work@corp.com".into(),
"drive".into(),
"files".into(),
"list".into(),
];
let filtered = filter_args_for_subcommand(&args, "drive");
assert_eq!(filtered, vec!["gws", "files", "list"]);
}
#[test]
fn test_filter_args_account_equals_before_service() {
let args: Vec<String> = vec![
"gws".into(),
"--account=work@corp.com".into(),
"drive".into(),
"files".into(),
"list".into(),
];
let filtered = filter_args_for_subcommand(&args, "drive");
assert_eq!(filtered, vec!["gws", "files", "list"]);
}
#[test]
fn test_filter_args_strips_account_equals() {
let args: Vec<String> = vec![
@@ -805,7 +838,7 @@ mod tests {
"files".into(),
"list".into(),
];
let filtered = filter_args_for_subcommand(&args);
let filtered = filter_args_for_subcommand(&args, "drive");
assert!(!filtered.iter().any(|a| a.contains("account")));
assert_eq!(filtered, vec!["gws", "files", "list"]);
}
+2 -1
View File
@@ -814,7 +814,8 @@ async fn execute_mcp_method(
};
let scopes: Vec<&str> = crate::select_scope(&method.scopes).into_iter().collect();
let (token, auth_method) = match crate::auth::get_token(&scopes, None).await {
let account = std::env::var("GOOGLE_WORKSPACE_CLI_ACCOUNT").ok();
let (token, auth_method) = match crate::auth::get_token(&scopes, account.as_deref()).await {
Ok(t) => (Some(t), crate::executor::AuthMethod::OAuth),
Err(e) => {
eprintln!(