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 definitions -
NewToolScopeInfo(required []Scope)- creates info from required scopes, auto-calculates accepted scopes -
GetToolScopeInfo(meta map[string]any)- creates info from tool Meta field
Key Methods
-
ToolScopeInfo.HasAcceptedScope(userScopes ...string)- checks if token has access -
ToolScopeInfo.MissingScopes(userScopes ...string)- returns missing required scopes -
ToolScopeMap.AllRequiredScopes()- returns all unique required scopes -
ToolScopeMap.ToolsRequiringScope(scope)- returns tools that require a scope -
ToolScopeMap.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 issues -
script/test- All tests pass