Skip to content

Add PAT scope filtering for stdio server - #1741

Merged
SamMorrowDrums merged 13 commits into
mainfrom
copilot/pat-scope-filtering
Jan 5, 2026
Merged

Add PAT scope filtering for stdio server#1741
SamMorrowDrums merged 13 commits into
mainfrom
copilot/pat-scope-filtering

Conversation

@SamMorrowDrums

@SamMorrowDrumsSamMorrowDrums commented Jan 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

Automatically filter tools based on the user's PAT OAuth scopes. This ensures users only see tools their token has permission to use, reducing clutter and preventing errors from attempting operations without the required scopes.

How It Works

  1. At startup: Server detects if token is a classic PAT (ghp_ prefix)
  2. Scope discovery: Makes HTTP HEAD request to GitHub API, reads X-OAuth-Scopes header
  3. Tool filtering: Tools requiring scopes the token doesn't have are hidden from the tool list

Token Type Behavior

Token TypeBehavior
Classic PAT (ghp_)Full scope filtering - tools hidden based on scopes
Fine-grained PAT (github_pat_)No filtering - all tools shown, API enforces permissions
Other tokensNo filtering - all tools shown

Changes

New Components in pkg/scopes/

  • filter.go: HasRequiredScopes() checks if user scopes satisfy tool requirements using the scope hierarchy
  • fetcher.go: FetchTokenScopes() gets token scopes via HTTP HEAD request (reads X-OAuth-Scopes header)

New Components in pkg/github/

  • scope_filter.go: CreateScopeFilter() creates an inventory.ToolFilter for scope-based filtering

Integration

  • Server automatically fetches token scopes on startup for classic PATs
  • Gracefully continues without filtering if scope fetch fails (logs warning)
  • Added documentation at docs/scope-filtering.md

Library Usage (for Remote Server)

The scope filtering logic is designed to be reusable:

import (
"github.com/github/github-mcp-server/pkg/scopes""github.com/github/github-mcp-server/pkg/github"
)
// Check if user scopes satisfy tool requirements hasAccess:=scopes.HasRequiredScopes(userScopes, tool.AcceptedScopes)
// Or create an inventory filterfilter:=github.CreateScopeFilter(userScopes)

Testing

  • Unit tests for HasRequiredScopes
  • Unit tests for FetchTokenScopes with mock HTTP server
  • Unit tests for CreateScopeFilter

Stacked On

This PR is stacked on #1740 (scope hierarchy PR) which provides the ExpandScopes function and scope definitions used here.

@SamMorrowDrums
SamMorrowDrums requested a review from a team as a code ownerJanuary 5, 2026 12:57
CopilotAI review requested due to automatic review settings January 5, 2026 12:57

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds PAT (Personal Access Token) scope filtering for the stdio server, enabling the server to hide tools that require OAuth scopes not granted to the token. The implementation fetches token scopes via an HTTP HEAD request to GitHub's API and applies filtering at server startup.

Key changes:

  • New pkg/scopes package with scope hierarchy-aware filtering and HTTP-based scope fetching
  • Integration with the inventory system to filter tools based on token scopes
  • Opt-in --enable-scope-filtering CLI flag (disabled by default for graceful degradation)

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
pkg/scopes/filter.goPackage documentation for scope filtering utilities
pkg/scopes/scopes.goAdds HasRequiredScopes function and internal expandScopeSet helper for hierarchy-aware scope checking
pkg/scopes/scopes_test.goComprehensive test coverage for scope expansion and filtering logic
pkg/scopes/fetcher.goHTTP fetcher implementation that retrieves token scopes via HEAD request to GitHub API
pkg/scopes/fetcher_test.goUnit tests for scope fetcher with mock HTTP servers
pkg/github/scope_filter.goCreates CreateToolScopeFilter function that bridges scopes package with inventory system
pkg/github/scope_filter_test.goTests for scope filter integration with inventory builder
internal/ghmcp/server.goIntegrates scope filtering into server startup, adding TokenScopes config field and fetchTokenScopesForHost helper
cmd/github-mcp-server/main.goAdds --enable-scope-filtering CLI flag with viper binding
README.mdAuto-generated documentation updates showing reordered accepted scopes (cosmetic)

Comment threadpkg/scopes/filter.go Outdated
@SamMorrowDrums
SamMorrowDrumsforce-pushed the copilot/add-oauth-scopes-documentation branch from c4c6491 to 742dfe3CompareJanuary 5, 2026 13:31
@SamMorrowDrums
SamMorrowDrumsforce-pushed the copilot/pat-scope-filtering branch 2 times, most recently from a496f06 to 7c31fdaCompareJanuary 5, 2026 13:44
omgitsads
omgitsads previously approved these changes Jan 5, 2026

@omgitsadsomgitsads left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🚀

Base automatically changed from copilot/add-oauth-scopes-documentation to mainJanuary 5, 2026 15:54
@SamMorrowDrums
SamMorrowDrums dismissed omgitsads’s stale reviewJanuary 5, 2026 15:54

The base branch was changed.

SamMorrowDrumsand others added 13 commits January 5, 2026 16:59
Add the ability to filter tools based on token scopes for PAT users.
This uses an HTTP HEAD request to GitHub's API to discover token scopes.
New components:
- pkg/scopes/filter.go: HasRequiredScopes checks if scopes satisfy tool requirements
- pkg/scopes/fetcher.go: FetchTokenScopes gets scopes via HTTP HEAD to GitHub API
- pkg/github/scope_filter.go: CreateScopeFilter creates inventory.ToolFilter
Integration:
- Add --filter-by-scope flag to stdio command (disabled by default)
- When enabled, fetches token scopes on startup
- Tools requiring unavailable scopes are hidden from tool list
- Gracefully continues without filtering if scope fetch fails (logs warning)
This allows the OSS server to have similar scope-based tool visibility
as the remote server, and the filter logic can be reused by remote server.
Scope filtering is now a built-in feature rather than a configurable option.
The server automatically fetches token scopes at startup and filters tools
accordingly. If scope detection fails, it logs a warning and continues with
all tools available.
- Scope filtering only applies to classic PATs which return X-OAuth-Scopes
- Fine-grained PATs and other token types skip filtering (all tools shown)
- Updated docs to clarify PAT filtering vs OAuth scope challenges
The README already has auto-generated tool documentation with scopes.
Keep only the scope hierarchy explanation which is structural.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@SamMorrowDrums
SamMorrowDrumsforce-pushed the copilot/pat-scope-filtering branch from e04a578 to 9bf0cf5CompareJanuary 5, 2026 15:59
@SamMorrowDrums
SamMorrowDrums merged commit 7d4a4a6 into mainJan 5, 2026
16 checks passed
@SamMorrowDrums
SamMorrowDrums deleted the copilot/pat-scope-filtering branch January 5, 2026 16:05
SamMorrowDrums added a commit that referenced this pull request Jan 19, 2026
Documents the new OAuth scope handling capabilities including:
- OAuth scope challenges (MCP step-up auth) for remote server
- PAT scope filtering for local/stdio server
- Documented scopes for all MCP tools
- New list-scopes CLI command
Related PRs:
- github-mcp-server: #1679, #1741, #1750, #1650
- github-mcp-server-remote: #503, #609, #618
Sign up for freeto 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.

3 participants

@SamMorrowDrums@omgitsads