# Google Workspace Gmail — Collab API

Gmail tools for InternalAI / HelloMCP — same contract as Calendar and Drive.

**Local:** `http://localhost:6010`  
**Prod:** `https://adhoc-mcp.lucos.com`

---

## Requirements coverage

| Requirement | Status | Tools |
|-------------|--------|-------|
| Search/read authorized employee mailboxes + threads | Done | `gmail_list_messages`, `gmail_list_threads`, `gmail_get_message`, `gmail_get_thread` |
| Recipients, timestamps, labels, message/thread IDs, attachments | Done | All read tools |
| Inbox, Sent, Archive, Trash | Done | `folder` arg: `inbox`, `sent`, `archive`, `trash`, `drafts`, `spam` |
| Resolve names → corporate email | Done | `args.user` / `args.to` via Directory + owner index |
| Draft, reply, forward, label, archive (approval-gated) | Done | `gmail_propose_*` → `gmail_confirm_write` |
| Secret redaction | Done | `GMAIL_REDACT_SECRETS` |
| Google Vault (retained/legal hold) | **Not this API** | Use Vault product separately when licensed |

---

## Server env

Add to `GOOGLE_DRIVE_SA_SCOPES` **and** Admin Console DWD for the service account client ID:

```bash
# Read
https://www.googleapis.com/auth/gmail.readonly

# Sends / replies / forwards
https://www.googleapis.com/auth/gmail.send

# Label changes, archive (remove INBOX)
https://www.googleapis.com/auth/gmail.modify
```

Also enable **Gmail API** in Google Cloud Console for the project.

```bash
GMAIL_WRITES_ENABLED=false
GMAIL_WRITE_APPROVAL_TTL_SEC=600
GMAIL_REDACT_SECRETS=true
GMAIL_MAX_BODY_CHARS=12000
```

Pending write tokens (`gmail_propose_*` → `gmail_confirm_write`) are held **in memory** (process Map) until confirm/cancel/TTL. They are not stored in SQLite. Lost on process restart — propose again.

Reuse identity vars: `DRIVE_INDEX_ADMIN_EMAIL`, `DRIVE_IMPERSONATE_DOMAINS`, `LUCOS_USER_GOOGLE_MAP`.

**Domains / multi-Workspace:** Impersonation is limited to `DRIVE_IMPERSONATE_DOMAINS`. Domains on the **same** Google Workspace as the default SA (e.g. `admedia.com`, `imds.tv`) use `GOOGLE_SERVICE_ACCOUNT_FILE`. Separate Workspaces (`advertising.com`, `cmoroom.com`) need their own SA + Domain-Wide Delegation:

```bash
DRIVE_IMPERSONATE_DOMAINS=admedia.com,imds.tv,advertising.com,cmoroom.com
GOOGLE_WORKSPACE_TENANTS_FILE=./config/google-workspace-tenants.json
```

Copy `config/google-workspace-tenants.example.json` → `google-workspace-tenants.json`, set each tenant’s `admin_email` if known, and place `secrets/advertising-sa.json` / `secrets/cmoroom-sa.json` after Admin setup. Tenant `scopes` must match that Workspace’s DWD (Gmail + Drive for InternalAI). Until the SA file exists + DWD is authorized, that domain fails with a clear config/auth error (AdMedia/`imds.tv` keep working).

---

## Read tools

| Tool | Purpose |
|------|---------|
| `gmail_list_users` | Workspace Directory users (Admin SDK) |
| `gmail_list_labels` | List labels for a user |
| `gmail_list_messages` | Search/list messages (metadata) |
| `gmail_get_message` | Message metadata + text window (skips fat MIME) |
| `gmail_list_threads` | List thread ids |
| `gmail_get_thread` | Full thread with all messages |
| `gmail_get_attachment` | Download attachment + extract text (DOCX, PDF, XLSX) |

### Common args

- `user` / `as_user` — whose mailbox (name or email; default = requester)
- `query` — Gmail search syntax
- `folder` — `inbox`, `sent`, `trash`, `archive`, `drafts`, `spam`
- `from`, `to`, `after`, `before`, `has_attachment`, `is_unread`
- `limit` — default 25, max 100
- `after` — pagination token (`next_after` from prior response)

---

## Write tools (propose → confirm)

| Propose | Action |
|---------|--------|
| `gmail_propose_send` | New email |
| `gmail_propose_reply` | Reply (`reply_all` optional) |
| `gmail_propose_forward` | Forward |
| `gmail_propose_draft` | Create draft |
| `gmail_propose_modify_labels` | Add/remove labels; `archive=true` removes INBOX |
| `gmail_confirm_write` | Execute pending token |
| `gmail_cancel_write` | Cancel pending token |

Requires `GMAIL_WRITES_ENABLED=true` and matching Gmail write scopes in DWD.

---

## Example curls

```bash
BASE=http://localhost:6010
ORG=00000000-0000-0000-0000-000000000001
USER=yogesh.kaurav@admedia.com
```

### List inbox

```bash
curl -sS -X POST "$BASE/internal/v1/tools/gmail_list_messages" \
  -H 'Content-Type: application/json' \
  -H "X-Lucos-Org-Id: $ORG" \
  -H "X-Lucos-User-Email: $USER" \
  --data-binary '{"environment":"staging","args":{"user":"me","folder":"inbox","limit":10}}'
```

### Search sent mail from Nathan

```bash
curl -sS -X POST "$BASE/internal/v1/tools/gmail_list_messages" \
  -H 'Content-Type: application/json' \
  -H "X-Lucos-Org-Id: $ORG" \
  --data-binary '{"environment":"staging","args":{"user":"yogesh.kaurav@admedia.com","folder":"sent","from":"Nathan","limit":5}}'
```

### Get full thread

```bash
curl -sS -X POST "$BASE/internal/v1/tools/gmail_get_thread" \
  -H 'Content-Type: application/json' \
  -H "X-Lucos-Org-Id: $ORG" \
  --data-binary '{"environment":"staging","args":{"user":"me","thread_id":"THREAD_ID"}}'
```

### Read attachment text (DOCX / PDF / XLSX)

Use `attachment_id`, `filename`, and `mime_type` from `gmail_get_message` → `attachments[]`.
Text is extracted automatically (not base64). Pass `offset` when `next_offset` is returned.

```bash
curl -sS -X POST "$BASE/internal/v1/tools/gmail_get_attachment" \
  -H 'Content-Type: application/json' \
  -H "X-Lucos-Org-Id: $ORG" \
  -H "X-Lucos-User-Email: $USER" \
  --data-binary '{
    "environment":"staging",
    "args":{
      "user":"me",
      "message_id":"MESSAGE_ID",
      "attachment_id":"ATTACHMENT_ID",
      "filename":"REBATES SECURITY DEVELOPMENT & VALIDATION REPORT.docx",
      "mime_type":"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    }
  }'
```

Limits (env): `GMAIL_MAX_ATTACHMENT_BYTES` (default 10 MiB download), `GMAIL_MAX_ATTACHMENT_TEXT_BYTES` (default 256 KiB per text window).

### Propose send → confirm

```bash
curl -sS -X POST "$BASE/internal/v1/tools/gmail_propose_send" \
  -H 'Content-Type: application/json' \
  -H "X-Lucos-Org-Id: $ORG" \
  -H "X-Lucos-User-Email: $USER" \
  --data-binary '{"environment":"staging","args":{"user":"me","to":"bob@admedia.com","subject":"Test","body":"Hello from collab API"}}'

curl -sS -X POST "$BASE/internal/v1/tools/gmail_confirm_write" \
  -H 'Content-Type: application/json' \
  -H "X-Lucos-Org-Id: $ORG" \
  --data-binary '{"environment":"staging","args":{"approval_token":"gmail_write_..."}}'
```

---

## Privacy

- Bodies/snippets redact likely secrets (API keys, bearer tokens, password fields) when `GMAIL_REDACT_SECRETS=true`.
- Email addresses in body text are partially masked.

---

## Google Vault

Vault export/search for legal hold and long-retention mail is **out of scope** for these tools. Use Google Vault when licensed and legally authorized; do not route Vault through this collab API unless a separate integration is built.
