# Connect Gmail with your own Google OAuth client

GhostGet’s Gmail adapter lists and reads your mail threads and contacts through Google’s Gmail and People APIs. You create the Google OAuth client and sign in once from your terminal. Your agent then calls three read-only actions with the account name you chose. GhostGet can’t send, draft, label, or delete mail.

This guide describes v0.18.82. Status on October 4, 2026: the Google Cloud console steps match Google’s [consent screen guide](https://developers.google.com/workspace/guides/configure-oauth-consent) and [Gmail API quickstart](https://developers.google.com/workspace/gmail/api/quickstart/python), the Claude connector details match Anthropic’s connector article and Claude Code’s MCP guide, and the sign-in was not run with a Google account for this guide. Run `ghostget capabilities gmail --json` for the actions installed on your computer.

## Choose Claude’s Gmail connector or GhostGet

If you work in Claude, Anthropic’s [Gmail connector](https://support.claude.com/en/articles/10166901-use-google-workspace-connectors) needs no Google Cloud project. Connect it at [claude.ai/customize/connectors](https://claude.ai/customize/connectors), and [Claude Code lists it in `/mcp`](https://code.claude.com/docs/en/mcp#use-mcp-servers-from-claude-ai) when you sign in with your Claude account. It searches and reads mail, drafts messages, and by default asks for your approval before it sends, replies, or forwards.

Use GhostGet when your agent is Codex, Cursor, or a script; when Claude Code signs in with an API key, because connectors load only with a Claude subscription sign-in; when you want the grant to belong to your own Google Cloud project with read-only scopes; or when you need contact lists with interaction counts and saved copies of threads with their attachments.

## What your agent can read

- `contacts.list` returns your saved Google Contacts, your Other contacts, or an `interactions` summary of the addresses you exchange mail with, including sent and received counts. The summary reads message headers, labels, and dates, never message text, and leaves out spam, trash, drafts, and chats.
- `messaging.list` returns your inbox or the results of one Gmail search, such as `from:example.com has:attachment`.
- `messaging.read` returns one thread, using the `thread_id` from `messaging.list`.

Reading a message doesn’t mark it as read. Each `messaging.list` row includes a `threadUrl`. Pass it to `ghostget read` with the same `--auth` name to print the thread as Markdown, or to `ghostget clip` to save the thread and its attachments in GhostGet’s private state folder.

GhostGet asks Google for three scopes: `gmail.readonly`, `contacts.readonly`, and `contacts.other.readonly`. Google classifies [`gmail.readonly` as a restricted scope](https://developers.google.com/workspace/gmail/api/auth/scopes): your consent lets the app read all of your mail, even though the `interactions` summary reads only headers.

## Create a Google OAuth desktop client

Use a Google Cloud project you own. These steps follow the Google Auth platform pages in the Google Cloud console.

1. Create a project, or choose an existing one, in the [Google Cloud console](https://console.cloud.google.com/).
2. Enable the Gmail API and the People API for the project.
3. Set up the consent screen under Google Auth platform › Branding. Choose External as the audience for a personal Gmail account.
4. Add your Google account as a test user under Google Auth platform › Audience.
5. Create a client under Google Auth platform › Clients, with Desktop app as the application type.
6. Download the client’s JSON file and keep it in a private folder.

```
chmod 600 /absolute/path/client_secret.json
```

GhostGet accepts only a Desktop app client. A Web application client stops sign-in with `Google OAuth client document contains unsupported field web`.

## Sign in from your terminal

Run these commands yourself in a terminal, not through your agent, so the sign-in never passes through the agent:

```
ghostget adapter sync-bundled --json
ghostget auth login gmail-main --client-file /absolute/path/client_secret.json
```

GhostGet opens Google’s consent page in your browser and waits up to 10 minutes for you to approve. Google sends the approval back to a temporary address at `127.0.0.1` on your computer, so finish sign-in in a browser on the same computer. GhostGet also prints the sign-in URL; add `--no-open` to skip opening the browser. While your app is in Testing, Google shows a warning before you grant access.

After you approve, GhostGet checks which Gmail address signed in, prints it, and renews access automatically. It keeps the credential in a file that only your user account can read, inside GhostGet’s state folder. That file isn’t encrypted or kept in your system keychain, so keep the state folder out of shared backups. `gmail-main` is the name your agent passes with `--auth`.

## Keep the connection past seven days

While your app’s publishing status is Testing, [Google ends each authorization seven days after consent](https://support.google.com/cloud/answer/15549945). To keep the connection, choose Publish app under Google Auth platform › Audience, then sign in again with `--force`:

```
ghostget auth login gmail-main --client-file /absolute/path/client_secret.json --force
```

A published app that asks for `gmail.readonly` shows Google’s unverified-app screen until Google verifies it. Google [doesn’t require verification](https://developers.google.com/identity/protocols/oauth2/production-readiness/restricted-scope-verification) for personal use, when you are the only user or your users are a few people you know.

If Google gives the credential an end date, GhostGet prints that date after sign-in, with a reminder to publish the app and sign in again with `--force`.

## Check the connection

```
ghostget gmail contacts.list --auth gmail-main \
  --input '{"collection":"contacts","limit":1,"include_stats":false}' --json
ghostget gmail messaging.list --auth gmail-main \
  --input '{"view":"inbox","limit":5}' --json
```

Then ask your agent, for example: “Use GhostGet with `--auth gmail-main` to list my five newest inbox threads.” An agent that runs commands in a sandbox needs network access first; see [Run GhostGet from Codex, Claude Code, or Cursor](https://ghostget.com/docs/tutorials/getting-started/#agents).

## Fix sign-in and renewal errors

- `Google OAuth client document contains unsupported field web`: the file belongs to a Web application client. Create a Desktop app client and download its JSON file.
- `Google authorization timed out before consent completed`: approve access within 10 minutes, then run `ghostget auth login` again.
- `Google did not issue a refresh token; revoke the prior app grant and repeat login`: remove the app from your Google Account’s [third-party connections](https://support.google.com/accounts/answer/13533235), then sign in again.
- `the managed Google refresh credential expired; run ghostget auth login again`: the end date GhostGet printed at sign-in has passed. Publish the app if it’s still in Testing, then sign in again with `--force`.
- `official provider returned HTTP 400 for POST /token`: Google refused to renew access, which its token endpoint reports as `invalid_grant`. Google’s [reasons](https://developers.google.com/identity/protocols/oauth2#expiration) include the seven-day limit for an app in Testing, removing the app’s access, changing your Google password while the app holds a Gmail scope, and six months without use. Publish the app if it’s still in Testing, then sign in again with `--force`.

## Disconnect Gmail

```
ghostget auth remove gmail-main --yes
```

This deletes GhostGet’s local credential. Google keeps your grant until you remove the app from your Google Account’s third-party connections.

## Review what your agent can reach

Browse [Gmail’s supported action names and access methods](https://ghostget.com/docs/reference/provider-capabilities/#provider-gmail), then read the [security model](https://ghostget.com/docs/explanation/security-model/) for how GhostGet ties each action to one account.
