How-to guide · Gmail adapter · v0.18.82
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 and Gmail API quickstart, 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 needs no Google Cloud project. Connect it at claude.ai/customize/connectors, and Claude Code lists it in /mcp 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.listreturns your saved Google Contacts, your Other contacts, or aninteractionssummary 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.listreturns your inbox or the results of one Gmail search, such asfrom:example.com has:attachment.messaging.readreturns one thread, using thethread_idfrommessaging.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: 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.
- Create a project, or choose an existing one, in the Google Cloud console.
- Enable the Gmail API and the People API for the project.
- Set up the consent screen under Google Auth platform › Branding. Choose External as the audience for a personal Gmail account.
- Add your Google account as a test user under Google Auth platform › Audience.
- Create a client under Google Auth platform › Clients, with Desktop app as the application type.
- Download the client’s JSON file and keep it in a private folder.
chmod 600 /absolute/path/client_secret.jsonGhostGet 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.jsonGhostGet 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. 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 --forceA published app that asks for gmail.readonly shows Google’s unverified-app screen until Google verifies it. Google doesn’t require 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}' --jsonThen 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.
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 runghostget auth loginagain.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, 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 asinvalid_grant. Google’s reasons 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 --yesThis deletes GhostGet’s local credential. Google keeps your grant until you remove the app from your Google Account’s third-party connections.