Skip to content

feat: feature configuration service for scope-based toggles#284

Open
allenhutchison wants to merge 5 commits intomainfrom
feat/feature-configuration-service
Open

feat: feature configuration service for scope-based toggles#284
allenhutchison wants to merge 5 commits intomainfrom
feat/feature-configuration-service

Conversation

@allenhutchison
Copy link
Contributor

Summary

Implements the Feature Configuration Service from #255, giving users and contributors control over which services and OAuth scopes the extension requests.

  • Feature registry (features/feature-config.ts) — 18 read/write feature groups across all services, each declaring its scopes, tools, and default state
  • Feature resolver (features/feature-resolver.ts) — Three-layer resolution: baked-in defaults → settings (future) → WORKSPACE_FEATURE_OVERRIDES env var
  • Dynamic scopes — OAuth scopes are now computed from enabled features instead of hardcoded
  • Gated tool registration — Disabled tools are silently skipped; auth tools always register
  • Documentation — Full feature configuration guide added to VitePress docs site

Configuration

# Disable Gmail writes and calendar delete
WORKSPACE_FEATURE_OVERRIDES="gmail.write:off,calendar.deleteEvent:off"

# Enable experimental features
WORKSPACE_FEATURE_OVERRIDES="slides.write:on,tasks.read:on,tasks.write:on"

Supports group-level (gmail.write:off) and tool-level (calendar.deleteEvent:off) overrides. Tool-level overrides are subtractive only.

Default-OFF features

slides.write, sheets.write, tasks.read, tasks.write — scopes not in the published GCP project. Contributors can enable them with their own GCP projects.

Test plan

  • 23 new unit tests covering registry integrity, override parsing, group enable/disable, tool-level overrides, subtractive-only enforcement, layer precedence, and scope deduplication
  • Manual: verify WORKSPACE_FEATURE_OVERRIDES="gmail.write:off" hides Gmail write tools from MCP tool listing
  • Manual: verify re-enabling a disabled feature prompts re-auth for new scopes
  • Manual: verify no behavior change when env var is unset (backward compatible)

Closes #255

Implements a three-layer configuration system (defaults → settings → env
overrides) that lets users and contributors control which services and
OAuth scopes the extension requests. Each service is split into read/write
feature groups. Disabled features have their tools unregistered and their
scopes removed from OAuth requests.

Closes #255
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a comprehensive feature configuration service, providing granular control over services and OAuth scopes. It enhances the extension's adaptability and security by dynamically computing scopes and gating tool registration based on user-defined settings and environment variables. The changes include a feature registry, a feature resolver, and updated documentation to guide users and contributors.

Highlights

  • Feature Configuration Service: This PR implements a feature configuration service that allows users and contributors to control which services and OAuth scopes the extension requests, enhancing flexibility and security.
  • Dynamic Scopes: OAuth scopes are now computed dynamically based on enabled features, rather than being hardcoded, providing a more adaptive permission system.
  • Tool Registration Gating: The registration of tools is now gated, with disabled tools being silently skipped, ensuring that only enabled features are exposed and auth tools are always registered.
  • Configuration Flexibility: Introduces a three-layer resolution for feature settings: baked-in defaults, settings (future), and WORKSPACE_FEATURE_OVERRIDES environment variable, offering multiple configuration options.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive feature configuration service, enabling dynamic control over which services and OAuth scopes the extension requests. The implementation includes a well-structured feature registry (feature-config.ts), a robust resolver (feature-resolver.ts) with a three-layer precedence model (defaults, settings, environment variables), and dynamic OAuth scope computation. The changes also correctly gate tool registration based on the enabled features, with a sensible exception for authentication tools. Extensive unit tests have been added for both the feature configuration and resolver logic, ensuring correctness and reliability. Documentation has been updated to reflect these new configuration capabilities, providing clear guidance for users and contributors. Overall, this is a well-designed and thoroughly implemented feature that significantly enhances the flexibility and maintainability of the extension.

@allenhutchison
Copy link
Contributor Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new feature configuration system that provides granular control over which Google Workspace services and tools are enabled, and which OAuth scopes are requested. This system uses a three-layer precedence model (baked-in defaults, future settings, and environment variables) and dynamically resolves the required scopes and enabled tools. Documentation for this feature has been added, and the server's tool registration process has been updated to respect this configuration. Feedback includes a high-severity concern regarding the docs.write feature group requesting overly broad drive scopes, several instances of incorrect copyright years (2026 instead of 2024), unused imports and variables in test files, and a design flaw in the registerTool wrapper that breaks its method-chaining contract.

{
service: 'docs',
group: 'write',
scopes: scopes('documents', 'drive'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The docs.write feature group requests the https://www.googleapis.com/auth/drive scope, which grants broad permissions to all of a user's Google Drive files. Based on the tools in this group (docs.create, docs.writeText, etc.), this scope appears to be unnecessary and violates the principle of least privilege. The https://www.googleapis.com/auth/documents scope should be sufficient.

Please remove the 'drive' scope unless it is strictly required for a reason not apparent from the code.

Suggested change
scopes: scopes('documents', 'drive'),
scopes: scopes('documents'),

} from '../../features/feature-resolver';
import {
FEATURE_GROUPS,
featureGroupKey as _featureGroupKey,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The alias _featureGroupKey is defined but never used within this file. It can be removed to improve code clarity.

Suggested change
featureGroupKey as _featureGroupKey,
featureGroupKey,

Comment on lines +131 to +134
const { enabledTools: _enabledTools, requiredScopes } = resolveFeatures(
undefined,
'tasks.read:on',
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The variable _enabledTools is destructured from the result of resolveFeatures but is never used. You can simplify the destructuring to only extract requiredScopes.

Suggested change
const { enabledTools: _enabledTools, requiredScopes } = resolveFeatures(
undefined,
'tasks.read:on',
);
const { requiredScopes } = resolveFeatures(
undefined,
'tasks.read:on',
);

) => {
if (!enabledTools.has(name) && !name.startsWith('auth.')) {
logToFile(`[features] Skipping disabled tool: ${name}`);
return undefined as never;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The wrapper for registerTool returns undefined when a tool is skipped. This breaks the method-chaining contract of the original McpServer.registerTool, which returns the server instance (this).

While not currently used for chaining in this file, this could lead to unexpected runtime errors if the API is used differently in the future. To make the wrapper more robust, it should return the server instance to maintain the contract.

Suggested change
return undefined as never;
return server;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RFC: Feature Configuration Service — Read/Write Scope-Based Feature Toggles

1 participant