Google Calendar
Proxy for the Google Calendar v3 REST API. Dual auth by design — the typical production setup runs two managed resources at once:
calendar-userinidp_passthroughmode for human users who want an agent to read their schedule, find free slots, or update their own events.calendar-roomsingoogle_samode for a booking agent that schedules meetings into Workspace resource (room) calendars on everyone's behalf.
Overview
The policy layer recognises this split and lets the operator lock each mode down differently:
- User-mode managed resources typically carry a
write_blockedlist on the general file-access control, for calendars the user is on but shouldn't let an agent edit (shared team, executive). - Room-mode managed resources typically run with
room_only_mode: true, which refuses any call whosecalendar_idisn't a Workspace resource calendar (*@resource.calendar.google.com).
Default policy
The connector ships policy/calendar.rego
(package pbac.connectors.identos.google_calendar). It enforces two
things; blocked and read-only calendar ids are operator config on the general
file-access control (see below), not rules here.
1. IdP-routing subject obligation
Same shape as drive/gmail — non-Google session + idp_passthrough mode
triggers a require_authn_at obligation.
2. room_only_mode deny
deny contains msg if {
input.resource.type == "urn:connector:identos:google-calendar"
data[...].room_only_mode == true
cal := input.context.rs_context.tool_arguments.calendar_id
not endswith(cal, "@resource.calendar.google.com")
...
}
A booking-agent safety net. When enabled, every calendar_id must look like a Workspace resource calendar. If the agent (or, more importantly, a prompt-injected response it's acting on) tries to touch a human calendar, the request is denied. Makes the service account's broad Workspace grants safe to deploy.
Supplying operator data
room_only_mode is the connector's own setting — a suffix test, not an id
set, so it stays connector rego:
KEY=pbac.operator.connectors.identos.google-calendar
curl -X PUT "http://localhost:8080/admin/api/policy-data/by-key?dataKey=$KEY" \
-H "X-Admin-API-Key: $PBAC_ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg k "$KEY" --argjson s '{
"room_only_mode": true
}' \
'{dataKey:$k, payload:($s|tojson)}')"
File access control
Blocking a specific calendar is operator config on the general file-access
control, shared with the Google file connectors — not a rule in this
connector. An identity source matches a calendar by its own id: ids in
blocked are denied on reads and writes, ids in write_blocked are denied on
writes only, so reads still work.
curl -X PUT http://localhost:8080/admin/policy/data/pbac/operator/controls/file_access \
-H "X-Admin-API-Key: $PBAC_ADMIN_API_KEY" \
-d '{
"sources": {
"calendars": {
"type": "identity",
"blocked": ["ceo@example.com"],
"write_blocked": ["team-alpha@example.com"]
}
},
"governed": {
"urn:connector:identos:google-calendar": ["calendars"]
}
}'
The control reads the id from the calendar_id tool argument and applies to
calendar:read, calendar:events:write and calendar:admin calls alike. Listing a connector under governed is what activates it; omit it
and the control is inert. Full mechanics (pooling, deny-wins, fail-closed) live
in the File Access Control guide.
Scope model
| Scope | Used for | |
|---|---|---|
| PBAC scopes (internal) | calendar:read, calendar:events:write, calendar:admin | Route authorization |
| Upstream OAuth scopes | calendar.readonly, calendar.events | Google-side grants the user consents to (idp_passthrough mode); the service-account flow doesn't use these |
See Google Workspace: multi-connector setup for how calendar's scopes compose with drive and gmail on one Google IdP.
Manifest reference
- ID:
identos.google-calendar - Version:
1.1.0 - Resource type:
urn:connector:identos:google-calendar
Supported auth modes
| Type | Details |
|---|---|
idp_passthrough | requires IdP google |
google_sa | setup fields: sa_key_env, domain |
Setup fields
| ID | Label | Default | Secret? | Notes |
|---|---|---|---|---|
base_url | API base URL | https://www.googleapis.com | no | — |
upstream_auth.type | Authentication | idp_passthrough | no | idp_passthrough forwards each user's own Google OAuth token (recommended). google_sa uses a Workspace service account with domain-wide delegation — useful for shared / room calendars where no human caller is involved. |
sa_key_env | Service account key | — | yes | Pick a secret containing the service-account JSON. Required only for the google_sa auth mode. / shown when upstream_auth.type == 'google_sa' |
domain | Workspace domain | — | no | placeholder: example.com / Required only for the google_sa auth mode. / shown when upstream_auth.type == 'google_sa' |
Scopes
| Scope |
|---|
calendar:read |
calendar:events:write |
calendar:admin |
Routes
| Method | Pattern | Scope | Resource template |
|---|---|---|---|
GET | /calendar/v3/users/me/calendarList | calendar:read | — |
GET | /calendar/v3/calendars/{calendar_id} | calendar:read | calendar://{{calendar_id}} |
GET | /calendar/v3/calendars/{calendar_id}/events | calendar:read | calendar://{{calendar_id}} |
GET | /calendar/v3/calendars/{calendar_id}/events/{event_id} | calendar:read | calendar://{{calendar_id}}/event/{{event_id}} |
POST | /calendar/v3/calendars/{calendar_id}/events | calendar:events:write | calendar://{{calendar_id}} |
PATCH | /calendar/v3/calendars/{calendar_id}/events/{event_id} | calendar:events:write | calendar://{{calendar_id}}/event/{{event_id}} |
DELETE | /calendar/v3/calendars/{calendar_id}/events/{event_id} | calendar:events:write | calendar://{{calendar_id}}/event/{{event_id}} |
POST | /calendar/v3/calendars/{calendar_id}/events/quickAdd | calendar:events:write | calendar://{{calendar_id}} |
POST | /calendar/v3/freeBusy | calendar:read | — |
GET | /calendar/v3/calendars/{calendar_id}/acl | calendar:admin | calendar://{{calendar_id}} |
MCP tools
| Name | Scope | Description |
|---|---|---|
list_calendars | calendar:read | List every calendar the authenticated user has on their calendar list (primary + shared + subscribed). |
get_calendar | calendar:read | Get a calendar's metadata (summary, timezone, description). |
list_events | calendar:read | List events on a calendar. Supports time filters (timeMin/timeMax), single-event expansion, and free-text search. |
get_event | calendar:read | Get a single event by ID. |
create_event | calendar:events:write | Create a new event on a calendar. Provide either the Google Calendar event body (full schema) or the convenience fields summary/start/end/attendees. |
update_event | calendar:events:write | Update an event (partial). Only supplied fields are modified. |
delete_event | calendar:events:write | Delete an event. Recurring events: pass sendUpdates=all to notify attendees. |
quick_add_event | calendar:events:write | Create an event from natural-language text (e.g. "Lunch with Alice Thursday 1pm"). Uses Calendar's quickAdd parser. |
query_free_busy | calendar:read | Query free/busy information for a set of calendars over a time window. Returns only busy intervals — no event details. |
list_acl | calendar:admin | List the access-control rules on a calendar (who it's shared with and how). |
Operator data schema
Keys the operator can supply under data.pbac.operator.connectors["identos.google-calendar"].* — consumed by the connector's policy.
| Key | Type | Description |
|---|---|---|
room_only_mode | boolean | When true, only calendars whose ID ends with @resource.calendar.google.com (Google Workspace resource/room calendars) are addressable. Useful for booking agents that should never touch human calendars. |