name: google-chat description: Use when the user wants to send Google Chat messages, list Chat spaces, manage the Chat poller, check Chat connectivity, or troubleshoot Google Chat integration. Covers MCP tools for messaging, space management, OAuth setup, and the poller that auto-responds to DMs and @mentions.
Google Chat
Your Gallia workspace includes a Google Chat integration that lets you send and receive messages through the Google Chat REST API. A bot user in your Google Workspace authenticates via OAuth and can auto-respond to DMs and @mentions using Claude Code.
Setup (New Installation)
1. Create a Google Cloud Project
- Go to Google Cloud Console and create a new project
- Enable the Google Chat API (APIs & Services > Library > search "Google Chat API")
- Go to APIs & Services > Credentials > Create Credentials > OAuth 2.0 Client ID
- Application type: Web application
- Add an authorized redirect URI:
https://<your-gallia-url>/proxy/3456/callback - Save the Client ID and Client Secret
2. Create a Bot User Account
Create a user in your Google Workspace Admin console (e.g., [email protected]). This user will be the "face" of your AI assistant in Google Chat. Other users will DM this account or @mention it in spaces.
3. Initialize OAuth Credentials
Create the config file with your OAuth credentials:
mkdir -p ~/.config
cat > ~/.config/gchat-oauth.json << 'EOF'
{
"clientId": "<your-client-id>",
"clientSecret": "<your-client-secret>",
"redirectUri": "https://<your-gallia-url>/proxy/3456/callback"
}
EOF
Then run the login flow:
cd ~/gallia/gchat && node oauth-login.js
Visit the URL it prints, log in as your bot account, and click Allow. The refresh token is saved automatically.
4. Configure Bot Identity (Optional)
Create a .env file to customize the bot's behavior:
cat > ~/gallia/gchat/.env << 'EOF'
GCHAT_BOT_NAME=MyBot
GCHAT_SYSTEM_PROMPT=You are MyBot, an AI assistant responding in Google Chat. Keep responses concise.
CLAUDE_WORK_DIR=/home/adom/project
EOF
See gchat/.env.example for all available environment variables.
5. Start the Poller
cd ~/gallia/gchat && node poller.js
Or in background:
cd ~/gallia/gchat && nohup node poller.js > /tmp/gchat-poller.log 2>&1 &
6. Verify in Gallia Viewer
Open the Google Chat view in Gallia Viewer (Tools > Google Chat). The health bar should show "Connected" with your space count. Try sending a test message to verify everything works.
Architecture
Google Chat <-> Chat REST API (OAuth) <-> Poller (node poller.js) <-> Claude Code CLI
<-> MCP Tools (adom-gchat) <-> Claude Code session
Two operational modes:
- Poller (primary) —
poller.jspolls all spaces every 5s, detects new messages, spawns Claude Code, sends responses - MCP tools (interactive) — Claude Code sends messages on-demand via
gchat_send_message,gchat_send_card, etc.
Available MCP Tools
All tools are registered under the adom-gchat MCP server.
gchat_send_message — Send a Message
Send a text message to any Google Chat space.
| Parameter | Type | Description |
|---|---|---|
space | string | Space resource name (e.g. "spaces/AAAA1234") for Chat API, or webhook space name (e.g. "general") for legacy webhooks |
text | string | Message text. Supports Google Chat formatting: *bold*, _italic_, ~strikethrough~, `code`, triple backticks |
threadKey | string? | Optional thread key for threading messages |
gchat_send_card — Send a Rich Card
Send a structured card message with headers, sections, and widgets.
| Parameter | Type | Description |
|---|---|---|
space | string | Space resource name or webhook space name |
card | object | Card v2 object with header, sections, widgets |
fallbackText | string? | Plain text shown in notifications |
threadKey | string? | Optional thread key |
gchat_list_spaces — List Spaces
Lists all Google Chat spaces the bot has access to. Shows both Chat API spaces (where the bot is a member) and legacy webhook spaces.
gchat_add_space — Register Webhook Space
Register a legacy webhook URL for a space. Not needed for spaces where the bot is already a member — use the space name from gchat_list_spaces instead.
| Parameter | Type | Description |
|---|---|---|
name | string | Short name for the space (e.g. "general", "eng") |
webhookUrl | string | Full webhook URL from Google Chat (starts with https://chat.googleapis.com/) |
gchat_remove_space — Remove Webhook Space
Remove a configured webhook space by name.
gchat_health — Check Connectivity
Check Google Chat connectivity: OAuth token status, Chat API reachability, and webhook configuration.
Message Flow (Poller)
When the poller detects a new message:
- Detect — Poll
listMessageswithorderBy=createTime descto get newest messages first - Filter — Skip the bot's own messages. In DMs, process all human messages. In groups, require @mention.
- Thinking — Send
"On it, {name}..."placeholder message - Heartbeat — Update thinking message every 30s with elapsed time while Claude works
- Respond — Update thinking message to
"Done in {time}", send Claude's response as a new message (triggers unread indicator) - Error — If Claude fails, send an error message with details
Configuration
Environment Variables
All configuration is via environment variables, set in ~/gallia/gchat/.env or exported in your shell. See .env.example for the full list.
| Variable | Default | Description |
|---|---|---|
GCHAT_CONFIG_PATH | ~/.config/gchat-oauth.json | OAuth credentials file |
GCHAT_WEBHOOKS_PATH | ~/.config/gchat-webhooks.json | Legacy webhook config |
GCHAT_STATE_PATH | ~/.config/gchat-poller-state.json | Poller state file |
GCHAT_BOT_NAME | Bot | Display name for @mention matching and logs |
GCHAT_BOT_USER_ID | (auto-match) | Google user ID for the bot (optional) |
GCHAT_SYSTEM_PROMPT | Generic assistant prompt | System prompt for Claude |
GCHAT_POLL_INTERVAL | 5000 | Poll interval in ms |
CLAUDE_BIN | claude | Path to Claude CLI |
CLAUDE_WORK_DIR | cwd | Working directory for Claude |
OAUTH_REDIRECT_URL | http://localhost:3456/ | OAuth redirect URL for login flow |
OAuth Credentials
File: ~/.config/gchat-oauth.json (or GCHAT_CONFIG_PATH)
Contains clientId, clientSecret, refreshToken, accessToken, expiresAt. Tokens auto-refresh when expired.
OAuth scopes: chat.messages, chat.messages.create, chat.spaces, chat.spaces.readonly, chat.memberships, chat.memberships.readonly
Webhook Config (Legacy)
File: ~/.config/gchat-webhooks.json (or GCHAT_WEBHOOKS_PATH)
Legacy webhook URLs for spaces. Used by MCP tools as a fallback when the bot isn't a member of a space. Register webhooks via gchat_add_space or manually edit the file.
Poller State
File: ~/.config/gchat-poller-state.json (or GCHAT_STATE_PATH)
Tracks lastSeen timestamp per space and caches user display names. The poller initializes new spaces to "now" to avoid processing old messages on first start.
DM Acceptance Requirement
When someone DMs the bot for the first time, that DM must be "accepted" before the bot can reply:
- Bot initiating: Use
spaces:setupAPI — the recipient must accept in Google Chat UI - User initiating: The bot must accept the DM in Google Chat UI (log in as the bot account)
- Without acceptance: API can read messages but cannot send (403 error)
- After acceptance: Full read/write works permanently
Claude Code Integration
- Claude CLI path: configurable via
CLAUDE_BINenv var - Working directory: configurable via
CLAUDE_WORK_DIRenv var - Must unset
CLAUDECODEenv var when spawning (avoids nested detection) - Uses
script -qcfor pseudo-TTY (required by Claude CLI) - ANSI escape codes are stripped from output
- Response truncated to 4000 chars (Google Chat limit)
Google Chat Formatting
*bold* _italic_ ~strikethrough~ `code` ```code block```
Troubleshooting
Bot doesn't respond to new messages
- Check the poller is running:
pgrep -f 'node poller' - Check poller logs:
tail -f /tmp/gchat-poller.log - Verify OAuth tokens: use
gchat_healthMCP tool - For new DM spaces — the poller refreshes spaces every ~5 min, or restart the poller
403 error on DMs
The DM hasn't been accepted yet. Log into Google Chat as the bot account and accept the DM, or have the bot initiate the DM via spaces:setup.
Messages detected but Claude errors
- Check for "Session ID already in use" — should be fixed with
--no-session-persistence - Check Claude CLI is installed and on PATH (or set
CLAUDE_BIN) - Check
CLAUDECODEenv var is being unset in the spawned process
Old messages being re-processed
The poller uses orderBy=createTime desc to fetch newest messages first. If old messages are being processed, the lastSeen timestamp in the poller state file may be stale. Delete the state file and restart the poller.
Files
| File | Purpose |
|---|---|
gchat/chat-api.js | Google Chat REST API client with OAuth token auto-refresh |
gchat/poller.js | Main polling loop — detects messages, spawns Claude, sends responses |
gchat/claude-runner.js | Spawns Claude Code CLI via script -qc PTY wrapper, strips ANSI |
gchat/server.js | Legacy webhook HTTP server on port 8780 |
gchat/oauth-login.js | One-time OAuth login flow (port 3456) |
gchat/load-env.js | Minimal .env file loader (no dependencies) |
gchat/mcp/server.js | MCP stdio transport entry point |
gchat/mcp/tools.js | MCP tool definitions (6 tools) |
gchat/.env.example | Template for environment variables |