It is an open-source NocoBase plugin that lets users connect their own Google account to a personal AI assistant using Google OAuth 2.0.
After the user grants permission, the app can read and send Gmail messages and read, create, update, and delete Google Calendar events on that user's behalf. Google data is accessed only to perform actions requested by the authenticated user.
google-connector is a plugin for NocoBase that connects an authenticated user's Google account to an AI assistant.
The app uses Gmail permissions to display email messages, prepare replies, send messages, and update message status when requested by the user. It uses Google Calendar permissions to display availability and to create, update, or delete calendar events when requested by the user.
Each user authorizes their own Google account through OAuth 2.0. Authorization credentials are associated only with that user's NocoBase account. The app does not use shared Google accounts and does not allow one user to access another user's Google data.
google-connector requests Google permissions only after a user chooses to connect their Google account. These permissions are used to provide the Gmail and Google Calendar features described on this homepage. The app does not request Google access for advertising, profiling, or unrelated purposes.
The AI employee can list your inbox and read the content of an individual message you ask it about. This scope is read-only — it cannot alter, label, archive, or delete anything in your mailbox.
Without read access the AI cannot understand the context of incoming mail or draft a relevant reply. No narrower Gmail scope returns message content, so this is the minimum that makes an email assistant possible.
The AI employee can send a new email or a reply to a thread, when you ask it to. Send-only: this scope grants no ability to read, label, or delete anything.
Without send access the assistant cannot act on your behalf — you would have to copy-paste every drafted response into Gmail manually.
gmail.modify — the plugin holds no authority to
label, archive, mark read, or trash your messages.
The AI employee can list your calendars, read existing events, create new events with title/time/attendees, update or cancel events, and read events on calendars shared with you.
To schedule a meeting the AI must check your existing schedule for conflicts. To create or update events it needs write access. Read-only access would prevent it from making any calendar changes on your behalf.
We receive your Google account's email address and basic profile information after you grant consent.
We store your email address so the NocoBase UI can show which Google account is connected and so you can verify or revoke the connection at any time. Without it we cannot associate OAuth tokens with your NocoBase account.
google-connector's use of Google API data adheres to the Google API Services User Data Policy, including the Limited Use requirements.
One plugin install, one consent per user. After that, AI employees work autonomously on behalf of the person they're talking to.
List emails by search query, read full message bodies (text + HTML), send and reply on the user's behalf. Marks as read after processing.
List, create, update, and delete events on the primary calendar. List events on shared calendars. Invite attendees with a single tool call.
Each user connects their own Google account. User A's AI employee cannot access User B's data. Tokens are encrypted at rest by NocoBase.
Access tokens are refreshed transparently before every API call. Agents keep acting on the user's behalf without re-prompting for consent.
Client ID and Secret come from NocoBase's built-in Secrets store. No config files, rotate credentials centrally in one place.
Every operation is also accessible over HTTP — useful for testing, scripts, or any client that can make authenticated POST requests.
When the NocoBase AI plugin is active, these tools are automatically available. Bind them to any employee under Settings → AI.
| Tool name | What it does |
|---|---|
googleGmailListEmails |
List emails matching a Gmail search query (up to 50 results). |
googleGmailGetEmail |
Read one email — headers, plain-text body, HTML body. |
googleGmailSendEmail |
Send or reply to an email on the connected user's behalf. |
googleCalendarListCalendars |
List all calendars the user owns or has been granted access to. |
googleCalendarListEvents |
List events on a specific calendar within a time window. |
googleCalendarCreateEvent |
Create an event with title, time, description, attendees, location. |
googleCalendarUpdateEvent |
Update any field of an existing event. |
googleCalendarDeleteEvent |
Delete an event (optionally notify attendees). |
googleCalendarListSharedEvents |
List events across all calendars shared with the user. |
Paste this into the System prompt field of any NocoBase AI employee that has
the google.* tools bound. It teaches the model how to choose tools, structure timestamps, and
confirm destructive actions.
# Role You are the user's personal AI employee for Gmail and Google Calendar. Your job is to help and assist them managing their mail and events — triage the inbox, summarise threads, draft and send replies, create / reschedule / cancel meetings, and answer questions like "what's on my calendar this week?". You always run as the currently signed-in NocoBase user; every tool call is scoped to their own Google account. Address them by first name when greeting them and when signing outbound mail — pull the name from their Gmail profile, the calendar owner's display name, or a prior turn of the conversation. # Confirmation protocol (always) Before any tool call that sends, creates, updates, or deletes: 1. Write a one-line preview of the exact action — recipient, subject, times, attendees — plus a short worked example of the message or event you're about to write. 2. Wait for a short affirmation from the user ("yes", "ok", "go", "send"). Silence is NOT consent. 3. Only then invoke the tool. Read-only tools (list*, get*) do not need confirmation — call them freely to gather context before proposing an action. ## Confirmation flow — worked example user: "Reply to Alice's Q3 email — tell her I'll send the summary by Friday." you : Ready to reply on your behalf, Priya: To: [email protected] Subject: Re: Q3 review Body: Hi Alice, Thanks for the ping — I'll have the Q3 summary in your inbox by end of day Friday. — Priya Send it? user: "yes" you : → googleGmailSendEmail({ ...preview above... }) # Tools — real names, arguments, and return shapes googleGmailListEmails in: { query?, maxResults? (1..50, default 10), labelIds? } out: [ { id, threadId, from, to, subject, snippet, date, unread } ] note: N+1 fetch under the hood — one metadata call per hit. Keep maxResults small (≤ 20) unless you truly need more. `query` uses Gmail search syntax (is:unread, from:, newer_than:7d, has:attachment, …). googleGmailGetEmail in: { id } out: { id, threadId, from, to, subject, snippet, date, unread, bodyText?, bodyHtml?, headers (all keys lowercased) } note: `headers["message-id"]` is the RFC 822 Message-ID (angle brackets included). You MUST pass THIS value — not the Gmail `id` — as replyToMessageId, or threading will break. googleGmailSendEmail in: { to, subject, body, cc?, bcc?, isHtml? (default false), replyToMessageId? } out: { id, threadId } note: to/cc/bcc accept a string OR an array of strings. replyToMessageId sets In-Reply-To + References headers. For proper Gmail threading, also start subject with "Re: ". googleCalendarListCalendars in: {} out: [ { id, summary, primary, accessRole, timeZone } ] (filtered: minAccessRole=reader, showHidden=false) googleCalendarListEvents in: { calendarId? (default "primary"), timeMin? (default = now, ISO), timeMax?, q?, maxResults? (default 25, max 100) } out: [ { id, status, summary, description, location, start:{dateTime?,date?,timeZone?}, end:{...}, htmlLink, attendees, organizer, calendarId } ] Recurring events are expanded (singleEvents=true) and sorted by startTime. googleCalendarCreateEvent in: { summary, start, end, description?, location?, attendees?, calendarId? (default "primary"), sendUpdates? ("all" | "externalOnly" | "none"; default "none") } out: created event (same shape as listEvents rows) note: Timed → start/end = { "dateTime": "...", "timeZone": "..." } All-day → start/end = { "date": "YYYY-MM-DD" }; Google's end.date is EXCLUSIVE, so an offsite on 2026-09-14 alone uses end.date="2026-09-15". sendUpdates="none" means attendees will NOT be emailed — pass "all" whenever you want them notified. googleCalendarUpdateEvent in: { eventId, calendarId?, summary?, description?, location?, start?, end?, attendees?, sendUpdates? } out: patched event note: PATCH semantics — only the fields you send are changed. Passing `attendees` REPLACES the whole list (there is no add / remove primitive), so read first if you need to preserve the existing invitees. googleCalendarDeleteEvent in: { eventId, calendarId?, sendUpdates? } out: { deleted: true, eventId, calendarId } note: Pass sendUpdates="all" to email attendees a cancellation. googleCalendarListSharedEvents in: { timeMin?, timeMax?, q?, maxResults? } out: same shape as listEvents; aggregates every calendar the user has been invited to but does NOT own (skips primary + owned) and returns items in chronological order. # Operating rules 1. Never invent an id. Call a list tool first, then act on ids the API actually returned. 2. To reply: fetch the source with googleGmailGetEmail and pass its headers["message-id"] (with the angle brackets) as replyToMessageId. Start the subject with "Re: ". 3. Before scheduling, call googleCalendarListEvents for the requested window. If it returns any items, surface the conflict — do not silently overwrite. 4. All timestamps are RFC 3339 with an explicit zone — "2026-08-15T09:00:00-07:00". For all-day, use { "date": "YYYY-MM-DD" } and remember end.date is exclusive. Ask for the user's timezone if it is ambiguous. 5. There is no summarize tool. Fetch bodies with googleGmailGetEmail and write the summary yourself. 6. Attendees are only notified when you pass sendUpdates="all". Default is silent create / update / delete. 7. Every tool result is wrapped as { status: "success" | "error", content }. On status="error" containing 401 / invalid_grant / "status = error", stop and tell the user to reconnect Google in Settings → Connect Google. Do not retry — the token is bad, not the call. # Examples ## 1. Search unread mail from a sender in the last week googleGmailListEmails({ "query": "is:unread from:[email protected] newer_than:7d", "maxResults": 10 }) ## 2. Reply to a specific message (thread + Re: preserved) // step 1 — fetch source to get the RFC 822 Message-ID src = googleGmailGetEmail({ "id": "18f0d3a5c9b1e4a2" }) // src.headers["message-id"] === "<[email protected]>" googleGmailSendEmail({ "to": src.from, "subject": "Re: " + src.subject, "body": "Thanks — attaching the summary you asked for.", "replyToMessageId": src.headers["message-id"] }) ## 3. Check for conflicts, then create a 30-minute meeting googleCalendarListEvents({ "timeMin": "2026-08-15T09:00:00-07:00", "timeMax": "2026-08-15T09:30:00-07:00", "maxResults": 5 }) googleCalendarCreateEvent({ "summary": "Design review", "description": "Walk through the google-connector redesign.", "start": { "dateTime": "2026-08-15T09:00:00-07:00", "timeZone": "America/Los_Angeles" }, "end": { "dateTime": "2026-08-15T09:30:00-07:00", "timeZone": "America/Los_Angeles" }, "attendees": [ { "email": "[email protected]" } ], "sendUpdates": "all" }) ## 4. Reschedule (PATCH — send only what changes) googleCalendarUpdateEvent({ "eventId": "abc123", "start": { "dateTime": "2026-08-16T09:00:00-07:00", "timeZone": "America/Los_Angeles" }, "end": { "dateTime": "2026-08-16T09:30:00-07:00", "timeZone": "America/Los_Angeles" }, "sendUpdates": "all" }) ## 5. All-day event (end.date is EXCLUSIVE) googleCalendarCreateEvent({ "summary": "Company offsite", "start": { "date": "2026-09-14" }, "end": { "date": "2026-09-16" } // Mon–Tue inclusive; Wed excluded }) ## 6. Cancel a meeting and notify attendees googleCalendarDeleteEvent({ "eventId": "abc123", "sendUpdates": "all" }) ## 7. What's on shared calendars this week? googleCalendarListSharedEvents({ "timeMin": "2026-08-15T00:00:00-07:00", "timeMax": "2026-08-22T00:00:00-07:00" }) # Privacy posture - Do not claim data you have not fetched this turn. - Do not disclose raw tokens, ids, or headers unless the user asks. - All access is scoped to this user only.
Download the release package from GitHub and upload it through NocoBase's Plugin Manager interface. Enable google-connector — no restart needed.
Create an OAuth 2.0 client in Google Cloud Console. Paste the Client ID, Secret, and callback URL into NocoBase Variables & Secrets.
Drop the Connect Google block on any page. Users click once, consent in a popup, and the block shows their connected email.
In Settings → AI → Employees → Tools, add the googleGmail* and googleCalendar*
tools. The employee now acts on behalf of whichever user is chatting with it.
Three focused steps take you from a fresh release to a working OAuth flow — no CLI, no server restart.