Skip to main content

Gmail

Proxy for the Gmail v1 REST API. Gmail is fundamentally user-scoped — every message and thread belongs to a single mailbox — so this connector only supports idp_passthrough. Each request carries the caller's own OAuth grant, and the policy layer adds operator-controlled guards on outbound mail and label manipulation.

Overview​

Typical uses:

  • An agent reads the user's unread inbox to summarize their morning.
  • A scheduling bot sends confirmation emails from the user's address.
  • A compliance workflow flags and trashes phishing matches.

Service-account mode (domain-wide delegation impersonating a specific mailbox) is technically possible against Gmail but deliberately not shipped: most operator questions we've heard on agentic Gmail access are about preventing an agent from mass-emailing out of a service mailbox, not enabling it. If your use case does need SA mode, file an issue — the groundwork is already in the schema.

Default policy​

The connector ships connectors/identos.google-gmail/policy/gmail.rego (package pbac.connectors.identos.google_gmail). It holds three kinds of rule.

1. IdP-routing subject obligation​

subject_obligations contains obl if {
input.resource.type == "urn:connector:identos:google-gmail"
input.subject.idp_provider != "google"
obl := { "type": "require_authn_at", "idp": "google", ... }
}

This rule does not fire today. It reads input.subject.idp_provider, and the subject the AS builds carries the IdP as input.subject.idp, so the comparison is undefined and the rule body fails. What does stop a caller without a Google link is the gateway itself. Before it forwards a call it checks for a usable linked Google token, and without one it answers 401 with "error": "idp_link_required" and a link_url.

Read-only mode is not a Gmail rule. Use the Operator read-only mode control, which denies every scope that writes on the resources you list under pbac.operator.controls.resource_read_only.

2. Outbound recipient guards​

Operators define two lists. The rules apply to calls under gmail:messages:send and gmail:messages:write:

{
"allowed_recipient_domains": ["acme.com", "partner.example"],
"blocked_recipient_domains": ["competitor.example"]
}

Any recipient in a blocked domain denies the call. The allowlist applies only when it is non-empty, and then every To/Cc/Bcc address must match it. The rules read the To, Cc and Bcc tool arguments from input.context.rs_context.tool_arguments. A raw RFC822 message hides its recipients, so it is denied while recipient domain rules are configured.

The rules see the same arguments on both gateway paths. An MCP call passes its tool arguments. A REST call through /gateway/<name>/gmail/v1/... has its JSON body, query string and path parameters merged into tool_arguments. A REST send carries the composed raw message rather than To/Cc/Bcc, so it is denied while recipient domain rules are configured.

3. Protected labels​

{
"protected_label_ids": ["Label_42_LEGAL_HOLD"]
}

Labels listed here can't be added, removed, or deleted. Prevents an agent from silently removing a legal-hold marker by calling modify_labels with removeLabelIds=[Label_42_LEGAL_HOLD]. Delete on the protected label itself is also blocked.

Supplying operator data​

KEY=pbac.operator.connectors.identos.google-gmail

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 '{
"allowed_recipient_domains": ["acme.com"],
"blocked_recipient_domains": ["competitor.example"],
"protected_label_ids": ["Label_42_LEGAL_HOLD"]
}' \
'{dataKey:$k, payload:($s|tojson)}')"

Scope model​

ScopeUsed for
PBAC scopes (internal)gmail:messages:read, gmail:messages:write, gmail:messages:send, gmail:labels:manageRoute authorization — note that send is a dedicated scope separate from write, so you can grant an agent the ability to label/archive mail without also granting it send
Upstream OAuth scopesgmail.readonly, gmail.send, gmail.modify, gmail.labelsThe Google-side grants the user must consent to

See Google Workspace: multi-connector setup for how gmail's upstream scopes compose with drive and calendar on a single Google IdP.

Manifest reference​

  • ID: identos.google-gmail
  • Version: 1.2.0
  • Resource type: urn:connector:identos:google-gmail

