What it does
gws is a single CLI for Drive, Gmail, Calendar, Sheets, Docs, Chat, Admin, and every other Google Workspace API. Written in Rust, it doesn't ship a hardcoded list of commands. It reads Google's Discovery Service at runtime and builds the entire clap command tree on the fly.
Why I starred it
Most API wrappers bake a snapshot of the API surface into their source code. When endpoints change, the wrapper needs a new release. gws took the opposite approach — it fetches the official Discovery Document for each service and generates its CLI structure from that JSON spec every time you run it. That means zero code changes when Google ships new endpoints.
The project also ships 100+ AI agent skills as SKILL.md files, making it a natural fit for LLM-driven automation against Google Workspace. That's what caught my eye initially, but the architecture is what kept me reading.
How it works
The entry point in crates/google-workspace-cli/src/main.rs follows a two-phase parsing strategy. First, it reads argv[1] to identify the service name. Then it fetches (or cache-hits) that service's Discovery Document and builds a full clap::Command tree from it before re-parsing the remaining arguments.
The core of this lives in crates/google-workspace/src/discovery.rs. The RestDescription struct deserializes the entire Discovery Document — resources, methods, parameters, schemas, auth scopes — using serde with camelCase rename rules. The fetch logic tries the standard Discovery URL first, then falls back to the newer $discovery/rest pattern used by APIs like Forms and Meet:
let url = format!(
"https://www.googleapis.com/discovery/v1/apis/{}/{}/rest",
encode_path_segment(service),
encode_path_segment(version),
);
// ... if that 404s:
let alt_url = format!("https://{service}.googleapis.com/$discovery/rest");
Discovery responses are cached locally for 24 hours in ~/.config/gws/cache/, keyed by {service}_{version}.json. The cache check uses file modification time — simple and effective.
The command tree generation in commands.rs is where things get interesting. build_cli() takes the deserialized RestDescription and recursively walks its resources and methods, building a nested clap::Command for each one. Every method gets --params, --json, --output, and pagination flags. Methods that support media upload also get --upload and --upload-content-type. The function build_resource_command recurses through sub-resources, so gws drive files permissions list just works without anyone hardcoding that path.
The service registry in crates/google-workspace/src/services.rs maps aliases to API names and versions. It's a static &[ServiceEntry] — the one piece that is hardcoded. But it's intentionally thin: just alias resolution and default versions. All the actual API surface comes from Discovery.
What I appreciate is the separation between the library crate (google-workspace) and the CLI crate (google-workspace-cli). The library handles Discovery fetching, validation, and HTTP client construction. The CLI layer handles argument parsing, authentication, and output formatting. You could build a different frontend on top of the library.
The executor in executor.rs handles request dispatch, including multipart uploads via an UploadSource enum that makes illegal states unrepresentable — you either upload from a file path or from in-memory bytes, never both, never neither when uploading. Body validation runs against the Discovery Document's JSON schemas before any HTTP request fires.
Using it
# List 5 most recent Drive files
gws drive files list --params '{"pageSize": 5}'
# Create a spreadsheet
gws sheets spreadsheets create --json '{"properties": {"title": "Q1 Budget"}}'
# Auto-paginate and pipe through jq
gws drive files list --params '{"pageSize": 100}' --page-all | jq -r '.files[].name'
# Inspect what an API method expects
gws schema drive.files.list
# Preview a request without sending it
gws chat spaces messages create \
--params '{"parent": "spaces/xyz"}' \
--json '{"text": "Deploy complete."}' \
--dry-run
Helper commands prefixed with + provide higher-level workflows on top of the raw API surface:
gws gmail +send --to alice@example.com --subject "Hello" --body "Hi there"
gws calendar +agenda --today --timezone America/New_York
gws workflow +standup-report
Rough edges
The service registry is static. If Google launches a brand new Workspace API tomorrow, you still need a code change to add its alias and default version to services.rs. The dynamic Discovery fetching only kicks in after you know which service to ask for.
Authentication setup requires a Google Cloud project with OAuth credentials. gws auth setup automates this if you have gcloud installed, but the manual path involves multiple steps in the Cloud Console. Unverified apps in testing mode are limited to ~25 OAuth scopes, which means the broad recommended preset fails — you have to cherry-pick services with gws auth login -s drive,gmail,sheets.
The project is pre-1.0 and explicitly warns about breaking changes. Commit history is active — weekly releases, recent refactors like dropping serde_yaml for TOML, adding cargo-deny for license auditing. That's a good sign for longevity, but pin your version if you're building automation on top of it.
Not an officially supported Google product, which means support comes through GitHub issues, not Google Cloud support channels.
Bottom line
If you interact with Google Workspace APIs from the terminal or through AI agents, gws replaces a mess of curl calls and per-service wrappers with a single binary that stays current with Google's own API definitions. The Discovery-driven architecture is the right call for an API surface this large and this frequently updated.
