Files
PaperClipAI/doc/connections/GOOGLE-WORKSPACE.md
T
DottaandPaperclip 23b2ceb0d1 feat(connections): add unified Google Workspace connector (#15545)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Connected Apps supply governed tools to those agents.
> - Google services currently require separate connections or one broad
request.
> - Users need one account and control over which services it can
access.
> - A picker must change the OAuth request, not just the screen.
> - This pull request binds selected services to consent and tool
access.
> - The benefit is simpler setup without enabling unselected tools.

## Linked Issues or Issue Description

**Subsystem affected**

Connections catalog, OAuth grants, and the MCP tool gateway.

**Current behavior**

Google setup uses separate service profiles. A combined request alone
cannot express the user's choice before Google consent. Accumulated
Google grants can be broader than a new request.

**Proposed behavior**

Offer one Workspace connection. Let users choose Gmail, Drive (including
Docs, Sheets, and Slides), and Calendar. Start all three with read and
write access. Let users disable groups or choose read-only access. Do
not offer Chat, Contacts/directory, or cross-service Search in new
setup. Bind selected scopes to durable OAuth state. Cap effective tool
access to both the saved request and provider-reported consent.

**Reason and benefit**

Users can choose services without managing several accounts. Existing
company, identity, agent, and action-policy controls remain in force.

**Breaking changes**

No database migration. Existing service connections and grants remain
supported. New Workspace requests require the companion broker change in
https://github.com/paperclipai/paperclip-cloud/pull/823. Old brokers
reject narrowed requests safely.

Related work: #14740 reduced Google scopes; #14774 introduced the
earlier catalog hold. #15412 proposes a separate Gmail REST
implementation. This maintainer-requested change extends the existing
Connections and governed tool gateway paths. It does not replace them.

## What Changed

- Add one Workspace catalog entry and a namespaced action catalog.
Retain legacy definitions and saved accounts.
- Use the approved production service picker in Apps and task-hosted
setup. Keep dropdowns between names and toggles. Block empty selection.
- Derive exact scopes from a closed registry. Default selection requests
nine scopes. New requests exclude Chat, Contacts/directory, and Search
scopes.
- Bind selected scopes through managed and customer-owned OAuth state,
callback, claim redemption, and refresh.
- Cap accumulated Google grants to the saved selection. Recheck
grant-specific permission at discovery, listing, and dispatch, including
cached actions.
- Restore the current subject's saved request on reconnect. Block on
grant-load failure. Preserve draft selection on cancel and retry. Do not
create an account on picker cancellation.
- Preserve healthy services during partial outages, without combining
grants across identities. Keep writes from being automatically replayed.
- Preserve the original 21-scope legacy Workspace envelope contract as
individual profiles evolve.
- Add production-component Storybook stories, request validators,
consent regression tests, and rollout guidance. Integrate the stories
with the current feature catalog while preserving their IDs.

## Verification

- Integrated branch merges current master and has no merge conflicts.
- Full workspace build passed on the integrated checkout.
- Token gates passed. Storybook catalog structure check passed: 283
files and 2,750 stories.
- Final affected app suites: 914 passed. This includes 502
database-backed service/gateway tests and 412
UI/shared/validator/broker-handoff/governance tests. The production
picker tests cover defaults, identity-specific reconnect, and
failed-load recovery.
- Full workspace typecheck passed after adding required current-master
Storybook annotations. The final UI build also passed.
- The full repository suite is still running and includes the two
pre-fix retry assertions. Both were updated for the current gateway's
explicit-retry contract and pass in the clean 502-test database run. No
complete final-head full-suite pass is claimed.
- The initial local database startup failure came from missing
native-library symlinks in the fresh dependency install. The package's
postinstall repair fixed it; the clean database-backed run has no
skipped tests.
- Broker focused suite: 47 passed, including real signed session →
callback → claim redemption and wrong-scope rejection. Full broker suite
before the final test-only follow-up: 3,562 passed, 103 skipped.
- Real integrated-app walkthrough: Connectors → Workspace →
customer-owned app → service picker. Verified all three defaults,
empty-selection blocking, cancel/reopen with the selected read-only
access preserved, preserved form values, and zero saved connections
after cancel. Credentials were synthetic; no Google grant was made.
- Current-head CI and Greptile review remain pending. Previous-head
results are not treated as current.

## Risks

Ship the companion broker before app instances send narrowed requests.
Enable the intended Workspace client/profile separately on the target
broker. This pull request does not deploy a tenant app, start a fleet
rollout, alter Google Console, or upgrade the pinned reviewer stack.

The three-group picker is not proof of live provider access. Before
production, verify a real selected-service grant, reconnect, refresh,
allowed reads/writes, and denied unselected actions on the isolated
staging reviewer app. Google verification and preview enrollment remain
separate requirements.

Legacy service profiles stay compatible. New grant metadata keeps
provider scope evidence separate from requested scope evidence. Missing
or unsupported evidence fails closed. No scope union across user or
organization grants is allowed.

## Model Used

OpenAI Codex, GPT-5-based coding agent, with repository editing, code
execution, and browser tools. The exact serving model ID and
context-window size were not exposed.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have described the issue in-PR following the relevant issue
template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal issue
ID
- [ ] I have run all final-head tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-19 12:07:30 -05:00

295 lines
17 KiB
Markdown

# Google Workspace connections
New Google Workspace setup uses one `google-workspace` connection and the
`workspace.all` OAuth profile. Paperclip first offers Gmail, Drive (including
Docs, Sheets, and Slides), and Calendar. All three start enabled with read and
write access. Users can disable a group or choose read-only access. New setup
does not offer Chat, Contacts/directory, or cross-service Search.
The combined connection has one credential grant, capability catalog, policy
profile, audit trail and reconnect lifecycle. It requests only the selected
groups' scopes (nine scopes for the default selection), not the legacy full
union. Google's consent screen also lets users decline
permissions; discovery skips services without grants and excludes unsupported
actions. The gateway checks actual grant scopes again before each call, including
after refresh. Requested scopes alone never authorize an action.
Discovery isolates service outages. Healthy services remain usable while the
connection's health message identifies unavailable services and recommends a
catalog refresh. Failed services' cached actions are disabled until rediscovered;
an outage affecting every requested service still fails the refresh. Loss of the
shared OAuth grant never degrades into a successful partial discovery. Because
the catalog is shared, actions skipped because of narrower consent are preserved
when another active grant supports them. Failed services and tools removed from a
successful discovery are still disabled. Each caller's listing and dispatch use only their
selected grant, without combining permissions from different identities.
Individual call failures are reported and audited without marking unrelated
Google services unhealthy; they do not trigger automatic write retries.
Existing individual product connections and their direct setup routes remain
supported. They are not automatically merged, upgraded or reauthorized. The Sheets
robot-account method remains separate because it uses explicitly shared files,
not the user's Google OAuth identity.
Google's hosted Workspace MCP servers are Developer Preview services. The app
endpoints remain separate. Paperclip namespaces actions by service and routes them
to a closed endpoint registry using the combined grant. It does not create child
connections or send provider tokens through Paperclip ID.
The catalog reuses the existing reviewed Google mark (`google-people.svg`),
registered under its own `google-workspace` brand identity. No new artwork is
downloaded or substituted for a product logo.
## Combined profile deployment and verification
Deploy the companion Cloud broker registry before enabling `workspace.all` in
`CLOUD_HARNESS_CONNECTOR_GOOGLE_ENABLED_PROFILES`. It uses the existing WORKSPACE
client pair; that client's Cloud project must have every applicable MCP API and
preview enrollment. Do not remove legacy enabled profiles while their grants are
in use. This code change does not enable the broker profile or deploy any stack.
The signed request and envelope authentication bind the exact selected request;
the legacy 21-scope profile remains supported for old clients. The encrypted
credential payload preserves Google's actual nonempty
subset. Missing scope evidence and scopes outside that reviewed union fail closed
on authorization and refresh. Individual legacy profiles retain exact matching.
For the new `google/workspace.all` profile only, HKDF info is SHA-256(AAD) to
remain below Node's 1024-byte limit. AES-GCM still authenticates the complete AAD;
legacy envelope key derivation is unchanged.
Managed OAuth: browser → Cloud `/v1/connector/sessions` → Google
`https://accounts.google.com/o/oauth2/v2/auth` → Cloud
`/v1/connector/oauth/google/callback` → instance
`/api/tools/oauth/cloud-connector/callback`. Cloud exchanges codes and refreshes at
`https://oauth2.googleapis.com/token`; provider tool calls go directly from the
instance to the fixed endpoints below. Customer OAuth uses the instance's existing
`/api/tools/oauth/callback` instead. Register that exact HTTPS or loopback callback
on the customer-owned client.
Removal is local for managed grants: Google's project-wide revocation can break
other connections. Before rollout, verify full and partial consent with an enrolled
test account, allowed reads/writes, reconnect, refresh and removal. Deterministic
tests are not real-provider proof. Combining profiles simplifies onboarding, not
Google's per-scope demonstration, justification or security-assessment obligations.
Sources: [granular consent](https://developers.google.com/identity/protocols/oauth2/resources/granular-permissions),
[Workspace MCP setup](https://developers.google.com/workspace/guides/configure-mcp-servers).
## Connections catalog and rollout
The Connections page offers one Google Workspace card for new accounts. Saved
individual-service accounts stay visible and retain their management routes.
This replaces the earlier display-only Google visibility hold. Showing the card
does not enable managed OAuth: signed instance-specific broker availability
still controls it. A customer-owned OAuth app remains a separate setup method.
Do not upgrade or unpin existing verification instances as an implementation
step. Qualify the selected-scope flow on an isolated reviewer staging app first.
See [service consent](GOOGLE-WORKSPACE-SERVICE-CONSENT.md) for the app/broker
deployment order and live verification gates.
## Developer Preview enrollment
Google grants preview access to the specific Workspace email addresses and
Google Cloud project numbers registered with the program. Submitting the form
is not the approval signal:
1. Google first sends a Google Group membership notification after verifying
the Workspace account.
2. Google then sends a final confirmation after registering the Cloud project,
usually within a couple of days. This final email is the signal that MCP
testing can begin.
3. If no final confirmation arrives within a week, check spam and contact the
Developer Preview program team from the
[program page](https://developers.google.com/workspace/preview).
Enrollment does not authorize every user of an OAuth client. Additional tester
emails and Cloud projects must be added through Google's member request forms.
Google's preview terms also prohibit making a pre-GA integration available to
end users outside the enrolled company or domain unless Google grants explicit
permission. Consequently, Paperclip-managed Google OAuth is limited to
registered internal testers during preview. Other companies must enroll their
own Workspace testers and Cloud project and use a customer-owned OAuth app until
Google makes Workspace MCP generally available.
## Service endpoints and legacy capability choices
| App card | MCP endpoint | Capability choices |
| --- | --- | --- |
| Gmail | `https://gmailmcp.googleapis.com/mcp/v1` | Read only; read and create drafts |
| Google Drive | `https://drivemcp.googleapis.com/mcp/v1` | Read only; read and create files |
| Google Docs | `https://docsmcp.googleapis.com/mcp/v1` | Read only; read and edit |
| Google Sheets | `https://sheetsmcp.googleapis.com/mcp/v1` | Read only; read and edit; share selected sheets with the robot account |
| Google Slides | `https://slidesmcp.googleapis.com/mcp/v1` | Read only; read and edit |
| Google Calendar | `https://calendarmcp.googleapis.com/mcp/v1` | Read only; read and manage events |
| Google Chat | `https://chatmcp.googleapis.com/mcp/v1` | Read only; read and send messages |
| Google People | `https://people.googleapis.com/mcp/v1` | Read contacts |
| Google Workspace Search | `https://workspacemcp.googleapis.com/mcp/v1` | Search Workspace |
Legacy product setup exposes capability choices under Change. The combined
Workspace setup has one capability and asks for service groups before Google
consent. The endpoint table also documents legacy connections; it is not the
new-account service list.
When the managed method is
available, it uses Paperclip by default. A small **Use your own Google OAuth app**
link reveals the custom client fields; **Use Paperclip instead** returns to the
managed method. The available authentication methods are:
- **Connect with Paperclip** uses the Paperclip Cloud broker when that exact
profile is returned for this enrolled instance by the signed
`POST https://my.paperclip.app/v1/connector/instance-status` request. The
anonymous capabilities document is global discovery only and never enables
an internal-pilot method locally.
- **Use your own Google OAuth app** uses customer-supplied OAuth credentials and
the app definition's exact reviewed scopes.
- **Use the Paperclip robot account** remains an additional Google Sheets-only
option for explicitly shared spreadsheets.
Before Google consent, the setup flow shows the credential's human and agent
access and lets the user change it. A personal choice stores
the tokens only on that user's grant. A company choice stores them on the
default organization grant, while still recording which signed-in Google
principal completed consent so refresh and reconnect stay bound to that
principal.
Catalog discovery and connection creation use the same signed, instance-specific
profile availability. Local enrollment files and Cloud-delivered environment
identities follow this same path; neither enables managed methods globally in
the static app definitions. Saved connections remain recognizable for OAuth
callback, refresh, and revoke, while the broker enforces current profile access.
Switching capability or authentication methods preserves the selected credential
owner when the new method supports that owner.
## Legacy product-specific broker profiles
The Paperclip-managed method signs every broker request with one explicit
profile. The broker binds that profile into sessions, one-time claims, sealed
token envelopes, and refresh. Per-profile removal is local-only for managed
Google grants. Google's revocation endpoint can invalidate all grants for the
same user and managed client, so Paperclip does not call it while removing one
Workspace profile.
| App | Read profile | Write profile |
| --- | --- | --- |
| Gmail | `gmail.read` | `gmail.draft` |
| Drive | `drive.read` | `drive.write` |
| Docs | `docs.read` | `docs.write` |
| Sheets | `sheets.read` | `sheets.write` |
| Slides | `slides.read` | `slides.write` |
| Calendar | `calendar.read` | `calendar.write` |
| Chat | `chat.read` | `chat.write` |
| People | `people.read` | — |
| Workspace Search | `workspace-search.read` | — |
Every new signed request includes a profile. The Cloud broker rejects a request
whose provider, profile, or exact scope set does not match its closed registry.
## Instance configuration
All Paperclip-managed Google methods use the existing enrolled-instance keys:
```dotenv
PAPERCLIP_CLOUD_CONNECTOR_BASE_URL=https://my.paperclip.app
PAPERCLIP_CLOUD_CONNECTOR_ENVIRONMENT=production
PAPERCLIP_CLOUD_CONNECTOR_INSTANCE_ID=inst_example
PAPERCLIP_CLOUD_CONNECTOR_SIGN_PRIVATE_KEY=...
PAPERCLIP_CLOUD_CONNECTOR_SEAL_PRIVATE_KEY=...
```
No per-app client secret is stored on the Paperclip instance for the managed
path. For customer-owned OAuth, the setup flow collects that customer's Google
OAuth client ID and secret and stores them through the normal instance-vault
path.
Cloud-hosted stacks receive these values through the existing per-stack secret
delivery path. A self-hosted instance creates its keys during enrollment and
stores them with owner-only permissions in the instance's ignored secret
directory. The setup page supplies its authenticated same-origin HTTPS address
to enrollment, so a normal Tailscale-hosted self-hoster does not need to edit
`config.json` or set `PAPERCLIP_PUBLIC_URL`; the enrolled origin becomes the
durable callback binding. The former `PAPERCLIP_ID_CONNECTOR_*` values use an incompatible
Paperclip ID protocol and are not read aliases. Enroll with Paperclip Cloud and
reconnect legacy grants before their old access tokens expire.
The gallery requests the broker capability document with a short cache. A
Paperclip-managed method is omitted unless its exact profile is enabled at the
broker; the independent app card and customer-owned OAuth method remain
available. This supports profile-by-profile rollout and rollback without
collapsing the nine cards into one app.
See [Gmail connection](./GMAIL.md) for the detailed enrollment, signing,
encryption, environment-isolation, and security review runbook inherited by all
profiles.
## Safety boundary
Every Google profile has an explicit MCP tool allowlist. Unknown Developer
Preview tools default to disabled. Read profiles expose only reviewed read
operations. Write profiles add only the reviewed write operations for their app;
destructive or unreviewed tools do not become available merely because Google
adds them upstream.
### Service-specific minimum scopes (2026-09-30)
Docs, Sheets, and Slides request only their service's read-only scope in the
read profile and its read/write scope in the write profile. They do not also
request `drive.readonly` or `drive.file`: those are authorization alternatives,
not additional requirements. Drive and Workspace Search retain their separate
Drive permissions. Calendar write requests `calendar.calendarlist.readonly`
and `calendar.events`; the latter also authorizes `suggest_time`. Calendar read
retains `calendar.events.freebusy` alongside list/event read-only access.
References: Google's [Docs read](https://developers.google.com/workspace/docs/api/reference/mcp/tools_list/read_doc)
and [update](https://developers.google.com/workspace/docs/api/reference/mcp/tools_list/update_doc),
[Sheets read](https://developers.google.com/workspace/sheets/api/reference/mcp/tools_list/get_spreadsheet)
and [update](https://developers.google.com/workspace/sheets/api/reference/mcp/tools_list/update_values),
[Slides read](https://developers.google.com/workspace/slides/api/reference/mcp/tools_list/read_presentation)
and [update](https://developers.google.com/workspace/slides/api/reference/mcp/tools_list/update_presentation),
and [Calendar suggest_time](https://developers.google.com/workspace/calendar/api/v3/reference/mcp/tools_list/suggest_time).
The write scopes support editing existing accessible files by ID. `drive.file`
is a valid narrower alternative for app-authorized files, but would require a
different per-file authorization workflow. Read-only profiles remain separate;
the project-wide union is not the scope set requested by every connection.
Deploy with the matching Cloud broker registry and test fresh grants in staging
before production. Signed authorization and refresh requests use exact scope
sets, so mixed versions fail closed. The broker rejects old broader grants and
refresh responses without explicit scope evidence for these reduced profiles.
Reconnect affected connections; do not relabel or globally revoke existing
tokens. Customer-owned methods request the same reduced sets on new consent;
previously issued grants are not retroactively narrowed. The 21-scope integration
union is unchanged by these profile-level reductions. Console must still keep
Drive/free-busy entries required by other profiles and separately used identity
scopes. Fresh provider proof is a release requirement, not implied by unit tests.
### Google Chat scope reduction (2026-09-22)
`chat.read` requests only `chat.spaces.readonly` and `chat.messages.readonly`.
`chat.write` adds `chat.messages.create`. The same sets apply to both managed
and customer-owned OAuth methods. Neither requests `chat.memberships.readonly`
nor `chat.users.readstate.readonly`.
Conversation lookup, message history, ordinary message search, and the write
profile's message sending remain supported. Membership listing and marking
messages read/unread remain outside the tool allowlist. `search_messages`
cannot filter by read state: the app hides `isUnread` from its agent and Test
schemas and rejects any explicit `isUnread`/`is_unread` argument, including
`false` or `null`, before dispatch. It never silently drops the filter. The
guard also applies to cached catalogs and existing broader grants.
References: Google's [Chat MCP setup](https://developers.google.com/workspace/chat/api/guides/configure-mcp-server)
and [message-search parameters](https://developers.google.com/workspace/chat/api/reference/mcp/tools_list/search_messages).
Roll out the app and Cloud broker scope registries together in staging and
production: their signed requests and sealed credentials use exact scope sets.
During a mixed-version rollout, Chat authorization/refresh can fail closed.
Existing Google tokens are not retroactively narrowed or revoked by this code
change. A refresh response still containing removed scopes, or omitting the
scope set needed to verify the grant, is rejected by the broker; reconnect
affected Chat grants for new consent. Do not revoke the
shared Google client to migrate one profile, because that can break other
Workspace connections. After deployment, verify fresh reduced-scope consent,
ordinary Chat search/history and sending, then reconcile Google Console and
the verification evidence with the deployed scope set.