add-gmail-tool

Installation
SKILL.md

Add Gmail Tool (OneCLI-native)

This skill wires the @gongrzhe/server-gmail-autoauth-mcp stdio MCP server into selected agent groups. The MCP server reads stub credentials containing the onecli-managed placeholder; the OneCLI gateway intercepts outbound calls to gmail.googleapis.com and injects the real OAuth bearer from its vault.

Tools exposed (from gmail-mcp@1.1.11, surfaced to the agent as mcp__gmail__<name>): search_emails, read_email, send_email, draft_email, delete_email, modify_email, batch_modify_emails, batch_delete_emails, download_attachment, list_email_labels, create_label, update_label, delete_label, get_or_create_label, list_filters, get_filter, create_filter, create_filter_from_template, delete_filter.

Why this pattern: v2's invariant is that containers never receive raw API keys — OneCLI is the sole credential path (see CHANGELOG v2.0.0). The stub-file pattern satisfies this: the container sees "onecli-managed" placeholders, the gateway swaps them in flight.

Phase 1: Pre-flight

Verify OneCLI has Gmail connected

onecli apps get --provider gmail

Expected: "connection": { "status": "connected" } with scopes including gmail.readonly, gmail.modify, gmail.send.

If not connected, tell the user:

Open the OneCLI web UI at http://127.0.0.1:10254, go to Apps → Gmail, and click Connect. Sign in with the Google account you want the agent to act as.

Verify stub credentials exist

ls -la ~/.gmail-mcp/gcp-oauth.keys.json ~/.gmail-mcp/credentials.json 2>&1

If both exist and contain "onecli-managed":

grep -l onecli-managed ~/.gmail-mcp/gcp-oauth.keys.json ~/.gmail-mcp/credentials.json

...skip to Phase 2.

If either file exists but does not contain onecli-managed, STOP and tell the user — these are real OAuth credentials from a previous non-OneCLI install. Back them up, then delete before proceeding. The OneCLI migration normally handles this; if it didn't, something is wrong.

If both files are absent, write them now:

mkdir -p ~/.gmail-mcp
cat > ~/.gmail-mcp/gcp-oauth.keys.json <<'EOF'
{
  "installed": {
    "client_id": "onecli-managed.apps.googleusercontent.com",
    "client_secret": "onecli-managed",
    "redirect_uris": ["http://localhost:3000/oauth2callback"]
  }
}
EOF
cat > ~/.gmail-mcp/credentials.json <<'EOF'
{
  "access_token": "onecli-managed",
  "refresh_token": "onecli-managed",
  "token_type": "Bearer",
  "expiry_date": 99999999999999,
  "scope": "https://www.googleapis.com/auth/gmail.readonly https://www.googleapis.com/auth/gmail.modify https://www.googleapis.com/auth/gmail.send"
}
EOF
chmod 600 ~/.gmail-mcp/gcp-oauth.keys.json ~/.gmail-mcp/credentials.json

Verify mount allowlist covers the path

cat ~/.config/nanoclaw/mount-allowlist.json

~/.gmail-mcp must sit under an allowedRoots entry (e.g. /home/<user>). If it doesn't, tell the user to run /manage-mounts first or add their home directory.

Check agent secret-mode

For each target agent group, confirm OneCLI will inject Gmail secrets into its container. Find the OneCLI agent ID that matches the group's agentGroupId:

onecli agents list

If that agent's secretMode is all, you're done — Gmail secrets (identified by OneCLI's Gmail hostPattern) will auto-inject. If it's selective, explicitly assign the Gmail secrets:

onecli secrets list     # find Gmail secret IDs (OneCLI creates one per connected app)
onecli agents set-secrets --id <agent-id> --secret-ids <gmail-secret-id>

Phase 2: Apply Code Changes

Check if already applied

grep -q 'GMAIL_MCP_VERSION' container/Dockerfile && \
grep -q "mcp__gmail__\*" container/agent-runner/src/providers/claude.ts && \
echo "ALREADY APPLIED — skip to Phase 3"

Add MCP server to Dockerfile

Edit container/Dockerfile. Find the pinned-version ARG block:

ARG CLAUDE_CODE_VERSION=2.1.116
ARG AGENT_BROWSER_VERSION=latest
ARG VERCEL_VERSION=latest
ARG BUN_VERSION=1.3.12

Add a new line:

ARG GMAIL_MCP_VERSION=1.1.11

Then find the last pnpm global-install RUN block (the one that installs @anthropic-ai/claude-code) and add a new block after it, before # ---- Entrypoint:

RUN --mount=type=cache,target=/root/.cache/pnpm \
    pnpm install -g \
        "@gongrzhe/server-gmail-autoauth-mcp@${GMAIL_MCP_VERSION}" \
        "zod-to-json-schema@3.22.5"

Pinned version matters — minimumReleaseAge in pnpm-workspace.yaml gates trunk installs, and CLAUDE.md requires a fixed ARG version for all Node CLIs installed into the image.

Why the zod-to-json-schema pin: @gongrzhe/server-gmail-autoauth-mcp@1.1.11 has loose deps (zod-to-json-schema: ^3.22.1, zod: ^3.22.4). pnpm resolves zod-to-json-schema to the latest 3.25.x, which imports zod/v3 — a subpath that only exists in zod>=3.25. But zod resolves to 3.24.x (highest satisfying ^3.22.4 without breaking peer ranges). Result: ERR_PACKAGE_PATH_NOT_EXPORTED at import time. Pinning zod-to-json-schema to a pre-v3-subpath version avoids it. Re-check if you bump GMAIL_MCP_VERSION.

Add tools to allowlist

Edit container/agent-runner/src/providers/claude.ts. Find 'mcp__nanoclaw__*', in TOOL_ALLOWLIST and add 'mcp__gmail__*', after it.

Rebuild the container image

./container/build.sh

Must complete cleanly. The new pnpm install -g layer is ~60s first time (cached on rebuild).

Phase 3: Wire Per-Agent-Group

For each agent group that should have Gmail (ask the user — typically their personal DM and CLI agents, sometimes shared household agents), edit groups/<folder>/container.json to add the mount and MCP server.

Merge these into the group's container.json:

{
  "mcpServers": {
    "gmail": {
      "command": "gmail-mcp",
      "args": [],
      "env": {
        "GMAIL_OAUTH_PATH": "/workspace/extra/.gmail-mcp/gcp-oauth.keys.json",
        "GMAIL_CREDENTIALS_PATH": "/workspace/extra/.gmail-mcp/credentials.json"
      }
    }
  },
  "additionalMounts": [
    {
      "hostPath": "/home/<user>/.gmail-mcp",
      "containerPath": ".gmail-mcp",
      "readonly": false
    }
  ]
}

Substitute <user> with the host user's home (use echo $HOME, don't assume ~ will expand — container-runner.ts does expand ~ via expandPath, but an explicit absolute path is clearer and matches what /manage-mounts writes).

