Skip to main content
skill CX Codex

gdrive-access — Claude Code skill for Google Drive CLI

This skill gives your agent Google Drive from the command line: list files, download them, sync whole folders. OAuth credentials come from 1Password.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add terrylica/cc-skills --skill gdrive-access -a claude-code
Open on GitHub

What gdrive-access does

The skill teaches an agent to work with Google Drive from a terminal: list what is in a folder, search by name, download a single file, pull a folder down with its subfolders. Writing works too — create-doc uploads a local HTML or .docx source and has Drive convert it into a native Google Doc, so the team can open and comment on it without a conversion step.

Under the hood there is a compiled CLI binary built on Bun. The skill stores no credentials itself: it reads GDRIVE_OP_UUID from the process environment and pulls the OAuth client out of 1Password through op. Every Drive operation is gated by a preflight — binary present, variable set, op signed in. If the binary is missing, you build it with bun install && bun run build.

Here is where it usually breaks. Under a 1Password service-account token, op only sees the vaults that token was granted, so a hardcoded --vault Employee fails — and piped blind into jq, that failure shows up as a confusing parse error. Run op vault list first and set the vault from the result. Also note the script unsets HTTPS_PROXY and HTTP_PROXY before talking to api.1password.com, because that host returns 502 through a proxy. On the first run a browser opens for Google consent; that is expected.

The skill is meant to be edited as you go: when an instruction drifts from reality, fix the file instead of working around it.

What it helps with

  • pull a Drive folder to disk with its subfolders
  • find a file by name with a Drive search query
  • download one document by its file_id
  • turn a local HTML file into a native Google Doc
  • check whether Drive is reachable from the agent environment

Example prompts for the agent

  • list the files in the Drive folder with id 1wqqqvBmeUFYuwOOEQhzoChC7KzAk-mAS
  • download that Drive file to ./output.pdf
  • sync the Drive folder into ./output_dir recursively

How to install gdrive-access

1Skills CLI (Vercel)

npx skills add terrylica/cc-skills --skill gdrive-access -a claude-code

2Manually: put the files in place

~/.claude/skills/gdrive-access/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/gdrive-access/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about gdrive-access

How do I install gdrive-access in Claude?

Run npx skills add terrylica/cc-skills --skill gdrive-access -a claude-code. The skill then lands at ~/.claude/skills/gdrive-access/SKILL.md.

What does gdrive-access do?

It gives an agent commands for Google Drive: listing files, searching, downloading, syncing folders, and creating a native Google Doc from HTML or .docx.

What does gdrive-access need to run?

The 1Password CLI, a signed-in session via op signin, and GDRIVE_OP_UUID set to the item's UUID. If the binary is not built yet, you also need Bun for bun install && bun run build.

Source text

SKILL.md · MIT
---
name: gdrive-access
description: "Access Google Drive via CLI with 1Password OAuth: list files, download, and sync folders. TRIGGERS - google drive, gdrive, drive folder, download drive, sync drive."
allowed-tools: Read, Bash, Grep, Glob, Write, AskUserQuestion
---

# Google Drive Access

List, download, and sync files from Google Drive programmatically via Claude Code.

> **Self-Evolving Skill**: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.

## MANDATORY PREFLIGHT (Execute Before Any Drive Operation)

**CRITICAL**: You MUST complete this preflight checklist before running any gdrive commands. Do NOT skip steps.

### Step 1: Check CLI Binary Exists

```bash
ls -la "$HOME/.claude/plugins/marketplaces/cc-skills/plugins/productivity-tools/skills/gdrive-access/scripts/gdrive" 2>/dev/null || echo "BINARY_NOT_FOUND"
```

**If BINARY_NOT_FOUND**: Build it first:

```bash
cd ~/.claude/plugins/marketplaces/cc-skills/plugins/productivity-tools/skills/gdrive-access/scripts && bun install && bun run build
```

### Step 2: Check GDRIVE_OP_UUID Environment Variable

```bash
echo "GDRIVE_OP_UUID: ${GDRIVE_OP_UUID:-NOT_SET}"
```

**If NOT_SET**: You MUST run the Setup Flow below. Do NOT proceed to gdrive commands.

### Step 3: Verify 1Password Authentication

```bash
op account list 2>&1 | head -3
```

**If error or not signed in**: Inform user to run `op signin` first.

---

## Setup Flow (When GDRIVE_OP_UUID is NOT_SET)

Follow these steps IN ORDER. Use AskUserQuestion at decision points.

### Setup Step 1: Check 1Password CLI

```bash
command -v op && echo "OP_CLI_INSTALLED" || echo "OP_CLI_MISSING"
```

**If OP_CLI_MISSING**: Stop and inform user:

> 1Password CLI is required. Install with: `brew install 1password-cli`

### Setup Step 2: Discover Drive OAuth Items in 1Password

```bash
unset HTTPS_PROXY HTTP_PROXY   # the OAuth proxy 502s api.1password.com
op vault list                  # FIRST: see which vaults this credential can actually reach
op item list --vault "<vault-from-above>" --format json 2>/dev/null | jq -r '.[] | select(.title | test("drive|oauth|google"; "i")) | "\(.id)\t\(.title)"'
```

⚠ **Do not hardcode `Employee`.** When `op` runs under a **service-account** token it sees only the vaults granted to that token, and `--vault Employee` fails with `"Employee" isn't a vault in this account` — which surfaces confusingly as a `jq: parse error` if you pipe it blind. Always run `op vault list` first and set `GDRIVE_OP_VAULT` accordingly. A service account also **requires** `--vault` on every `op item get`; without it you get `a vault query must be provided`.

**Parse the output** and proceed based on results:

### Setup Step 3: User Selects OAuth Credentials

**If items found**, use AskUserQuestion with discovered items:

```
AskUserQuestion({
  questions: [{
    question: "Which 1Password item contains your Google Drive OAuth credentials?",
    header: "Drive OAuth",
    options: [
      // POPULATE FROM op item list RESULTS - example:
      { label: "Google Drive API (56peh...)", description: "OAuth client in Employee vault" },
      { label: "Gmail API - project-f (abc12...)", description: "Can also access Drive" },
    ],
    multiSelect: false
  }]
})
```

**If NO items found**, use AskUserQuestion to guide setup:

```
AskUserQuestion({
  questions: [{
    question: "No Google Drive OAuth credentials found in 1Password. How would you like to proceed?",
    header: "Setup",
    options: [
      { label: "Create new OAuth credentials (Recommended)", description: "I'll guide you through Google Cloud Console setup" },
      { label: "I have credentials elsewhere", description: "Help me add them to 1Password" },
      { label: "Skip for now", description: "I'll set this up later" }
    ],
    multiSelect: false
  }]
})
```

- If "Create new OAuth credentials": Read and present [references/gdrive-api-setup.md](./references/gdrive-api-setup.md)
- If "I have credentials elsewhere": Guide user to add to 1Password with required fields
- If "Skip for now": Inform user the skill won't work until configured

### Setup Step 4: Confirm Environment Configuration

The CLI reads `GDRIVE_OP_UUID` (and optionally `GDRIVE_OP_VAULT`) from the process environment and nothing else. After user selects an item (with UUID), use AskUserQuestion:

```
AskUserQuestion({
  questions: [{
    question: "Export GDRIVE_OP_UUID from your shell profile (~/.zshrc)?",
    header: "Configure",
    options: [
      { label: "Yes, add to ~/.zshrc (Recommended)", description: "Appends an export line; applies to every new shell" },
      { label: "Show me the line only", description: "I'll add it manually, or pass it inline per call" }
    ],
    multiSelect: false
  }]
})
```

**If "Yes, add to ~/.zshrc"**: check that no `GDRIVE_OP_UUID` export already exists, then append:

```bash
export GDRIVE_OP_UUID="<selected-uuid>"
export GDRIVE_OP_VAULT="<vault-from-step-2>"   # only if not Employee
```

**If "Show me the line only"**: Output the export lines for the user to add manually. Passing the variable inline per call (`GDRIVE_OP_UUID=<uuid> gdrive ...`, as Step 6 does) also works.

### Setup Step 5: Reload and Verify

```bash
source ~/.zshrc && echo "GDRIVE_OP_UUID after reload: ${GDRIVE_OP_UUID:-NOT_SET}"
```

**If still NOT_SET**: Inform user to restart their shell.

### Setup Step 6: Test Connection

```bash
GDRIVE_OP_UUID="${GDRIVE_OP_UUID}" $HOME/.claude/plugins/marketplaces/cc-skills/plugins/productivity-tools/skills/gdrive-access/scripts/gdrive list 1wqqqvBmeUFYuwOOEQhzoChC7KzAk-mAS
```

**If OAuth prompt appears**: This is expected on first run. Browser will open for Google consent.

---

## Drive Commands (Only After Preflight Passes)

```bash
GDRIVE_CLI="$HOME/.claude/plugins/marketplaces/cc-skills/plugins/productivity-tools/skills/gdrive-access/scripts/gdrive"

# List files in a folder
$GDRIVE_CLI list <folder_id>

# List with details (size, modified date)
$GDRIVE_CLI list <folder_id> --verbose

# Search for files
$GDRIVE_CLI search "name contains 'training'"

# Get file info
$GDRIVE_CLI info <file_id>

# Download a single file
$GDRIVE_CLI download <file_id> -o ./output.pdf

# Sync entire folder to local directory
$GDRIVE_CLI sync <folder_id> -o ./output_dir

# Sync with subfolders
$GDRIVE_CLI sync <folder_id> -o ./output_dir -r

# JSON output (for parsing)
$GDRIVE_CLI list <folder_id> --json
```

## Creating a native Google Doc (write)

`create-doc` uploads a local **HTML** (or `.docx`) source and has Drive convert it into a **native
Google Doc** (`application/vnd.google-apps.document`) — the team can open + comment on it directly, no
"convert" step. This is the recommended Drive pattern: `files.create` with the **target** mimeType in the
metadata and the **source** bytes as media, sent as a _multipart_ upload (a simple upload silently skips
conversion).

```bash
# Markdown is the usual author format → HTML (highest-fidelity import) → native Google Doc
pandoc notes.md -o /tmp/notes.html --standalone
$GDRIVE_CLI create-doc /tmp/notes.html --name "Meeting Notes" --parent <folder_id>
# → prints the new Doc's id, mimeType (application/vnd.google-apps.document), and docs.google.com link

# Overwrite an existing Doc's contents in place (keeps the same id / link / comments):
$GDRIVE_CLI create-doc /tmp/notes.html --update <doc_id>
```

**Why not rclone / `.docx`?** rclone's `--drive-import-formats` is unreliable for this (name-collision
and sync-confusion gotchas), and a plain `.docx` in Drive opens in Docs but isn't a _native_ Doc. The Drive
API conversion above is the robust FOSS path.

**Write scope (one-time re-auth).** `create-doc` needs the `drive.file` scope (create/manage files this
app makes — least-privilege, NOT full-drive). It was added alongside the original `drive.readonly`, so the
first write re-prompts for consent. If it doesn't, force it:
`rm ~/.claude/tools/gdrive-tokens/$GDRIVE_OP_UUID.json` then re-run. `--update` only works on Docs this app
created (drive.file limitation); to overwrite externally-created Docs, broaden the scope to `auth/drive`.

## Rate limits & retries (built in)

Drive returns **HTTP 403 `rateLimitExceeded` / `userRateLimitExceeded`** (and sometimes **429**) when a
project bursts too many queries — we hit this doing several writes back-to-back. The CLI now wraps every
Drive call in **exponential backoff with jitter** (`withBackoff` in `lib/drive.ts`): wait
`min(2^n·1000 + random_ms, 64s)`, finite retries, per Google's official guidance. Non-rate-limit errors
still fail loud. To stay under the limit proactively: **batch / space out** bulk operations, avoid
redundant metadata calls, and don't refresh the token on every call.

## When the saved token is dead: `Error: invalid_grant`

`Token expired, refreshing...` followed by **`Error: invalid_grant`** means the stored **refresh token** is revoked or expired — not the access token. Refreshing cannot recover it. Causes: the OAuth client is still in Google Cloud **"Testing"** publishing status (refresh tokens then expire after **7 days**), the user revoked app access, or the password changed. The only fix through this CLI is a full re-consent, which needs an interactive browser:

```bash
rm ~/.claude/tools/gdrive-tokens/$GDRIVE_OP_UUID.json   # then re-run any gdrive command
```

**Fallback that needs no browser: `rclone`.** If `rclone listremotes` shows a `type = drive` remote for the same Google account, it carries its own independent token and usually still works. Verified 2026-09-02 when `invalid_grant` blocked the CLI entirely. Access an arbitrary folder **by ID** — including one under "Shared with me", which has no My Drive path:

```bash
rclone lsjson --recursive --max-depth 2 --drive-root-folder-id <folder_id> <remote>:
rclone lsjson --metadata --drive-root-folder-id <folder_id> <remote>:<subfolder>   # adds owner + btime (created)
rclone copy --drive-root-folder-id <folder_id> "<remote>:<subfolder>/<file>" ./dest/
```

`--metadata` is the only way to get **owner** and **created time (`btime`)**; plain `lsjson` returns neither. Always report which path you used — the rclone remote may be a different Google identity than `GDRIVE_OP_UUID`.

## Extracting Folder ID from URL

Google Drive folder URL:

```
https://drive.google.com/drive/folders/1wqqqvBmeUFYuwOOEQhzoChC7KzAk-mAS
                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                       This is the folder ID
```

## Drive Search Syntax

| Query                          | Description           |
| ------------------------------ | --------------------- |
| `name contains 'keyword'`      | Name contains keyword |
| `name = 'exact name'`          | Exact name match      |
| `mimeType = 'application/pdf'` | By file type          |
| `modifiedTime > '2026-01-01'`  | Modified after date   |
| `trashed = false`              | Not in trash          |
| `'<folderId>' in parents`      | In specific folder    |

Reference: <https://developers.google.com/drive/api/guides/search-files>

## Environment Variables

| Variable          | Required | Description                                                                                                                                   |
| ----------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `GDRIVE_OP_UUID`  | Yes      | 1Password item UUID for OAuth credentials                                                                                                     |
| `GDRIVE_OP_VAULT` | Usually  | 1Password vault. Code default is `Employee`, which a service-account token generally CANNOT see — run `op vault list` and set this explicitly |

## Token Storage

OAuth tokens stored at: `~/.claude/tools/gdrive-tokens/<uuid>.json`

- Central location (not in plugin, not in project)
- Organized by 1Password UUID (supports multi-account)
- Created with chmod 600

## Google Docs Export

Google Docs (Docs, Sheets, Slides) are automatically exported:

| Google Type  | Export Format |
| ------------ | ------------- |
| Document     | .docx         |
| Spreadsheet  | .xlsx         |
| Presentation | .pptx         |
| Drawing      | .png          |

## References

- [gdrive-api-setup.md](./references/gdrive-api-setup.md) - Google Cloud OAuth setup guide

## Post-Change Checklist

- [ ] YAML frontmatter valid (no colons in description)
- [ ] Trigger keywords current
- [ ] Path patterns use $HOME not hardcoded paths
- [ ] References exist and are linked

## Post-Execution Reflection

After this skill completes, reflect before closing the task:

0. **Locate yourself.** — Find this SKILL.md's canonical path before editing.
1. **What failed?** — Fix the instruction that caused it.
2. **What worked better than expected?** — Promote to recommended practice.
3. **What drifted?** — Fix any script, reference, or dependency that no longer matches reality.
4. **Log it.** — Evolution-log entry with trigger, fix, and evidence.

Do NOT defer. The next invocation inherits whatever you leave behind.

© terrylica · MIT · terrylica/cc-skills

Similar skills

All skills for Agent setup: memory, context, orchestration →

From the same set2

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor