CData Connect AI — Google Calendar Skill
⚠️ Prerequisites — load these first
- connect-ai-base skill
Before proceeding, locate and read the connect-ai-base skill. If it is not available in the current environment (not loaded, not enabled, or not installed), stop immediately. Do not proceed with the task. Tell the user that the connect-ai-base skill is required and ask them to install and enable it before continuing.
This skill provides Google Calendar-specific guidance for querying Google Calendar data through CData Connect AI. It composes on top of the connect-ai-base skill, which handles the discovery workflow, error recovery, SQL dialect, and query naming convention.
Precedence
This skill replaces getInstructions for the GoogleCalendar driver. Do not call getInstructions for Google Calendar — the guidance it provides is already incorporated here. Proceed directly to schema discovery (getTables / getColumns) after identifying the Google Calendar connection via getCatalogs.
Schema
Google Calendar is a single-schema driver. The schema name is GoogleCalendar.
SELECT [Id], [Summary], [StartDateTime], [EndDateTime]
FROM [YourConnection].[GoogleCalendar].[AllCalendars]
WHERE [CalendarId] = 'primary'
LIMIT 10
Per-calendar tables — important
getTables for this driver returns far more than the handful of static tables. Each calendar the account can access is exposed as its own dynamically-generated table, named after the calendar's title (e.g. [Holidays in United States], [A Private Calendar], [Test Calendar]). These names often contain spaces or special characters and must be bracket-quoted.
There are two ways to read events:
AllCalendars— the union view across every calendar. This is the primary, stable table to query. Scope it with a[CalendarId]filter (use'primary'for the user's main calendar).- A specific per-calendar table — query the named table directly when you already know the calendar and want only its events.
Prefer AllCalendars with a CalendarId filter in almost all cases — it is stable, documented, and avoids guessing at calendar-table names. Use the Calendars table to enumerate available calendars and their IDs rather than scanning getTables output (which is dominated by per-calendar and test tables).
Query Process
Google Calendar data is organized around events that belong to calendars. Most requests start with "find the relevant events in a time window," then refine or act.
- Identify the connection — if unknown, call
getCatalogsand filter forDRIVER = 'GoogleCalendar'. - Identify the calendar — default to
CalendarId = 'primary'. To list calendars the account can see, queryCalendars([Id],[Summary],[AccessRole]). - Query events from
AllCalendars— always include a date filter on[StartDateTime](large calendars time out otherwise) and a[CalendarId]filter for performance. - Refine — filter by
[Status], attendees, all-day vs. timed, recurrence, etc. - Act — create events with
ImportEventorQuickAddEvent, check free/busy withGetAvailability, move events withMoveEvent, or modify events with UPDATE onAllCalendars.
Data Model
Key Tables
- AllCalendars — Union of events across all calendars. The primary table for event queries; scope with
[CalendarId]. Holds event details, attendees, recurrence, reminders, and conference data. - Calendars — Calendar metadata: title, description, timezone, color, visibility, and the authenticated user's
AccessRoleper calendar. - AccessControlRules — Sharing/permission (ACL) rules per calendar: who can read or write a calendar.
- Colors (view) — Available color IDs and their hex values for calendars and events.
- EventsAttachments — Attachment metadata for events (Drive file ID, URL, title, MIME type).
- UserSettings (view) — The authenticated user's calendar preferences.
- Per-calendar tables — One dynamically-generated table per accessible calendar (see "Per-calendar tables" above).
Key Relationships
- Events → Calendar:
AllCalendars.[CalendarId]corresponds toCalendars.[Id]. - ACL rules → Calendar:
AccessControlRules.[CalendarId]corresponds toCalendars.[Id]. - Attachments → Event:
EventsAttachments.[EventId]corresponds toAllCalendars.[Id](and[CalendarId]matches). - Recurring-event instances → parent:
AllCalendars.[RecurringEventId]points to the parent recurring event's[Id].
Important Columns
AllCalendars
Id— Event identifier (primary key, read-only)CalendarId— Calendar the event belongs to (key, writable — used to scope/override which calendar you query;'primary'= the user's main calendar)Summary— Event titleDescription— Event descriptionLocation— Free-form location textStartDateTime/EndDateTime— Timed-event start/end (TIMESTAMP).EndDateTimeis exclusiveStartDateTimeZone/EndDateTimeZone— IANA time zone (e.g.'America/New_York')StartDate/EndDate— DATE values used instead of the DateTime columns for all-day eventsAllDayEvent— BOOLEAN, read-only — true when the event is all-dayStatus—confirmed,tentative, orcancelledSendNotification— BOOLEAN, writable — set to true on INSERT/UPDATE to notify attendeesAttendeesEmails— Comma-separated attendee emailsAttendeesDisplayNames— Comma-separated attendee namesAttendeesResponseStatus— Comma-separated responses (accepted,declined,needsAction,tentative)AttendeesOptional— Comma-separated booleans for optional attendeesOrganizerEmail/OrganizerDisplayName— Organizer identity (read-only)CreatorEmail/CreatorDisplayName— Creator identity (read-only)Recurrences— Pipe-separated RRULE/EXRULE/RDATE/EXDATE lines (recurring events only)RecurringEventId— Parent event ID for instances of a recurring event (read-only)OriginalStartTimeDateTime— For a recurring instance, its scheduled start per the recurrence ruleCreated/Updated— Lifecycle timestamps (read-only)HangoutLink— Google Meet/Hangout link, if any (read-only)ConferenceEntryPointsAggregate— Conference entry points (URLs, phone numbers)HTML_Link— Absolute link to the event in the Google Calendar web UI (read-only)Transparency—opaque(blocks time, default) ortransparent(does not block time)Visibility— Event visibilityColorId— Event color (INTEGER ID into the event section ofColors)EventType—default,outOfOffice,focusTime, orworkingLocation
Calendars
Id— Calendar identifier (usually an email address; key)Summary— Calendar titleDescription— Calendar descriptionTimezone— Default time zoneAccessRole— The authenticated user's role:owner,writer,reader, orfreeBusyReader(read-only)Hidden— Whether the calendar is hidden from the listSelected— Whether the calendar's content shows in the UIColorId— Calendar color (INTEGER ID into the calendar section ofColors)SummaryOverride— User-set display title for the calendarLocation— Free-form calendar location
AccessControlRules
CalendarId— Calendar the rule applies to (key)Id— ACL rule identifier (key, read-only)Role—none,freeBusyReader,reader,writer, orownerScopeType—default,user,group, ordomainScopeValue— Email of a user/group or a domain name (not applicable whenScopeType = 'default')
EventsAttachments
EventId— Event the attachment belongs to (key)CalendarId— Calendar identifierFileURL— URL link to the attachment (key)FileId— Google Drive file ID, when the attachment lives in Drive (read-only)Title— Attachment nameMimeType— MIME type of the attachment
Common Query Patterns
Next week's events
SELECT [Id], [Summary], [StartDateTime], [EndDateTime], [Location], [AttendeesEmails]
FROM [YourConnection].[GoogleCalendar].[AllCalendars]
WHERE [CalendarId] = 'primary'
AND [StartDateTime] >= DATEADD(week, 1, CAST(GETDATE() AS DATE))
AND [StartDateTime] < DATEADD(week, 2, CAST(GETDATE() AS DATE))
ORDER BY [StartDateTime]
Upcoming confirmed events only
SELECT [Summary], [StartDateTime], [EndDateTime], [Location]
FROM [YourConnection].[GoogleCalendar].[AllCalendars]
WHERE [CalendarId] = 'primary'
AND [StartDateTime] >= GETDATE()
AND [Status] = 'confirmed'
ORDER BY [StartDateTime]
Events involving a specific attendee
Attendee data is comma-separated, so match with LIKE:
SELECT [Summary], [StartDateTime], [AttendeesEmails]
FROM [YourConnection].[GoogleCalendar].[AllCalendars]
WHERE [CalendarId] = 'primary'
AND [StartDateTime] >= GETDATE()
AND [AttendeesEmails] LIKE '%person@example.com%'
ORDER BY [StartDateTime]
Finding open slots on a given afternoon
Query existing events, then identify gaps for scheduling:
SELECT [StartDateTime], [EndDateTime], [Summary]
FROM [YourConnection].[GoogleCalendar].[AllCalendars]
WHERE [CalendarId] = 'primary'
AND DATEPART(weekday, [StartDateTime]) = 6 -- Friday (Sun=1 ... Sat=7)
AND [StartDateTime] >= DATEADD(week, 1, CAST(GETDATE() AS DATE))
AND [StartDateTime] < DATEADD(week, 2, CAST(GETDATE() AS DATE))
AND DATEPART(hour, [StartDateTime]) >= 12
ORDER BY [StartDateTime]
For true free/busy across one or more calendars, prefer the GetAvailability stored procedure (below) over computing gaps manually.
List calendars the account can access
SELECT [Id], [Summary], [AccessRole], [Timezone]
FROM [YourConnection].[GoogleCalendar].[Calendars]
ORDER BY [Summary]
Who can access a calendar (sharing rules)
SELECT [Id], [Role], [ScopeType], [ScopeValue]
FROM [YourConnection].[GoogleCalendar].[AccessControlRules]
WHERE [CalendarId] = 'primary'
Stored Procedures
QuickAddEvent
Creates an event from a natural-language string — the fastest way to add a simple event.
EXEC [YourConnection].[GoogleCalendar].QuickAddEvent
@CalendarId = 'primary',
@Summary = 'Lunch with Sarah next Friday at noon',
@SendUpdates = 'all'
@CalendarId and @Summary are required.
ImportEvent
Adds a fully-specified event. @CalendarId and @ICalUID are required — always supply a unique @ICalUID (RFC5545 UID, e.g. 'unique-id-20251114@yourdomain.com').
EXEC [YourConnection].[GoogleCalendar].ImportEvent
@CalendarId = 'primary',
@ICalUID = 'unique-id-20251114@yourdomain.com',
@Summary = 'Product Launch Planning',
@Description = 'Discuss Q1 product launch strategy',
@Location = 'Conference Room B',
@StartDateTime = '2025-11-15T14:00:00',
@EndDateTime = '2025-11-15T15:30:00',
@StartDateTimeZone = 'America/New_York',
@EndDateTimeZone = 'America/New_York',
@Attendees = '[{"email": "alice@example.com"}, {"email": "bob@example.com"}]',
@RemindersUseDefault = 0,
@ReminderOverrides = '[{"method": "email", "minutes": 60}, {"method": "popup", "minutes": 15}]'
JSON format for executeProcedure:
{
"catalogName": "YourConnection",
"schemaName": "GoogleCalendar",
"procedureName": "ImportEvent",
"parameters": {
"@CalendarId": "primary",
"@ICalUID": "unique-id-20251114@yourdomain.com",
"@Summary": "Product Launch Planning",
"@StartDateTime": "2025-11-15T14:00:00",
"@EndDateTime": "2025-11-15T15:30:00",
"@StartDateTimeZone": "America/New_York",
"@EndDateTimeZone": "America/New_York",
"@Attendees": "[{\"email\": \"alice@example.com\"}]"
}
}
@Attendees,@ReminderOverrides, and@ConferenceDataEntryPointsaccept JSON arrays (or temporary-table input) only.@Recurrencesis a pipe-separated list of RRULE/EXRULE/RDATE/EXDATE lines (DTSTART/DTEND are not allowed). For a simple repeat, a singleRRULE:...string is sufficient — see Recurrence rules below.- Attachments are added by reference via
@AttachmentsFileUrls(pipe-separated URLs) with@SupportsAttachments = true— there is no file-upload parameter (see Conventions).
ImportEventhas no notification parameter.@SendNotificationis a column onAllCalendars(used with INSERT/UPDATE), and@SendUpdatesis a parameter onQuickAddEventandMoveEvent— neither applies toImportEvent. Attendee notifications for imported events are controlled by the calendar's default notification settings.
GetAvailability
Returns free/busy windows for one or more calendars. All three parameters are required.
EXEC [YourConnection].[GoogleCalendar].GetAvailability
@TimeMin = '2025-11-22T09:00:00Z',
@TimeMax = '2025-11-22T17:00:00Z',
@EntityIds = 'primary'
@EntityIds is a comma-separated list of calendar (or group) identifiers — pass several to compare availability across calendars.
MoveEvent
Moves an event from one calendar to another, preserving its details.
EXEC [YourConnection].[GoogleCalendar].MoveEvent
@CalendarId = 'primary',
@EventId = '<event-id>',
@Destination = '<destination-calendar-id>',
@SendUpdates = 'all'
@CalendarId (source), @EventId, and @Destination are required.
DownloadAttachments
Downloads an event's attachment content.
Cloud-compatible call shape — omit @LocalFile (server-side disk path) and @FileStream (output stream, cannot pass through MCP), and set @Encoding = 'BASE64'; the response returns the file content as base64 in a FileData column.
{
"catalogName": "YourConnection",
"schemaName": "GoogleCalendar",
"procedureName": "DownloadAttachments",
"parameters": {
"@CalendarId": "primary",
"@EventId": "<event-id>",
"@Encoding": "BASE64"
}
}
Find attachment identifiers first via EventsAttachments:
SELECT [EventId], [FileId], [FileURL], [Title], [MimeType]
FROM [YourConnection].[GoogleCalendar].[EventsAttachments]
WHERE [CalendarId] = 'primary'
AND [EventId] = '<event-id>'
Subscribe* / StopWatchingResources — not usable through Connect AI MCP
SubscribeToEventChanges, SubscribeToAclChanges, SubscribeToCalendarListChanges, SubscribeToSettingsChanges, and StopWatchingResources register/cancel Google push-notification (webhook) channels. They require a publicly reachable callback address and return no queryable data, so they are not useful in an interactive Connect AI/MCP session. Ignore them for query and scheduling tasks.
Write Operations
The Google Calendar driver supports INSERT and UPDATE on AllCalendars (and the per-calendar tables) where the connection and the authenticated Google user's AccessRole allow it.
Update an event
Always scope by both [Id] and [CalendarId]:
UPDATE [YourConnection].[GoogleCalendar].[AllCalendars]
SET [Summary] = 'Updated Meeting Title',
[Location] = 'Virtual - Zoom Link',
[SendNotification] = 1
WHERE [Id] = '<event-id>'
AND [CalendarId] = 'primary'
Reschedule an event
UPDATE [YourConnection].[GoogleCalendar].[AllCalendars]
SET [StartDateTime] = '2025-11-20T15:00:00',
[EndDateTime] = '2025-11-20T16:00:00',
[SendNotification] = 1
WHERE [Id] = '<event-id>'
AND [CalendarId] = 'primary'
Prefer in-place UPDATE so event history and attendee responses are preserved. To create events, prefer the ImportEvent / QuickAddEvent procedures over raw INSERT — they expose attendees, reminders, recurrence, and conference data cleanly.
If write operations are blocked, the Connect AI connection may be in readonly mode (CData side) or the authenticated Google user may lack writer/owner AccessRole on the target calendar (check Calendars.[AccessRole]). Guide the user to enable write access on the connection and confirm their calendar permissions.
Recurrence rules (RRULE)
@Recurrences / [Recurrences] use RFC5545 rules. Common forms:
- Weekdays daily:
RRULE:FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR - Weekly on specific days:
RRULE:FREQ=WEEKLY;BYDAY=MO,WE - First Monday monthly:
RRULE:FREQ=MONTHLY;BYDAY=1MO - Limit occurrences: append
;COUNT=10 - End by date: append
;UNTIL=20251231T000000Z
For multiple rules/exceptions, separate lines with | (pipe). Do not include DTSTART/DTEND lines.
Google Calendar-Specific Conventions
- Query
AllCalendarswith aCalendarIdfilter, not the per-calendar tables. Each calendar is also exposed as its own dynamically-named table, butAllCalendarsis the stable entry point. Use'primary'for the user's main calendar; enumerate others via theCalendarstable. - Date filters are mandatory on event queries.
AllCalendarswill time out on large calendars without a[StartDateTime]range. Add a[CalendarId]filter for performance too. - All-day events use
StartDate/EndDate(DATE), notStartDateTime/EndDateTime. Filter all-day events with[AllDayEvent] = 1and read the DATE columns.AllDayEventis read-only — it is derived, not set directly. EndDateTimeis exclusive. A 1:00–2:00 event hasStartDateTime = ...T13:00andEndDateTime = ...T14:00.- Always specify time zones when creating events (
@StartDateTimeZone/@EndDateTimeZone) to avoid ambiguity. ImportEventrequires a unique@ICalUID. Reusing a UID causes the request to be treated as the same event. Generate a fresh UID per event.- Attendees and reminders are JSON on procedures but comma-separated on the table.
ImportEventtakes@Attendees/@ReminderOverridesas JSON arrays; theAllCalendarstable exposes the same data as comma-separatedAttendeesEmails/ReminderOverrideMethodscolumns. Search attendees withLIKEon the comma-separated column. - Attachments are added by URL reference, not uploaded.
ImportEventaccepts@AttachmentsFileUrls(pipe-separated URLs, typically Google Drive links) with@SupportsAttachments = true. There is no base64/file-upload parameter, so the usual cloud file-upload limitation does not apply here — attachments must already exist at a URL. SendNotificationcontrols attendee emails on write. Set to 1 to notify attendees of an insert/update, 0 for a silent change.Transparencycontrols busy/free.opaque(default) blocks time and shows as busy;transparentdoes not.- Conference/Meet links are read-only. Read
HangoutLinkfor the Meet link orConferenceEntryPointsAggregatefor other platforms; create conference data via theImportEventConferenceData*parameters. DATEPART(weekday, ...)numbering is Sun=1 … Sat=7 (so Friday = 6).- Use
GetAvailabilityfor free/busy, not manual gap math, when the question is "when is everyone free."