mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-24 18:19:05 +02:00
## 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>
295 lines
17 KiB
Markdown
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.
|