-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Expose ghmcp package #437
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
Expose ghmcp package #437
Conversation
oops, created this PR against the public repo instead of my fork. Closing. |
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.
Pull Request Overview
This PR exposes the previously internal MCP server API through a new public pkg/ghmcp
package and introduces a TokenProvider
hook for dynamic token refresh without restarting the MCP server.
- Adds
pkg/ghmcp
as a public wrapper that re-exports internal types and functions. - Extends both
StdioServerConfig
andMCPServerConfig
with aTokenProvider
field and updates internal transports to use it. - Updates examples, README, and
cmd/github-mcp-server
import paths to showcase the new public API and dynamic token support.
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
File | Description |
---|---|
pkg/ghmcp/ghmcp.go | Public wrapper exporting internal MCP API and types |
pkg/ghmcp/example_tokenrefresh_test.go | Example demonstrating dynamic token refresh wrapper |
pkg/ghmcp/example_test.go | Updated usage examples for new public package |
pkg/ghmcp/README.md | Documentation for static and dynamic token configurations |
internal/ghmcp/server.go | Added TokenProvider , updated auth transports, deprecations |
cmd/github-mcp-server/main.go | Switched MCP server import to pkg/ghmcp |
Comments suppressed due to low confidence (1)
internal/ghmcp/server.go:434
- There are no unit tests covering
tokenProviderTransport
(and its peerbearerAuthProviderTransport
). Add tests to verify that theAuthorization
header is set correctly under both static and dynamic token scenarios.
func (t *tokenProviderTransport) RoundTrip(req *http.Request) (*http.Response, error) {
// bearerAuthProviderTransport uses a token provider for dynamic token support | ||
type bearerAuthProviderTransport struct { | ||
transport http.RoundTripper | ||
tokenProvider TokenProvider | ||
} | ||
|
||
func (t *bearerAuthProviderTransport) RoundTrip(req *http.Request) (*http.Response, error) { | ||
req = req.Clone(req.Context()) | ||
token := t.tokenProvider() | ||
req.Header.Set("Authorization", "Bearer "+token) | ||
return t.transport.RoundTrip(req) | ||
} | ||
|
||
// tokenProviderTransport adds GitHub authentication using a token provider | ||
type tokenProviderTransport struct { | ||
transport http.RoundTripper | ||
tokenProvider TokenProvider | ||
} | ||
|
||
func (t *tokenProviderTransport) RoundTrip(req *http.Request) (*http.Response, error) { | ||
req = req.Clone(req.Context()) | ||
token := t.tokenProvider() | ||
req.Header.Set("Authorization", "Bearer "+token) | ||
return t.transport.RoundTrip(req) | ||
} |
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 bearerAuthProviderTransport
and tokenProviderTransport
implementations share identical RoundTrip
logic. Consider consolidating them into a single reusable auth transport to reduce duplication and simplify maintenance.
// bearerAuthProviderTransport uses a token provider for dynamic token support | |
type bearerAuthProviderTransport struct { | |
transport http.RoundTripper | |
tokenProvider TokenProvider | |
} | |
func (t *bearerAuthProviderTransport) RoundTrip(req *http.Request) (*http.Response, error) { | |
req = req.Clone(req.Context()) | |
token := t.tokenProvider() | |
req.Header.Set("Authorization", "Bearer "+token) | |
return t.transport.RoundTrip(req) | |
} | |
// tokenProviderTransport adds GitHub authentication using a token provider | |
type tokenProviderTransport struct { | |
transport http.RoundTripper | |
tokenProvider TokenProvider | |
} | |
func (t *tokenProviderTransport) RoundTrip(req *http.Request) (*http.Response, error) { | |
req = req.Clone(req.Context()) | |
token := t.tokenProvider() | |
req.Header.Set("Authorization", "Bearer "+token) | |
return t.transport.RoundTrip(req) | |
} | |
// authTransport is a reusable transport that adds GitHub authentication using a token provider | |
type authTransport struct { | |
transport http.RoundTripper | |
tokenProvider TokenProvider | |
} | |
func (t *authTransport) RoundTrip(req *http.Request) (*http.Response, error) { | |
req = req.Clone(req.Context()) | |
token := t.tokenProvider() | |
req.Header.Set("Authorization", "Bearer "+token) | |
return t.transport.RoundTrip(req) | |
} |
Copilot uses AI. Check for mistakes.
|
||
- `Version`: Version of your server | ||
- `Host`: GitHub API host (e.g., "https://github.com" or "https://github.enterprise.com") | ||
- `Token`: GitHub personal access token (static token, use TokenProvider for dynamic tokens) |
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.
Since the Token
field is now deprecated in code, update this line to mark it as Deprecated
in the README (e.g., "Deprecated: use TokenProvider
...").
- `Token`: GitHub personal access token (static token, use TokenProvider for dynamic tokens) | |
- `Token` (Deprecated): GitHub personal access token (static token). Use `TokenProvider` for dynamic tokens. |
Copilot uses AI. Check for mistakes.
tokenProvider := cfg.TokenProvider | ||
if tokenProvider == nil { | ||
if cfg.Token == "" { | ||
return nil, fmt.Errorf("either Token or TokenProvider must be set") |
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.
Make this error message more explicit by referencing the config type and fields, for example: "MCPServerConfig requires either Token or TokenProvider to be set"
.
return nil, fmt.Errorf("either Token or TokenProvider must be set") | |
return nil, fmt.Errorf("MCPServerConfig requires either Token or TokenProvider to be set") |
Copilot uses AI. Check for mistakes.
No worries @albertywu. If you have a request for some of this to be exported, feel free to open an issue. Since we expect breaking changes around our Go symbols, I put a bunch of this in |
This PR makes two changes, so we can actually use this MCP server internally at my company:
Exposes a public package
pkg/ghmcp
. This public package simply delegates to the privateinternal/ghmcp
package for implementation.Adds ability for the client to specify a token provider function. Since the MCP server is a long-lived process, this provides a mechanism to update the token dynamically without needing to stop / restart the MCP server.