-
Notifications
You must be signed in to change notification settings - Fork 3.1k
feat: Add list-scopes command to show required OAuth scopes #1487
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: SamMorrowDrums/oauth-scopes-phase2
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,364 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package main | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "context" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "encoding/json" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "fmt" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "os" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "sort" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "strings" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/github/github-mcp-server/pkg/github" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/github/github-mcp-server/pkg/lockdown" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/github/github-mcp-server/pkg/raw" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/github/github-mcp-server/pkg/scopes" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/github/github-mcp-server/pkg/toolsets" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/github/github-mcp-server/pkg/translations" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gogithub "github.com/google/go-github/v79/github" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/shurcooL/githubv4" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/spf13/cobra" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/spf13/viper" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ToolScopeInfo contains scope information for a single tool. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type ToolScopeInfo struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Name string `json:"name"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Toolset string `json:"toolset"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ReadOnly bool `json:"read_only"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RequiredScopes []string `json:"required_scopes"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AcceptedScopes []string `json:"accepted_scopes,omitempty"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ScopesOutput is the full output structure for the list-scopes command. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type ScopesOutput struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Tools []ToolScopeInfo `json:"tools"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| UniqueScopes []string `json:"unique_scopes"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ScopesByTool map[string][]string `json:"scopes_by_tool"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ToolsByScope map[string][]string `json:"tools_by_scope"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| EnabledToolsets []string `json:"enabled_toolsets"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ReadOnly bool `json:"read_only"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var listScopesCmd = &cobra.Command{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Use: "list-scopes", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Short: "List required OAuth scopes for enabled tools", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Long: `List the required OAuth scopes for all enabled tools. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| This command creates a toolset group based on the same flags as the stdio command | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| and outputs the required OAuth scopes for each enabled tool. This is useful for | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| determining what scopes a token needs to use specific tools. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The output format can be controlled with the --output flag: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - text (default): Human-readable text output | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - json: JSON output for programmatic use | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - summary: Just the unique scopes needed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Examples: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # List scopes for default toolsets | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| github-mcp-server list-scopes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # List scopes for specific toolsets | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| github-mcp-server list-scopes --toolsets=repos,issues,pull_requests | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # List scopes for all toolsets | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| github-mcp-server list-scopes --toolsets=all | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Output as JSON | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| github-mcp-server list-scopes --output=json | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Just show unique scopes needed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| github-mcp-server list-scopes --output=summary`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RunE: func(_ *cobra.Command, _ []string) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return runListScopes() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func init() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| listScopesCmd.Flags().StringP("output", "o", "text", "Output format: text, json, or summary") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _ = viper.BindPFlag("list-scopes-output", listScopesCmd.Flags().Lookup("output")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rootCmd.AddCommand(listScopesCmd) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // mockScopesGetClient returns a mock GitHub client for scope listing. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func mockScopesGetClient(_ context.Context) (*gogithub.Client, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return gogithub.NewClient(nil), nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // mockScopesGetGQLClient returns a mock GraphQL client for scope listing. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func mockScopesGetGQLClient(_ context.Context) (*githubv4.Client, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return githubv4.NewClient(nil), nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // mockScopesGetRawClient returns a mock raw client for scope listing. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func mockScopesGetRawClient(_ context.Context) (*raw.Client, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func runListScopes() error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Get toolsets configuration (same logic as stdio command) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var enabledToolsets []string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := viper.UnmarshalKey("toolsets", &enabledToolsets); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to unmarshal toolsets: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // No passed toolsets configuration means we enable the default toolset | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if len(enabledToolsets) == 0 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enabledToolsets = []string{github.ToolsetMetadataDefault.ID} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| readOnly := viper.GetBool("read-only") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| outputFormat := viper.GetString("list-scopes-output") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Create translation helper | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t, _ := translations.TranslationHelper() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Create toolset group with mock clients (no actual API calls needed) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| repoAccessCache := lockdown.GetInstance(nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tsg := github.DefaultToolsetGroup(readOnly, mockScopesGetClient, mockScopesGetGQLClient, mockScopesGetRawClient, t, 5000, github.FeatureFlags{}, repoAccessCache) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Process enabled toolsets (same logic as server.go) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If "all" is present, override all other toolsets | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if github.ContainsToolset(enabledToolsets, github.ToolsetMetadataAll.ID) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enabledToolsets = []string{github.ToolsetMetadataAll.ID} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If "default" is present, expand to real toolset IDs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if github.ContainsToolset(enabledToolsets, github.ToolsetMetadataDefault.ID) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enabledToolsets = github.AddDefaultToolset(enabledToolsets) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Enable the requested toolsets | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err := tsg.EnableToolsets(enabledToolsets, nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to enable toolsets: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Collect all tools and their scopes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output := collectToolScopes(tsg, enabledToolsets, readOnly) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Output based on format | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch outputFormat { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "json": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return outputJSON(output) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "summary": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return outputSummary(output) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return outputText(output) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func collectToolScopes(tsg *toolsets.ToolsetGroup, enabledToolsets []string, readOnly bool) ScopesOutput { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var tools []ToolScopeInfo | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| scopeSet := make(map[string]bool) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| scopesByTool := make(map[string][]string) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toolsByScope := make(map[string][]string) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Get all toolset names and sort them for consistent output | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var toolsetNames []string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for name := range tsg.Toolsets { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if name != "dynamic" { // Skip dynamic toolset | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toolsetNames = append(toolsetNames, name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sort.Strings(toolsetNames) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, toolsetName := range toolsetNames { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toolset := tsg.Toolsets[toolsetName] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !toolset.Enabled { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Get active tools (respects read-only setting) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| activeTools := toolset.GetActiveTools() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, serverTool := range activeTools { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tool := serverTool.Tool | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Extract scopes from tool metadata | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requiredScopes := scopes.GetScopesFromMeta(tool.Meta) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requiredScopeStrs := scopes.ScopeStrings(requiredScopes) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Calculate accepted scopes (scopes that also satisfy the requirement due to hierarchy) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| acceptedScopeStrs := []string{} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, reqScope := range requiredScopes { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| accepted := scopes.GetAcceptedScopes(reqScope) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, accScope := range accepted { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if accScope != reqScope { // Don't duplicate the required scope | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| accStr := accScope.String() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Avoid duplicates | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| found := false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, existing := range acceptedScopeStrs { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if existing == accStr { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| found = true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !found { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| acceptedScopeStrs = append(acceptedScopeStrs, accStr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+182
to
+201
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| acceptedScopeStrs := []string{} | |
| for _, reqScope := range requiredScopes { | |
| accepted := scopes.GetAcceptedScopes(reqScope) | |
| for _, accScope := range accepted { | |
| if accScope != reqScope { // Don't duplicate the required scope | |
| accStr := accScope.String() | |
| // Avoid duplicates | |
| found := false | |
| for _, existing := range acceptedScopeStrs { | |
| if existing == accStr { | |
| found = true | |
| break | |
| } | |
| } | |
| if !found { | |
| acceptedScopeStrs = append(acceptedScopeStrs, accStr) | |
| } | |
| } | |
| } | |
| } | |
| // Calculate accepted scopes (scopes that also satisfy the requirement due to hierarchy) | |
| acceptedScopeMap := make(map[string]bool) | |
| for _, reqScope := range requiredScopes { | |
| accepted := scopes.GetAcceptedScopes(reqScope) | |
| for _, accScope := range accepted { | |
| if accScope != reqScope { // Don't duplicate the required scope | |
| acceptedScopeMap[accScope.String()] = true | |
| } | |
| } | |
| } | |
| acceptedScopeStrs := make([]string, 0, len(acceptedScopeMap)) | |
| for scope := range acceptedScopeMap { | |
| acceptedScopeStrs = append(acceptedScopeStrs, scope) | |
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,24 @@ | ||||||||||||||||||||
| #!/bin/bash | ||||||||||||||||||||
| # | ||||||||||||||||||||
| # List required OAuth scopes for enabled tools. | ||||||||||||||||||||
| # | ||||||||||||||||||||
| # Usage: | ||||||||||||||||||||
| # script/list-scopes [--toolsets=...] [--output=text|json|summary] | ||||||||||||||||||||
| # | ||||||||||||||||||||
| # Examples: | ||||||||||||||||||||
| # script/list-scopes | ||||||||||||||||||||
| # script/list-scopes --toolsets=all --output=json | ||||||||||||||||||||
| # script/list-scopes --toolsets=repos,issues --output=summary | ||||||||||||||||||||
| # | ||||||||||||||||||||
|
|
||||||||||||||||||||
| set -e | ||||||||||||||||||||
|
|
||||||||||||||||||||
| cd "$(dirname "$0")/.." | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # Build the server if it doesn't exist or is outdated | ||||||||||||||||||||
| if [ ! -f github-mcp-server ] || [ cmd/github-mcp-server/list_scopes.go -nt github-mcp-server ]; then | ||||||||||||||||||||
| echo "Building github-mcp-server..." >&2 | ||||||||||||||||||||
| go build -o github-mcp-server ./cmd/github-mcp-server | ||||||||||||||||||||
| fi | ||||||||||||||||||||
|
|
||||||||||||||||||||
| exec ./github-mcp-server list-scopes "$@" | ||||||||||||||||||||
|
Comment on lines
+18
to
+24
|
||||||||||||||||||||
| # Build the server if it doesn't exist or is outdated | |
| if [ ! -f github-mcp-server ] || [ cmd/github-mcp-server/list_scopes.go -nt github-mcp-server ]; then | |
| echo "Building github-mcp-server..." >&2 | |
| go build -o github-mcp-server ./cmd/github-mcp-server | |
| fi | |
| exec ./github-mcp-server list-scopes "$@" | |
| # Always run the latest code, consistent with other scripts | |
| exec go run ./cmd/github-mcp-server list-scopes "$@" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The toolset processing logic is missing a call to
github.CleanToolsets()that exists in the stdio command (seeinternal/ghmcp/server.goline 102). This function:Without this, the command may behave inconsistently with the stdio command when users provide invalid or malformed toolset names. Add the CleanToolsets call before processing special keywords: