Skip to main content

bootstrap-google-tools

Install/auth CLIs the sandbox lacks - `gws` (Drive, Gmail, Sheets, Calendar), `gcloud`, `agents-cli`, `mcp-cli` (MCP servers). Use on "command not found" or before GCP/Workspace/MCP work.

Source facts

Repository
google/adk-recipes
Last source activity
August 18, 2026 at 23:39
Detected SKILL.md language
English
Stars
10,453
Forks
2,913

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
bootstrap-google-tools
description
Install/auth CLIs the sandbox lacks - `gws` (Drive, Gmail, Sheets, Calendar), `gcloud`, `agents-cli`, `mcp-cli` (MCP servers). Use on "command not found" or before GCP/Workspace/MCP work.
# Install & authenticate CLIs in the sandbox The sandbox ships lean — Node, `gws`, `gcloud`, `agents-cli`, and `mcp-cli` are **not** prebaked. This skill is the verified install + auth recipe for each. The tools are **independent**: install only the one the task needs. This is a menu, not a sequence — there's no 1-2-3 to run in order. ## Persistence model — binaries in `~/.local`, credentials in `/workspace` Two different durability rules, because the sandbox has two boundaries: - **Across normal sessions** the sandbox reattaches, so *everything* (`$HOME` included) persists. Install once, reuse next session. - **Across a runtime-image upgrade** the sandbox is re-provisioned and **only `/workspace` is migrated** — and that migration is a zip that **drops symlinks and strips executable bits** (and is best-effort). So it can't carry installed CLIs at all; it's only safe for plain data files. That dictates the split: | What | Where | On upgrade | |---|---|---| | CLI binaries | `~/.local` (bin dir `~/.local/bin` is on `PATH`) | gone — reinstalled (cheap, automatic via the `command -v` check) | | Credentials / config | `/workspace/lha/config/` (`gws/`, `gcloud/`, `mcp_servers.json`) | **migrated** — logins survive | So: install binaries the normal way, but point each tool's **config dir** at `/workspace/lha/config` (via its own env var, shown per tool) so the expensive part — the OAuth client + tokens — survives upgrades. Don't put binaries on `/workspace`: they can't migrate anyway, and bloating the migration zip risks the credentials that *can*. `~/.local/bin` is already on `PATH` (runtime image), and the `bash` shell is non-login (no `~/.profile`), so that image `PATH` is what makes installs resolve. ## Pick what you need | Task | Tool | Needs first | |---|---|---| | Google Workspace — Drive / Gmail / Sheets / Calendar / Chat | `gws` | Node | | Raw GCP, or creating a `gws` OAuth client by hand | `gcloud` | — | | Call another agent deployed remotely (A2A / ADK) | `agents-cli` | — (`uv` is present) | | Use tools exposed by an MCP server | `mcp-cli` | — (standalone binary) | Each tool's **usage** lives in its own skill (`google-workspace`, mcp-cli's shipped skill, the `google-agents-cli-*` set). This skill only gets a tool installed and authed, then hands off. ## Before installing: check first Within a session (and across sessions without an upgrade) a tool may already be present — check before installing, and install only what's missing: ``` bash(command="command -v gws") # or gcloud / agents-cli / mcp-cli / node ``` ## Node + npm *(prerequisite for `gws`)* The sandbox is Debian x86_64 with `curl` and `tar` but **no `xz`**. So you must download the `.tar.gz` build, **not** the `.tar.xz`. Pick a current LTS version: ``` bash(command='mkdir -p ~/.local/bin && cd ~ && V=v22.x.x && \ curl -fsSLo node.tgz "https://nodejs.org/dist/$V/node-$V-linux-x64.tar.gz" && \ tar -xzf node.tgz -C ~/.local && rm node.tgz && \ ln -sf ~/.local/node-$V-linux-x64/bin/node ~/.local/node-$V-linux-x64/bin/npm ~/.local/node-$V-linux-x64/bin/npx ~/.local/bin/') ``` Replace `v22.x.x` with the real current LTS. The `ln -sf … ~/.local/bin/` step puts `node`/`npm`/`npx` on `PATH`. Then verify: ``` bash(command="node --version && npm --version") ``` - Use `.tar.gz` — `.tar.xz` will fail to extract (`xz` is absent). ## `gws` — Google Workspace CLI Install the npm global into `~/.local` (needs Node on `PATH` — see above) with `--prefix`, so the binary lands at `~/.local/bin/gws`: ``` bash(command='npm install -g --prefix ~/.local @googleworkspace/cli && gws --version') ``` Expect `0.22.x`. `gws` self-identifies as "not an officially supported Google product." **Keep `gws` credentials on `/workspace`.** By default `gws` reads/writes `~/.config/gws` — which is **not** migrated on a runtime upgrade. Point it at `/workspace` and use the file keyring (no OS keyring headless) **on every `gws` command** so the OAuth client, token, and encryption key survive: ``` export GOOGLE_WORKSPACE_CLI_CONFIG_DIR=/workspace/lha/config/gws export GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file ``` **First, always: probe the pre-injected token.** When the user has used **"Connect Workspace"** in the web UI, the `GOOGLE_WORKSPACE_CLI_TOKEN` secret is auto-injected into every `bash` command — `gws`'s **highest-priority** auth source. Don't inspect the environment or ask; just run a cheap read against the surface you need and see if it works: ``` bash(command="export GOOGLE_WORKSPACE_CLI_CONFIG_DIR=/workspace/lha/config/gws GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file && gws gmail messages list --params '{\"maxResults\": 1}'") ``` If it returns data, you're done — **no OAuth client, no `gws auth login`, no loopback bridge**. It's a ~1h token (no refresh); the user re-clicks Connect when it lapses. The token only carries the **surfaces + access level** (read-only by default) the user picked — a `gws` call outside those scopes fails (`403`/scope error), which means "reconnect with more surfaces or read-write," **not** that the token is missing. **Don't trust `gws auth status`** — with the env token it reports `auth_method: none` / `credential_source: token_env_var` even while reads succeed, so a real read is the only reliable check. Only if the probe `401`s (truly no token) do you fall through to the OAuth-client options below. **Otherwise, point `gws` at an OAuth client** — `gws` ships none of its own. Pick one: 1. Drop a `client_secret.json` into `/workspace/lha/config/gws/` (must be a **Desktop app** OAuth client — see the `google-workspace` skill). 2. Set `GOOGLE_WORKSPACE_CLI_CLIENT_ID` + `GOOGLE_WORKSPACE_CLI_CLIENT_SECRET`. 3. Service account: `GOOGLE_APPLICATION_CREDENTIALS=/workspace/lha/config/gws/key.json` — for fully unattended automation (no interactive login). 4. `gws auth setup` — provisions a project + OAuth client, but is a full-screen TUI that **can't be driven headless**; prefer option 1 in the sandbox. `gws auth login` is loopback-browser only, but **does complete headless** — you bridge the OAuth redirect by hand (start it backgrounded, relay the auth URL to the user, then `curl` their pasted `localhost:<port>/?code=...` redirect back to the waiting listener). The **`google-workspace`** skill has the full auth recipe (Desktop-app client type, Internal audience, the loopback bridge) plus command shapes (Drive, Docs, Sheets, Gmail, Calendar, Chat). For fully unattended jobs with no user present to paste the redirect, use the service-account option. **Under a routine, the Google/gcloud token is present only if you declared its secret name (`GOOGLE_WORKSPACE_CLI_TOKEN` / `CLOUDSDK_AUTH_ACCESS_TOKEN`) in the routine's `secrets:`.** **Once `gws` is set up, strongly suggest installing Google's official `gws` skills.** They live in the `googleworkspace/cli` repo and go deeper than the builtin `google-workspace` skill — one per surface. Install the ones the user needs (`gws-shared` is the common base the others build on): ``` bash(command="npx --yes skills add googleworkspace/cli@gws-shared -y") bash(command="npx --yes skills add googleworkspace/cli@gws-gmail -y") # also: gws-drive, gws-docs, gws-docs-write, gws-sheets, gws-calendar, # gws-events, gws-chat-send ``` The Skills CLI stages each under `.agents/skills/<skill>/`, which Horizon auto-discovers — just `load_skill(action="reload")`, no move needed (see the `find-skills` skill for details). (`npx --yes skills find gws` lists the current set with install counts.) ## `gcloud` Install under `~/.local` (slim — don't add extra components) and symlink the entrypoints onto `PATH`: ``` bash(command='mkdir -p ~/.local/bin && cd ~/.local && \ curl -fsSLo gcloud.tgz "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-linux-x86_64.tar.gz" && \ tar -xzf gcloud.tgz && rm gcloud.tgz && \ ./google-cloud-sdk/install.sh --quiet --usage-reporting=false --path-update=false && \ ln -sf ~/.local/google-cloud-sdk/bin/gcloud ~/.local/google-cloud-sdk/bin/gsutil ~/.local/google-cloud-sdk/bin/bq ~/.local/bin/') ``` **No durable gcloud credentials are kept in the sandbox.** GCP access is via the short-lived **token-as-secret** path below, which persists nothing — no refresh token, no ADC file, no key. (You may set `CLOUDSDK_CONFIG=/workspace/lha/config/gcloud` as a scratch config dir, but there is no login state to persist across upgrades.) Verify: ``` bash(command="gcloud --version") ``` ### Security: GCP creds is a high-trust combination Logging in deposits credentials **the agent** can use, in a sandbox with open outbound internet egress. An injected agent could read the user's GCP data, `bq extract` / `gsutil cp` it to a foreign bucket, mint a service-account key, or grant external IAM — all over `*.googleapis.com`. Layer A (exfil_guard) still gates credential/secret exfil and uploads to non-allowlisted hosts, and still blocks the GCP metadata server; it has no GCP-action awareness (it can't see what a `gcloud` call does). **The real control is credential minimization:** - **Use the short-lived token-as-secret path only** (below). We keep **no durable credential** (refresh token / ADC file / SA key) in the sandbox, so a single injection can't become standing access that outlives the ~1 h token. - Mint the token with the **narrowest scopes** the task needs; `cloud-platform` (everything the user's IAM allows) is a broad, high-trust default — not a free one. - Confirm the user actually wants the agent acting as them on GCP before adding the token. ### Authenticate — short-lived access token as a secret (the only GCP path) The lowest-trust, least-friction path, and the **only** one we use (works for corp / org-restricted accounts too): the user mints a **short-lived access token on their own machine** (already gcloud-authed there, so the org's device/CAA policy is satisfied) and hands it to the agent as a **secret**. Nothing durable lands in the sandbox, and the token self-expires in ~1 hour. 1. The user runs locally, in their own terminal (authed as the right account): ``` gcloud auth print-access-token ``` 2. The user saves it as a secret **named `CLOUDSDK_AUTH_ACCESS_TOKEN`** in the `/lha/secrets` UI — never pasted into chat (it's a live credential). 3. Secrets are **auto-injected as env vars into every `bash` command**, and gcloud reads `CLOUDSDK_AUTH_ACCESS_TOKEN` from the environment — so the agent just runs commands, no login / ADC / OAuth client at all: ``` bash(command="export CLOUDSDK_CONFIG=/workspace/lha/config/gcloud && gcloud projects list") ``` `gsutil` and `bq` read the same env token. **Don't name the token in a `curl`/`wget`** (`-H "Authorization: Bearer $CLOUDSDK_AUTH_ACCESS_TOKEN"`): the exfil guard hard-blocks a network command that references a secret env var (by design — it stops the agent exfiltrating the token). Use the gcloud-family CLIs, which pick it up from the environment without naming it; a raw REST call that must carry the token needs a `/grant`. Why this is the default: - **Short-lived (~1 h):** a single injection can't become standing access; on expiry the user re-mints and updates the secret. - **Nothing durable in the sandbox** — no refresh token on `/workspace` to reuse. - **Corp-clean:** consent already happened through the user's org-approved local gcloud — no `Account restricted`, no custom OAuth client, no remote-bootstrap. - It still acts as the user for that window (short lifetime bounds misuse, doesn't eliminate it) and carries the user's login scopes. For a **long unattended job** the token expires (~1 h) and must be **re-minted and the secret updated** — by design we keep no durable credential in the sandbox to fall back on. ## `agents-cli` — call a remote agent (A2A / ADK) To hand a task to **another agent deployed remotely** (Cloud Run, Vertex Agent Runtime, or any A2A endpoint), shell out to `agents-cli` — no in-process wiring needed. This is the outbound counterpart to in-process `subagent()`: the remote agent is its own service with its own state; you just send a prompt over HTTP. Install (one-time; `uv` is already in the sandbox — it installs to `~/.local/bin`): ``` bash(command="command -v agents-cli || uv tool install google-agents-cli") bash(command="agents-cli --version") ``` (Install pulls from wherever `google-agents-cli` is published — if the sandbox can't reach that index, that's the same egress dependency as the Node/gcloud downloads.) Invoke: ``` bash(command='agents-cli run "summarize this repo" --url https://my-agent-xxxx.run.app --mode a2a') ``` - `--mode a2a` for the A2A protocol; `--mode adk` for ADK SSE (`/run_sse`, or `:streamQuery` on Agent Runtime). `--mode` is **required** with `--url`. - `--app-name <name>` to target a specific agent at that endpoint. - `--session-id <id>` to continue a conversation; `-v` for full JSON events. - stdout is the remote agent's final response — that *is* your result (blocking). For a long-running remote task, run it with `process(action='spawn', command=...)` and poll via the `process` tool (fire-and-forget). **Auth — usually automatic.** `agents-cli` auto-detects Google credentials from the sandbox identity: an **ID token** (audience = service URL) for Cloud Run, an **access token** for Vertex AI / Agent Runtime. For this to yield a usable token, the sandbox/Cloud Run service account needs the right IAM on the target (`roles/run.invoker` for Cloud Run; Vertex perms for Agent Runtime) — a 401/403 is almost always missing IAM, not a CLI problem. **User-delegated auth** (call *as the user*): pass the token explicitly, which overrides auto-detect: ```
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub