Skip to main content
Glama
thrix

gcloud-workspace-mcp

by thrix

gcloud-workspace-mcp

An MCP server for Google Docs, Drive and Gmail that authenticates through your local gcloud CLI. It doesn't need an OAuth client of its own.

Why it exists

Some Google Workspace tenants block every third-party OAuth client. Other Workspace MCP servers ask you to register or reuse an OAuth client ID, and on those tenants the sign-in stops at an "access blocked by your admin" screen. Google's own Cloud SDK client usually stays allowed. So this server borrows the access tokens that gcloud mints on your machine and calls the Google REST APIs over HTTPS.

If your tenant lets you register an OAuth app, a full-featured server such as google_workspace_mcp will cover more of the API. Use this one when gcloud is the only client you can log in with.

The only dependency is the mcp SDK. The HTTP layer uses the Python standard library.

Related MCP server: Google Docs & Gmail MCP Server

Two credential stores

gcloud keeps two token stores, and you can't swap one for the other. A 403 usually means a call used the wrong one.

Surface

Store

Token command

Quota header

Docs & Drive

user

gcloud auth print-access-token

not needed

Gmail

ADC

gcloud auth application-default print-access-token

required

One-time setup

Install the Google Cloud SDK so gcloud is on your PATH. Then grant the scopes. The server never runs these commands for you.

Docs and Drive (user credentials):

# Grants the full drive scope. gcloud has no read-only variant.
gcloud auth login --enable-gdrive-access

Gmail (Application Default Credentials):

gcloud auth application-default login \
  --scopes=https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/gmail.readonly
gcloud auth application-default set-quota-project <your-quota-project>

Keep cloud-platform in the list if other tools on your machine use ADC for Google Cloud. The login rewrites ~/.config/gcloud/application_default_credentials.json in place, so back it up first if you care about its current scopes.

Install and register

You need uv. Register the server in Claude Code:

claude mcp add -s user gcloud-workspace \
  -e GCLOUD_QUOTA_PROJECT=<your-quota-project> \
  -- uvx --from git+https://github.com/thrix/gcloud-workspace-mcp gcloud-workspace-mcp

For another client, add this to its MCP config:

{
  "mcpServers": {
    "gcloud-workspace": {
      "command": "uvx",
      "args": [
        "--from", "git+https://github.com/thrix/gcloud-workspace-mcp",
        "gcloud-workspace-mcp"
      ],
      "env": { "GCLOUD_QUOTA_PROJECT": "<your-quota-project>" }
    }
  }
}

Pin a release by appending @v0.1.0 to the git URL.

Container

CI publishes a multi-arch image (amd64, arm64) to ghcr.io/thrix/gcloud-workspace-mcp. It holds the server and the Google Cloud SDK on a distroless Hummingbird Python image. You still do the one-time gcloud logins on your host. The container reads and refreshes the tokens through a mount of your gcloud config directory.

mkdir -p -m 700 "$HOME/.cache/gcloud-workspace-mcp/downloads"
claude mcp add -s user gcloud-workspace -- \
  podman run -i --rm --userns=keep-id:uid=65532,gid=65532 \
    -v "$HOME/.config/gcloud:/gcloud:z" \
    -v "$HOME/.cache/gcloud-workspace-mcp/downloads:$HOME/.cache/gcloud-workspace-mcp/downloads:z" \
    -e GCLOUD_WORKSPACE_DOWNLOAD_DIR="$HOME/.cache/gcloud-workspace-mcp/downloads" \
    -e GCLOUD_QUOTA_PROJECT=<your-quota-project> \
    ghcr.io/thrix/gcloud-workspace-mcp:latest

--userns=keep-id maps your host user to the image's UID 65532, so gcloud can write refreshed tokens back to your config. The downloads directory is mounted at the same path it has on your host, so the paths the server returns work outside the container too. Pass -e GCLOUD_WORKSPACE_ENABLE_WRITES=1 to enable the write tools.

Tag

Points at

latest, 0.1, 0.1.0

the newest release, and each release line

main

the tip of main

sha-<commit>

one commit on main or a release tag

Each published image carries a signed build provenance attestation. Check it with gh attestation verify oci://ghcr.io/thrix/gcloud-workspace-mcp:latest --owner thrix.

To build the image yourself, run podman build -t gcloud-workspace-mcp . and use gcloud-workspace-mcp as the image name. To pick a different SDK release, pass --build-arg GCLOUD_VERSION=<version> together with that release's GCLOUD_SHA256_X86_64 and GCLOUD_SHA256_ARM. The build checks the tarball against them.

Environment

Variable

Meaning

GCLOUD_QUOTA_PROJECT

ADC quota project, sent as x-goog-user-project. Gmail needs it.

GCLOUD_WORKSPACE_ENABLE_WRITES

Set to 1 to register the write tools. See Writes.

GCLOUD_WORKSPACE_DOWNLOAD_DIR

