Skip to content

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

Closed
wants to merge 2 commits into from
Closed

Conversation

albertywu
Copy link

This PR makes two changes, so we can actually use this MCP server internally at my company:

  1. Exposes a public package pkg/ghmcp. This public package simply delegates to the private internal/ghmcp package for implementation.

  2. 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.

@Copilot Copilot AI review requested due to automatic review settings May 25, 2025 22:44
@albertywu albertywu requested a review from a team as a code owner May 25, 2025 22:44
@albertywu
Copy link
Author

oops, created this PR against the public repo instead of my fork. Closing.

Copy link
Contributor

@Copilot Copilot AI left a 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 and MCPServerConfig with a TokenProvider 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 peer bearerAuthProviderTransport). Add tests to verify that the Authorization header is set correctly under both static and dynamic token scenarios.
func (t *tokenProviderTransport) RoundTrip(req *http.Request) (*http.Response, error) {

Comment on lines +415 to +439
// 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)
}
Copy link
Preview

Copilot AI May 25, 2025

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.

Suggested change
// 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)
Copy link
Preview

Copilot AI May 25, 2025

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...").

Suggested change
- `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")
Copy link
Preview

Copilot AI May 25, 2025

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".

Suggested change
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.

@albertywu albertywu closed this May 25, 2025
@williammartin
Copy link
Collaborator

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 internal. I don't expect I'll move it out soon, but a place for the conversation can still be useful. Cheers.

Sign up for free to 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.

2 participants