# Google Chat MCP [Health: Active]

**Category:** 💬 Communication  
**Repository:** https://github.com/A1-x-Tech/mcp-google-chat  
**GitHub Stars:** 0  
**npm Downloads (last month):** 1006  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/google-chat-mcp

## Description
MCP server for the Google Chat API: spaces, messages, threads, reactions and memberships.

## Tools
Capabilities this server exposes over MCP:

- **auth_status** — Shows whether this server is connected to Google: token presence and source (env variables or a stored in-chat login), expiry, the Google account email, granted vs missing OAuth scopes, where the credentials file lives and where the OAuth client comes from. Makes no network calls and never returns the token itself. Call it first when other tools report the server is not connected.
- **setup_instructions** — Step-by-step checklist for connecting this server to Google: creating a Google Cloud project and a Desktop-app OAuth client, publishing the consent screen (mandatory — Testing-mode refresh tokens die after 7 days), downloading the client JSON and handing its PATH to set_client. Works without any credentials; the checklist shortens to 'enable the API + log in' when an OAuth client is already configured (one client serves the whole mcp-google-* line). Never asks the user to paste secrets into the chat.
- **set_client** — Saves the OAuth client credentials from the JSON file downloaded from Google Cloud Console ('Download JSON' on a Desktop-app client). Pass the file PATH — the secret must never be pasted into the chat. The client is stored once in the shared ~/.config/mcp-google-auth/client.json (owner-only) and reused by every mcp-google-* server; tokens stay per-server. After this, call start_login.
- **start_login** — First step of connecting from the chat, without editing config files or restarting the client. Returns authorizeUrl — show it to the user as a clickable link and ask them to open it in the browser ON THIS MACHINE, pick the Google account and approve access. A one-shot listener on 127.0.0.1 catches Google's redirect; the code is exchanged locally and never passes through the chat. Does not open the browser itself. The attempt lives 10 minutes; when the browser shows the success page, call finish_login.
- **finish_login** — Second step: waits up to 20 s for the browser consent to finish, saves the tokens to an owner-only file and verifies the login with a read-only identity call, returning the account email and the granted scopes. After success every tool works immediately — no client restart. If the user granted only part of the requested permissions, the login is still saved and missingScopes lists what will not work. Logging in under a different Google account replaces the previous login (its refresh token is revoked best-effort) and the response carries previousAccountEmail so the change never goes unnoticed.
- **logout** — Revokes the stored token at Google (oauth2.googleapis.com/revoke) and deletes the local credentials file. Tokens supplied via env variables are NOT touched — remove them from the MCP client config manually; envTokenStillSet in the response says whether any are still in effect.
- **list_spaces** — Lists the spaces the authenticated user is a member of: name (spaces/<id>), displayName (empty for direct messages), spaceType (SPACE = named room, GROUP_CHAT, DIRECT_MESSAGE), spaceThreadingState and timestamps. This is the discovery entry point — space names from here feed every other tool. space_type narrows the listing server-side; there is no text search here — match displayName client-side, or use search_spaces (Workspace admin only). Paginate with page_token from nextPageToken; results are unordered.
- **get_space** — Returns one space's details: displayName, spaceType, spaceDetails (description/guidelines), spaceThreadingState (THREADED_MESSAGES = replies go into threads, otherwise the space is flat), membershipCount, createTime and settings. Use it to check the threading model before send_message with a thread, or to confirm a space id before writing into it.
- **search_spaces** — Server-side search over ALL named spaces in the Workspace organization — including ones the caller is not a member of. ADMIN-ONLY: the call runs with useAdminAccess=true and requires a Google Workspace administrator authorized with the chat.admin.spaces or chat.admin.spaces.readonly scope; anyone else gets PERMISSION_DENIED — fall back to list_spaces and match displayName client-side. query uses the API's search syntax and MUST contain customer = "customers/my_customer" AND spaceType = "SPACE"; add displayName:"text" for name search, e.g. customer = "customers/my_customer" AND spaceType = "SPACE" AND displayName:"onboarding". order_by accepts membership_count.joined_direct_human_user_count, last_active_time or create_time with ASC/DESC.
- **find_direct_message** — Finds the EXISTING direct-message space between the authenticated user and another user, returning the space (name spaces/<id>) to send_message into. Returns HTTP 404 when no DM with that user exists yet — this tool cannot create one (creating DMs needs spaces.setup via raw_request). The user can be a Google user id (users/123...) or an email address.
- **list_messages** — Lists messages in a space (including messages from blocked members and spaces): name, text, sender, createTime, thread.name, attachment metadata and emoji reaction summaries. Filters are the API's only two: created_after (createTime) and thread_name (one thread's messages) — there is no text search, match client-side. order defaults to ascending by createTime; show_deleted includes tombstones of deleted messages. Poll incrementally with created_after + page_token instead of re-listing history. Requires the chat.messages.readonly (or chat.messages) scope and works only with user authentication.
- **get_message** — Fetches one message by its full resource name: text, formattedText, sender, createTime/lastUpdateTime, thread.name (reply target for send_message), attachment[] metadata (name, contentName, contentType, downloadUri, attachmentDataRef) and emojiReactionSummaries. Also resolves custom-id names (spaces/<space>/messages/client-<id>) for messages sent with message_id. Deleted messages return deletionMetadata instead of content.
- **send_message** — Sends a text message to a space AS THE AUTHENTICATED USER (their name and avatar; needs the chat.messages.create or chat.messages scope, and the user must be a member of the space). Text supports Chat markup: *bold*, _italic_, ~strike~, `code`, <https://url|link>, <users/123> mentions. Threads: pass thread_name (from a message's thread.name) or a stable thread_key of your choosing to reply in a thread; by default a missing thread falls back to starting a new one — set reply_option="or_fail" to error instead. In non-threaded spaces the thread params are ignored by the API. message_id (must start with "client-") makes the send addressable later without storing the returned name — reuse of an id fails with ALREADY_EXISTS, which also makes accidental duplicate sends detectable. Returns the created message with name, thread.name and createTime. A send is NEVER retried after a 5xx/timeout: check with list_messages before re-sending. Cards (cardsV2) are app-auth-only — out of scope; use raw_request with a Chat-app token.
- **update_message** — Replaces the text of an existing message (updateMask=text; the previous text is overwritten, not appended). With user authentication only the authenticated user's OWN messages can be edited — editing someone else's returns PERMISSION_DENIED, a Chat rule, not a missing scope. The message keeps its name and thread; lastUpdateTime is set and clients show an Edited marker. Updating cards or accessory widgets needs app auth via raw_request.
- **delete_message** — Permanently deletes a message. With user authentication the user can delete their OWN messages; space managers can also delete others' messages in spaces they manage — otherwise PERMISSION_DENIED (a Chat permission rule, not a missing scope). If the message started a thread that has replies, the delete fails with FAILED_PRECONDITION; force=true deletes the replies too but works only with app (service-account) authentication. Deletion cannot be undone — read the message with get_message first if its content matters. Returns an empty object on success.
- **manage_reactions** — Emoji reactions on a message, as the authenticated user. action=add puts a unicode emoji (the emoji character itself, e.g. "👍" or "🎉" — not :shortcode:) on the message; adding the same emoji twice fails with ALREADY_EXISTS. action=list returns who reacted with what — each reaction's name (spaces/.../reactions/<id>), emoji and user; filter with emoji to one emoji's reactions. action=remove deletes ONE reaction by its full reaction_name from list — only the authenticated user's own reactions can be removed (someone else's returns PERMISSION_DENIED). Custom (workspace) emoji need raw_request with emoji.customEmoji. Scopes: chat.messages.reactions (add/remove; .create suffices for add-only) or chat.messages.reactions.readonly (list).
- **get_attachment** — Fetches metadata of one message attachment by its resource name (from a message's attachment[].name): contentName (filename), contentType (MIME), source (DRIVE_FILE or UPLOADED_CONTENT), downloadUri/thumbnailUri (short-lived, for a signed-in browser user — not for server-side download) and attachmentDataRef. NOTE the auth split: this dedicated endpoint accepts only APP (service-account) authentication — with user credentials it returns an error, but the SAME metadata is already embedded in get_message's attachment[] field, so user-auth flows should read it there. Downloading raw bytes goes through the media endpoint (v1/media/<resourceName>?alt=media) and uploading new attachments through the upload endpoint — both outside this server's tools.
- **list_members** — Lists memberships in a space: each membership's name (spaces/<space>/members/<member> — the handle manage_members needs), member (users/<id>, displayName, type HUMAN|BOT), role (ROLE_MEMBER or ROLE_MANAGER) and state (JOINED, INVITED, NOT_A_MEMBER). role filters to managers or members; show_invited includes invited-but-not-joined users, show_groups includes Google Groups. Requires the chat.memberships.readonly (or chat.memberships) scope and membership in the space.
- **manage_members** — Membership management in a space — WORKS ONLY WITH SUFFICIENT ACCESS: the authenticated user needs the chat.memberships scope, and add/update_role/remove additionally require them to be a space MANAGER (otherwise PERMISSION_DENIED — a Chat role rule, not a network problem; check their role via list_members). action=get reads one membership by member_name. action=add invites/adds a human user (space + user, optional role=manager); in DMs and group chats members cannot be added. action=update_role switches a membership between member and manager (member_name + role). action=remove deletes the membership — the user is kicked from the space immediately and this is not undoable from here (re-add creates a fresh invitation). Google Groups and Chat-app memberships are managed via raw_request.
- **raw_request** — Escape hatch to call any Google Chat API v1 path directly, for requests the typed tools don't cover — e.g. creating a space (path "v1/spaces", method POST, body {"spaceType":"SPACE","displayName":"..."}), setting up a DM ("v1/spaces:setup"), custom-emoji reactions, Google Group memberships, space events ("v1/spaces/<id>/spaceEvents"), or updating a space ("v1/spaces/<id>" PATCH with a query updateMask). The path may carry a query string (e.g. "v1/spaces/AAA/members?showInvited=true"). The Bearer token is added automatically; the method defaults to GET. Not for media: attachment upload/download use different endpoints (upload/v1, media/v1) that this server does not proxy.

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `npx` (confidence: high):

```json
"mcpServers": {
  "google-chat-mcp": {
    "command": "npx",
    "args": ["-y","mcp-google-chat@latest"]
  }
}
```

## Documentation

## What Google Chat MCP does

Google Chat MCP gives an AI client tools for working with Google Chat spaces, direct messages, messages, threads, reactions, memberships, and attachment metadata. It includes 20 tools for discovering conversations, catching up on messages, posting replies, maintaining messages, checking reactions, and managing who belongs to a space.

The server acts through the Google Chat API using the signed-in Google account. Messages are posted under that user's identity. Editing and deletion are limited to the user's own messages and reactions, while membership changes require the user to be a space manager.

## How it works

The server uses OAuth credentials and keeps its tokens itself. A user can start the connection by asking the AI client to connect Google Chat; the flow guides OAuth client setup, receives Google's redirect on `127.0.0.1`, and uses PKCE. No configuration file or restart is required for this connection flow.

Google Chat resource names matter when acting on messages, threads, members, reactions, and attachments. A typical workflow is to list spaces or messages first, then use the exact resource name returned by the API. Replies can target a thread name or thread key. Sending normally falls back to a new thread when the target cannot be threaded, with an option to fail instead.

Read operations do not change Chat data. Sending, editing, deleting, reacting, and changing membership do. The server marks read, write, and destructive tools so the client can apply its own confirmation prompts. A failed write is not retried after an ambiguous result, preventing an uncertain send from being replayed as a duplicate.

## Setup and configuration

Node.js 20 or newer and a Google account with access to Google Chat are required. The package can be added as an stdio MCP server with:

```bash
npx -y mcp-google-chat@latest
```

Codex, Claude Code, Claude Desktop, Cursor, and VS Code each have documented stdio configurations for the package. After adding it to the client, initiate the OAuth flow from the conversation rather than supplying credentials during installation.

The server uses the scopes granted by the OAuth token. Read-only scopes are sufficient for browsing spaces and messages. A separate service-account access token can be supplied through `GOOGLE_CHAT_ACCESS_TOKEN` when using the Google Chat app configuration path.

## Tools and capabilities

- Discover spaces and existing direct messages.
- List messages and inspect conversation threads.
- Send messages with thread control.
- Update or delete the signed-in user's messages.
- Add or remove the signed-in user's reactions and inspect other reactions.
- Read attachment names, types, and related metadata.
- List members, identify space managers, and add, re-role, or remove members.
- Make an API call through `raw_request` for methods without a dedicated tool.

## Limitations and notes

Google Chat text search is not available through the Chat API. Message retrieval can be filtered by creation time and thread, but not by message content. Finding a direct message locates an existing conversation and does not create one.

Attachment files are not uploaded or downloaded; the server exposes attachment metadata only. Space creation and other API methods without dedicated tools require `raw_request`.

The standard flow acts as the signed-in user. Acting as a Google Chat app, including cards, app direct messages, the dedicated attachment endpoint, or forced deletes, requires separate Google Cloud configuration and the service-account token path. Membership operations also depend on manager permissions.

_Full upstream README: https://allmcps.com/mcp/google-chat-mcp/readme_