Supported auth modes​

TypeDetails
idp_passthroughrequires IdP google

Setup fields​

IDLabelDefaultSecret?Notes
base_urlAPI base URLhttps://gmail.googleapis.comno—

Scopes​

Scope
gmail:messages:read
gmail:messages:write
gmail:messages:send
gmail:labels:manage

Routes​

MethodPatternScopeResource template
GET/gmail/v1/users/me/profilegmail:messages:read—
GET/gmail/v1/users/me/messagesgmail:messages:read—
GET/gmail/v1/users/me/messages/{message_id}gmail:messages:readgmail://message/{{message_id}}
GET/gmail/v1/users/me/threadsgmail:messages:read—
GET/gmail/v1/users/me/threads/{thread_id}gmail:messages:readgmail://thread/{{thread_id}}
POST/gmail/v1/users/me/messages/sendgmail:messages:send—
POST/gmail/v1/users/me/messages/{message_id}/modifygmail:messages:writegmail://message/{{message_id}}
POST/gmail/v1/users/me/messages/{message_id}/trashgmail:messages:writegmail://message/{{message_id}}
POST/gmail/v1/users/me/messages/{message_id}/untrashgmail:messages:writegmail://message/{{message_id}}
GET/gmail/v1/users/me/labelsgmail:messages:read—
POST/gmail/v1/users/me/labelsgmail:labels:manage—
DELETE/gmail/v1/users/me/labels/{label_id}gmail:labels:managegmail://label/{{label_id}}
GET/gmail/v1/users/me/draftsgmail:messages:read—
POST/gmail/v1/users/me/draftsgmail:messages:write—

MCP tools​

NameScopeDescription
get_profilegmail:messages:readGet the authenticated user's Gmail profile (email address, total messages, history ID).
list_messagesgmail:messages:readList messages in the authenticated user's mailbox. Supports Gmail search syntax (e.g. "from:alice newer_than:7d").
get_messagegmail:messages:readGet a single message by ID. Use format=full for headers+body, format=metadata for headers only.
list_threadsgmail:messages:readList conversation threads. Same query syntax as list_messages.
get_threadgmail:messages:readGet a single conversation thread with all its messages.
send_messagegmail:messages:sendSend an email. Supply either a pre-built RFC 822 'raw' payload (base64url) or the structured To/Cc/Bcc/Subject/Body fields and the connector will assemble the MIME message for you.
modify_labelsgmail:messages:writeAdd or remove labels on a message (e.g. mark as read by removing UNREAD, archive by removing INBOX).
trash_messagegmail:messages:writeMove a message to the Trash. Reversible for 30 days via untrash_message.
untrash_messagegmail:messages:writeRestore a trashed message to the inbox.
list_labelsgmail:messages:readList all labels in the authenticated user's mailbox (both system labels and user-created).
create_labelgmail:labels:manageCreate a new user label in the mailbox. Returns the label with its id, which modify_labels needs.
delete_labelgmail:labels:manageDelete a user-created label. System labels (INBOX, SENT, etc.) cannot be deleted.
list_draftsgmail:messages:readList unsent draft messages in the mailbox, with their ids and message metadata.
create_draftgmail:messages:writeCreate a draft message (not sent). Same message payload shape as send_message.

Operator data schema​

Keys the operator can supply under data.pbac.operator.connectors["identos.google-gmail"].* — consumed by the connector's policy.

KeyTypeDescription
allowed_recipient_domainsarrayOutbound email domains the caller is allowed to send to (e.g. 'acme.com'). Empty means no restriction. If set, every To/Cc/Bcc recipient must match.
blocked_recipient_domainsarrayOutbound email domains that must never receive mail (evaluated before allowed_recipient_domains; easier to manage a small denylist than a large allowlist).
protected_label_idsarraySystem/user label IDs that modify_labels and delete_label must not touch. Useful for regulatory holds (LEGAL, LITIGATION_HOLD, etc.).