Why the container path is relative: mount-security rejects absolute containerPath values. Additional mounts are prefixed with /workspace/extra/, so containerPath: ".gmail-mcp" lands at /workspace/extra/.gmail-mcp. The MCP server's GMAIL_OAUTH_PATH / GMAIL_CREDENTIALS_PATH env vars point at that absolute location inside the container.

Phase 4: Build and Restart

pnpm run build
systemctl --user restart nanoclaw  # Linux
# launchctl kickstart -k gui/$(id -u)/com.nanoclaw   # macOS

Phase 5: Verify

Test from the wired agent

Tell the user:

In your <agent-name> chat, send: "list my gmail labels" or "search my inbox for invoices from last month".

The agent should use mcp__gmail__list_labels / mcp__gmail__search. The first call may take a second or two while the MCP server starts and OneCLI does the token exchange.

Check logs if the tool isn't working

tail -100 logs/nanoclaw.log logs/nanoclaw.error.log | grep -iE 'gmail|mcp'
# Per-container logs — session-scoped:
ls data/v2-sessions/*/stderr.log | head

Common signals:

  • command not found: gmail-mcp → image wasn't rebuilt or PATH doesn't include /pnpm (should — ENV PATH="$PNPM_HOME:$PATH" in Dockerfile).
  • ENOENT: no such file or directory, open '/workspace/extra/.gmail-mcp/credentials.json' → mount is missing. Check ~/.config/nanoclaw/mount-allowlist.json includes a parent of ~/.gmail-mcp.
  • 401 Unauthorized from gmail.googleapis.com → OneCLI isn't injecting. Check the agent's secret mode (onecli agents secrets --id <agent-id>) and that the Gmail app is connected (onecli apps get --provider gmail).
  • Agent says "I don't have Gmail tools" → mcp__gmail__* wasn't added to TOOL_ALLOWLIST, or the agent-runner wasn't rebuilt (image cache — run ./container/build.sh again with --no-cache if suspicious).

Removal

  1. Delete the "gmail" entry from mcpServers and the .gmail-mcp entry from additionalMounts in each group's container.json.
  2. Remove 'mcp__gmail__*' from TOOL_ALLOWLIST in container/agent-runner/src/providers/claude.ts.
  3. Remove the GMAIL_MCP_VERSION ARG and the pnpm install -g @gongrzhe/server-gmail-autoauth-mcp block from container/Dockerfile.
  4. pnpm run build && ./container/build.sh && systemctl --user restart nanoclaw.
  5. (Optional) rm -rf ~/.gmail-mcp/ if no other host-side tool needs the stubs.
  6. (Optional) Disconnect Gmail in OneCLI: onecli apps disconnect --provider gmail.

Notes

  • Stub format is OneCLI-prescribed. The access_token: "onecli-managed" pattern with expiry_date: 99999999999999 tells the Google auth client the token is valid; OneCLI intercepts the outgoing Gmail API call and rewrites Authorization: Bearer onecli-managed to the real token. expiry_date: 0 (refresh-interception) is an alternative the OneCLI docs describe — both work but OneCLI's own migrate command writes the far-future variant, which is what this skill assumes.
  • Scopes are set at OAuth connect time. If the agent needs scopes beyond what's currently connected (e.g. the user later wants calendar.readonly for combined email/calendar workflows), disconnect and reconnect Gmail in the OneCLI web UI with the expanded scope set.
  • This is tool-only. Inbound email as a channel (emails trigger the agent) is a separate piece of work — it needs a src/channels/gmail.ts adapter that polls the inbox and routes to a messaging group. The pre-v2 qwibitai skill had this; it has not been ported to v2's channel architecture as of v2.0.0.

Credits & references

  • MCP server: @gongrzhe/server-gmail-autoauth-mcp by GongRzhe — MIT-licensed.
  • OneCLI credential stubs: pattern documented at https://onecli.sh/docs/guides/credential-stubs/gmail.md.
  • Skill pattern: modeled on add-atomic-chat-tool and add-vercel.
  • Addresses: issue #1500 (proxy Gmail/Calendar OAuth tokens through credential proxy) for the Gmail side.
  • Related PRs: #1810 (pre-install Gmail/Notion MCP) overlaps on the "install the MCP server in the image" idea but bundles many unrelated changes; this skill is the focused OneCLI-native version.
Related skills
Installs
2
GitHub Stars
28.5K
First Seen
12 days ago