mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-21 00:05:49 +02:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Apps give agents controlled access to external services. > - Google Chat uses OAuth profiles and reviewed MCP tools. > - Those profiles request membership and read-state access that the supported feature set does not need. > - Removing read-state access also requires us to block unread search filters, including on existing connections. > - This pull request reduces both OAuth methods and enforces the reduced search contract before dispatch. > - Users retain conversation lookup, message history, ordinary search, and approved message sending. ## Linked Issues or Issue Description **What happened?** Both Google Chat profiles request membership and read-state scopes. The supported tool set does not include membership listing or read-state updates. Message search still advertises an unread filter. Related work: Refs #12619. **Expected behavior** Managed and customer-owned OAuth request only the scopes needed for supported features. Unsupported unread filters fail clearly before any provider call. Existing cached catalogs and broader grants must not bypass that policy. **Steps to reproduce** Start Google Chat OAuth from either connection method and inspect the requested scopes. Inspect the message-search tool schema, then submit a search with `searchParameters.isUnread` set to true or false. **Paperclip version or commit** The scope change is based on master at `110d176fc`. **Deployment mode** Managed Cloud and self-hosted instances with Google Chat Apps enabled. ## What Changed - Remove `chat.memberships.readonly` and `chat.users.readstate.readonly` from shared profiles and all four Chat connection methods. - Hide unsupported read-state fields and instructions in agent and board Test tool schemas. - Reject explicit unread filters, including false, null, snake-case fields, and encoded filter objects, before provider dispatch. - Recheck previously approved calls and support existing profile-bound and URL-only Chat connections. - Add scope, signed broker request, OAuth URL, allowlist, schema, and dispatch regression tests. - Document coordinated app/broker rollout, existing-grant reconnects, and the remaining deployment checks. ## Verification - All seven focused OAuth and Chat gateway test files pass: 498 tests on the rebased branch. - `pnpm -r typecheck` and `pnpm build` pass, using pinned pnpm 9.15.4. - The complete sharded CI test matrix passes, including general server, Chat, workspace, serialized server, Runner, and all eight browser e2e shards. The duplicate unsharded local `pnpm test:run` was stopped after CI passed; it did not complete locally. - `git diff --check` passes. - `node scripts/ingest-app-definitions.mjs` succeeds and leaves the branch unchanged. Google Workspace JSON is the durable reviewed input used by the generator. - All current-head CI checks pass at `7ee755371714dc036fff1c7da844776fee3f2ec1`, including typecheck, build, and canary dry run. Greptile is 5/5 with zero unresolved threads. The generator concern was withdrawn after review of the source and regeneration evidence. - No production deployment or live Google consent test was performed. After coordinated deployment, verify reduced consent scopes, normal search/history, message sending, and rejection of unread filters. ## Risks - Coordinate deployment with the companion Cloud broker scope change. Mixed versions can reject exact-scope requests. - Existing tokens are not narrowed or revoked. Grants with old scopes need new consent. Do not revoke a shared Google client to migrate one profile. - Explicit unread filters now return an error instead of being sent to Google. Ordinary search and the approved send tool remain available. - No database, UI, lockfile, or workflow changes. ## Model Used OpenAI Codex, a GPT-5-based coding agent, with tool use and code execution. The exact runtime model ID and context window were not exposed in this session. ## 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 either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run 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 any risks above - [x] All Paperclip CI gates are green - [x] 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>
191 lines
9.6 KiB
Markdown
191 lines
9.6 KiB
Markdown
# Google Workspace connections
|
|
|
|
Paperclip presents Google Workspace as nine independent Apps entries, not as
|
|
one combined Google connection:
|
|
|
|
1. Gmail
|
|
2. Google Drive
|
|
3. Google Docs
|
|
4. Google Sheets
|
|
5. Google Slides
|
|
6. Google Calendar
|
|
7. Google Chat
|
|
8. Google People
|
|
9. Google Workspace Search
|
|
|
|
Each entry creates its own connection, consent grant, capability catalog,
|
|
policy, audit trail, and reconnect/revoke lifecycle. Connecting Drive does not
|
|
create a Docs or Sheets connection, and connecting Gmail does not enable
|
|
Workspace Search.
|
|
|
|
Google's hosted Workspace MCP servers are Developer Preview services. The app
|
|
cards remain independent even when several services use the same customer-owned
|
|
Google OAuth client or the same Paperclip Cloud broker deployment.
|
|
|
|
## 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.
|
|
|
|
## App matrix
|
|
|
|
| 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 |
|
|
|
|
The setup flow asks for the capability first. 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 asks whether the credential is for just
|
|
the connecting user or for any human in the company. 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.
|
|
|
|
## 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.
|
|
|
|
### 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.
|