Where binary downloads and raw Gmail messages go. Defaults to $XDG_CACHE_HOME/gcloud-workspace-mcp/downloads, or ~/.cache/... without XDG_CACHE_HOME.

Tools

Drive and Docs use the user credentials:

Tool

What it does

drive_search(query, page_size=20, page_token="")

Search with a raw Drive q query across My Drive, files shared with you and every shared drive you belong to. Returns {files, nextPageToken, incompleteSearch}; incompleteSearch: true means Drive skipped some shared drives.

drive_get_metadata(file_id)

Metadata for one file: owners, size, link, parents.

docs_export(file_id, mime_type="text/markdown", offset=0)

Export a Google-native file as text/markdown, text/plain, text/html, text/csv or application/pdf. Docs take every type but text/csv. Sheets take text/csv, which holds the first sheet only, or PDF. Slides take text/plain or PDF. Drive caps exports at 10 MB.

drive_download(file_id, offset=0)

Download an uploaded file (PDF, xlsx, txt, images), up to 10 MB.

Text comes back 50,000 characters at a time. When there is more, the result ends with a note that gives the offset for the next call.

PDF exports and binary downloads don't go into the tool result, since base64 would blow past the client's output limit. The server saves them to the download directory as <file id>.<ext> and returns {path, name, mimeType, bytes}. Claude Code can open the PDF or image at that path with its Read tool. The directory is created with mode 0700 and each file with 0600. A second download of the same file overwrites the first.

Gmail uses ADC plus the quota header:

Tool

What it does

gmail_search(query, max_results=20, page_token="")

Search with Gmail syntax. Returns {messages, nextPageToken, resultSizeEstimate}. The estimate stops at 201, so page through results to count them.

gmail_get_message(message_id, format="text")

text gives you {id, threadId, labelIds, headers, body, attachments}, with the body decoded from text/plain or from tag-stripped text/html, in the charset the part declares. metadata, minimal and full return the Gmail resource unchanged, up to 1 MB. raw saves the whole message, attachments included, to <message id>.eml in the download directory (up to 10 MB) and returns the resource with path, mimeType and bytes in place of the base64.

gmail_get_thread(thread_id, format="text")

Every message in a thread, oldest first, decoded the same way. The bodies share a 100,000-character budget, filled from the newest message back. Older messages past it keep their headers, get body: null, and appear in omittedBodies so you can fetch them with gmail_get_message. The resource formats cap the whole thread at 1 MB.

Writes

The write tools stay unregistered until you set GCLOUD_WORKSPACE_ENABLE_WRITES=1, so your MCP client can't see or call them by default.

Tool

What it does

docs_create_from_markdown(title, markdown, parent_id="")

Create a Google Doc. Drive converts the markdown to native formatting.

docs_replace_content(file_id, markdown)

Replace a Doc's whole body with converted markdown. Drive keeps the old content in version history. Refuses any file that isn't a native Google Doc.

docs_append_text(file_id, text)

Append plain text to the end of a Doc through the Docs API.

gmail_create_draft(to, subject, body, cc="", thread_id="")

Create a plain-text draft, threaded as a reply when you pass thread_id. You send it yourself from Gmail.

The server offers no tool that sends mail, deletes files or moves them to the trash.

Drafts need one more ADC scope:

gcloud auth application-default login \
  --scopes=https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/gmail.readonly,https://www.googleapis.com/auth/gmail.compose

Prompt injection. The model reads your documents and mail, and anyone who can share a doc or send you an email can put instructions in it. With writes on, a poisoned doc could get the model to overwrite another doc or draft a message to someone. Turn writes on when you need them and review what the model changes.

Security

  • The server gets tokens only by reading the stdout of the gcloud subprocess. It never puts a token on a command line.

  • It never logs a token or returns one in a tool result or error message.

  • It attaches a token only to https URLs on a *.googleapis.com host. It checks this before it mints the token, checks every redirect target the same way, and refuses every other URL.

See SECURITY.md to report a vulnerability.

Limitations

  • You need a logged-in gcloud on the same machine. The server can't run an interactive login. When gcloud asks for reauthentication, the tool error tells you, and you re-run the login. The server gives gcloud no stdin, so a prompt fails instead of waiting, and it stops a gcloud call after 60 seconds.

  • print-access-token doesn't report an expiry. The server assumes a 55-minute lifetime, refreshes 5 minutes early, and retries once after a 401.

  • Gmail calls need GCLOUD_QUOTA_PROJECT, and they fail with 403 accessNotConfigured until that project enables the Gmail API. Drive and Docs calls don't send that header. They bill the Cloud SDK's own project, and GCLOUD_QUOTA_PROJECT can't enable an API for them.

  • Message bodies get cut at 50,000 characters, and a thread's bodies at 100,000 in total. Attachments come back as names and sizes only. HTML-only mail keeps table cells apart and shows each link target after its text.

License

MIT

Related MCP Connectors

Related MCP Servers