github-mcp-server
github-mcp-server copied to clipboard
feat: Add exported ToolScopeMap for library use
Summary
Adds exported types and functions in pkg/scopes for library users who need fast OAuth scope lookups at runtime.
Part 4 (final) of the OAuth scopes work:
- PR #1485: Phase 1 - OAuth scopes on tool metadata
- PR #1486: Phase 2 - Fine-grained permissions documentation
- PR #1487: Phase 3 - list-scopes command
- This PR: Phase 4 - Exported ToolScopeMap for library use
Changes
- Add
pkg/scopes/tool_scope_map.go- exported types and functions - Add
pkg/scopes/tool_scope_map_test.go- comprehensive tests
Exported Types
ToolScopeMap
map[string]*ToolScopeInfo for tool name -> scopes lookup
ToolScopeInfo
Contains RequiredScopes and AcceptedScopes as ScopeSet
ScopeSet
map[string]bool for O(1) scope lookup performance
Key Functions
BuildToolScopeMapFromMeta(tools []ToolMeta)- builds map from tool definitionsNewToolScopeInfo(required []Scope)- creates info from required scopes, auto-calculates accepted scopesGetToolScopeInfo(meta map[string]any)- creates info from tool Meta field
Key Methods
ToolScopeInfo.HasAcceptedScope(userScopes ...string)- checks if token has accessToolScopeInfo.MissingScopes(userScopes ...string)- returns missing required scopesToolScopeMap.AllRequiredScopes()- returns all unique required scopesToolScopeMap.ToolsRequiringScope(scope)- returns tools that require a scopeToolScopeMap.ToolsAcceptingScope(scope)- returns tools that accept a scope
Usage Example
import "github.com/github/github-mcp-server/pkg/scopes"
// Build scope map from tool definitions
tools := []scopes.ToolMeta{
{Name: "get_repo", Meta: someToolMeta},
{Name: "create_issue", Meta: anotherToolMeta},
}
scopeMap := scopes.BuildToolScopeMapFromMeta(tools)
// Check if user's token can use a tool
if info, ok := scopeMap["create_issue"]; ok {
userScopes := []string{"repo", "user"}
if info.HasAcceptedScope(userScopes...) {
// User can use this tool
} else {
missing := info.MissingScopes(userScopes...)
fmt.Printf("Missing scopes: %v\n", missing)
}
}
// Get all required scopes
allRequired := scopeMap.AllRequiredScopes()
fmt.Printf("All required: %v\n", allRequired.ToSlice())
Design Decisions
- ScopeSet uses
map[string]bool- O(1) lookup performance for production environments - AcceptedScopes includes parent scopes - If a tool requires
public_repo,repois also accepted due to hierarchy - Functions work with minimal interfaces -
ToolMetastruct only requiresNameandMetafields
Testing
script/lint- 0 issuesscript/test- All tests pass