Problem
The default feature configuration in workspace-server/src/features/feature-config.ts requests six .readonly OAuth scopes that are not listed in scripts/setup-gcp.sh (and therefore not configured on the published GCP project's OAuth consent screen). Any user authenticating against the published project with default settings hits Google's "This app is blocked — This app tried to access sensitive info in your Google Account" screen, because requesting an unregistered sensitive/restricted scope on an unverified app is a hard block with no click-through.
Missing scopes
| Feature group |
Scope missing from published project / setup-gcp.sh |
drive.read |
https://www.googleapis.com/auth/drive.readonly |
calendar.read |
https://www.googleapis.com/auth/calendar.readonly |
chat.read |
https://www.googleapis.com/auth/chat.spaces.readonly |
chat.read |
https://www.googleapis.com/auth/chat.messages.readonly |
chat.read |
https://www.googleapis.com/auth/chat.memberships.readonly |
gmail.read |
https://www.googleapis.com/auth/gmail.readonly |
The corresponding write-group scopes (drive, calendar, chat.spaces, chat.messages, chat.memberships, gmail.modify) are in the published project and grant read access at the API level, so this is purely a consent-screen registration gap introduced when read/write feature groups were split out.
Root cause: two sources of truth
The scope list in scripts/setup-gcp.sh (lines 94–106) is a hardcoded array that must be manually kept in sync with workspace-server/src/features/feature-config.ts. When the read/write feature split added the six .readonly scopes to feature-config.ts, neither setup-gcp.sh nor the published GCP consent screen was updated, and nothing caught the drift.
Any fix for this issue should also close the drift gap, so this can't silently recur the next time scopes change. Options:
- Generate
setup-gcp.sh's scope list from feature-config.ts at build/run time (e.g. a small Node script the shell script shells out to, or a generated file checked in with a "do not edit" header).
- Add a CI check that parses
setup-gcp.sh's SCOPES=(...) block and diffs it against resolveFeatures().requiredScopes, failing the build on mismatch.
- Make
setup-gcp.sh query the server at runtime — e.g. node -e "console.log(require('./workspace-server/dist/auth/scopes').SCOPES.join('\n'))" — so it always prints whatever the code actually requests.
Option 3 is the smallest diff; option 2 catches the problem at PR time, which is ideal.
Proper fix for the scope mismatch itself
Restructure resolveFeatures in workspace-server/src/features/feature-resolver.ts so read groups contribute their .readonly scope only when the paired write group is disabled — falling back to the broader write scope when both groups are active. This avoids asking users to consent to both broad and narrow scopes (e.g. drive and drive.readonly), and it keeps read tools functional when users disable a write group via WORKSPACE_FEATURE_OVERRIDES (e.g. gmail.write:off).
Once that's in, the published GCP consent screen and scripts/setup-gcp.sh would still need to gain the six .readonly scopes, since they'd be requested whenever a user disables the corresponding write group. That update should land via whichever consistency mechanism is chosen above.
Workaround for users hitting the block today
Until the published project is updated, users can match the currently-registered scope set via:
export WORKSPACE_FEATURE_OVERRIDES="drive.read:off,calendar.read:off,chat.read:off,gmail.read:off"
Tradeoff: the read-only tools in those four services (e.g. gmail.search, drive.search, calendar.list, chat.listSpaces) won't be registered with the MCP server, even though the write-group scopes would cover their API calls. Users who need those tools should instead run ./scripts/setup-gcp.sh against their own GCP project and manually add the six missing .readonly scopes to their consent screen.
Repro
- Unset
WORKSPACE_CLIENT_ID and WORKSPACE_CLOUD_FUNCTION_URL so the client uses DEFAULT_CONFIG (pointing at the published geminicli.com cloud function).
- Trigger the OAuth flow on an account that isn't a test user on some project that covers every requested scope.
- Observe "This app is blocked" instead of the consent screen.
Problem
The default feature configuration in
workspace-server/src/features/feature-config.tsrequests six.readonlyOAuth scopes that are not listed inscripts/setup-gcp.sh(and therefore not configured on the published GCP project's OAuth consent screen). Any user authenticating against the published project with default settings hits Google's "This app is blocked — This app tried to access sensitive info in your Google Account" screen, because requesting an unregistered sensitive/restricted scope on an unverified app is a hard block with no click-through.Missing scopes
setup-gcp.shdrive.readhttps://www.googleapis.com/auth/drive.readonlycalendar.readhttps://www.googleapis.com/auth/calendar.readonlychat.readhttps://www.googleapis.com/auth/chat.spaces.readonlychat.readhttps://www.googleapis.com/auth/chat.messages.readonlychat.readhttps://www.googleapis.com/auth/chat.memberships.readonlygmail.readhttps://www.googleapis.com/auth/gmail.readonlyThe corresponding write-group scopes (
drive,calendar,chat.spaces,chat.messages,chat.memberships,gmail.modify) are in the published project and grant read access at the API level, so this is purely a consent-screen registration gap introduced when read/write feature groups were split out.Root cause: two sources of truth
The scope list in
scripts/setup-gcp.sh(lines 94–106) is a hardcoded array that must be manually kept in sync withworkspace-server/src/features/feature-config.ts. When the read/write feature split added the six.readonlyscopes tofeature-config.ts, neithersetup-gcp.shnor the published GCP consent screen was updated, and nothing caught the drift.Any fix for this issue should also close the drift gap, so this can't silently recur the next time scopes change. Options:
setup-gcp.sh's scope list fromfeature-config.tsat build/run time (e.g. a small Node script the shell script shells out to, or a generated file checked in with a "do not edit" header).setup-gcp.sh'sSCOPES=(...)block and diffs it againstresolveFeatures().requiredScopes, failing the build on mismatch.setup-gcp.shquery the server at runtime — e.g.node -e "console.log(require('./workspace-server/dist/auth/scopes').SCOPES.join('\n'))"— so it always prints whatever the code actually requests.Option 3 is the smallest diff; option 2 catches the problem at PR time, which is ideal.
Proper fix for the scope mismatch itself
Restructure
resolveFeaturesinworkspace-server/src/features/feature-resolver.tsso read groups contribute their.readonlyscope only when the paired write group is disabled — falling back to the broader write scope when both groups are active. This avoids asking users to consent to both broad and narrow scopes (e.g.driveanddrive.readonly), and it keeps read tools functional when users disable a write group viaWORKSPACE_FEATURE_OVERRIDES(e.g.gmail.write:off).Once that's in, the published GCP consent screen and
scripts/setup-gcp.shwould still need to gain the six.readonlyscopes, since they'd be requested whenever a user disables the corresponding write group. That update should land via whichever consistency mechanism is chosen above.Workaround for users hitting the block today
Until the published project is updated, users can match the currently-registered scope set via:
Tradeoff: the read-only tools in those four services (e.g.
gmail.search,drive.search,calendar.list,chat.listSpaces) won't be registered with the MCP server, even though the write-group scopes would cover their API calls. Users who need those tools should instead run./scripts/setup-gcp.shagainst their own GCP project and manually add the six missing.readonlyscopes to their consent screen.Repro
WORKSPACE_CLIENT_IDandWORKSPACE_CLOUD_FUNCTION_URLso the client usesDEFAULT_CONFIG(pointing at the publishedgeminicli.comcloud function).