Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"github.com/github/github-mcp-server/internal/ghmcp"
"github.com/github/github-mcp-server/pkg/github"
"github.com/github/github-mcp-server/pkg/translations"
gogithub "github.com/google/go-github/v82/github"
gogithub "github.com/google/go-github/v87/github"
"github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/stretchr/testify/require"
)
Comment on lines 18 to 24
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.25.0
require (
github.com/go-chi/chi/v5 v5.2.5
github.com/go-viper/mapstructure/v2 v2.5.0
github.com/google/go-github/v82 v82.0.0
github.com/google/go-github/v87 v87.0.0
github.com/google/jsonschema-go v0.4.2
github.com/josephburnett/jd/v2 v2.5.0
github.com/lithammer/fuzzysearch v1.1.8
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArs
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-github/v82 v82.0.0 h1:OH09ESON2QwKCUVMYmMcVu1IFKFoaZHwqYaUtr/MVfk=
github.com/google/go-github/v82 v82.0.0/go.mod h1:hQ6Xo0VKfL8RZ7z1hSfB4fvISg0QqHOqe9BP0qo+WvM=
github.com/google/go-github/v87 v87.0.0 h1:9Ck3dcOxWJyfsN8tzdah4YvmqB/7ZsstMglv/PkOsl0=
github.com/google/go-github/v87 v87.0.0/go.mod h1:hGUoT5pwm/ck5uLL+wroSVQfg8mpe+buxllCcGV4VaM=
github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0=
github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU=
github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=
Expand Down
51 changes: 32 additions & 19 deletions internal/ghmcp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,19 @@ import (
"github.com/github/github-mcp-server/pkg/scopes"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/github/github-mcp-server/pkg/utils"
gogithub "github.com/google/go-github/v82/github"
gogithub "github.com/google/go-github/v87/github"
"github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/shurcooL/githubv4"
)

// githubClients holds all the GitHub API clients created for a server instance.
type githubClients struct {
rest *gogithub.Client
gql *githubv4.Client
gqlHTTP *http.Client // retained for middleware to modify transport
raw *raw.Client
repoAccess *lockdown.RepoAccessCache
rest *gogithub.Client
restUATransp *transport.UserAgentTransport
gql *githubv4.Client
gqlHTTP *http.Client // retained for middleware to modify transport
raw *raw.Client
repoAccess *lockdown.RepoAccessCache
}

// createGitHubClients creates all the GitHub API clients needed by the server.
Expand All @@ -61,10 +62,18 @@ func createGitHubClients(cfg github.MCPServerConfig, apiHost utils.APIHostResolv
}

// Construct REST client
restClient := gogithub.NewClient(nil).WithAuthToken(cfg.Token)
restClient.UserAgent = fmt.Sprintf("github-mcp-server/%s", cfg.Version)
restClient.BaseURL = restURL
restClient.UploadURL = uploadURL
restUATransport := &transport.UserAgentTransport{
Transport: http.DefaultTransport,
Agent: fmt.Sprintf("github-mcp-server/%s", cfg.Version),
}
restClient, err := gogithub.NewClient(
gogithub.WithHTTPClient(&http.Client{Transport: restUATransport}),
gogithub.WithAuthToken(cfg.Token),
gogithub.WithEnterpriseURLs(restURL.String(), uploadURL.String()),
)
if err != nil {
return nil, fmt.Errorf("failed to create REST client: %w", err)
}

// Construct GraphQL client
// We use NewEnterpriseClient unconditionally since we already parsed the API host
Expand All @@ -80,7 +89,10 @@ func createGitHubClients(cfg github.MCPServerConfig, apiHost utils.APIHostResolv
gqlClient := githubv4.NewEnterpriseClient(graphQLURL.String(), gqlHTTPClient)

// Create raw content client (shares REST client's HTTP transport)
rawClient := raw.NewClient(restClient, rawURL)
rawClient, err := raw.NewClient(restClient, rawURL)
if err != nil {
return nil, fmt.Errorf("failed to create raw client: %w", err)
}

// Set up repo access cache for lockdown mode
var repoAccessCache *lockdown.RepoAccessCache
Expand All @@ -95,11 +107,12 @@ func createGitHubClients(cfg github.MCPServerConfig, apiHost utils.APIHostResolv
}

return &githubClients{
rest: restClient,
gql: gqlClient,
gqlHTTP: gqlHTTPClient,
raw: rawClient,
repoAccess: repoAccessCache,
rest: restClient,
restUATransp: restUATransport,
gql: gqlClient,
gqlHTTP: gqlHTTPClient,
raw: rawClient,
repoAccess: repoAccessCache,
}, nil
}

Expand Down Expand Up @@ -170,7 +183,7 @@ func NewStdioMCPServer(ctx context.Context, cfg github.MCPServerConfig) (*mcp.Se
github.RegisterUIResources(ghServer)
}

ghServer.AddReceivingMiddleware(addUserAgentsMiddleware(cfg, clients.rest, clients.gqlHTTP))
ghServer.AddReceivingMiddleware(addUserAgentsMiddleware(cfg, clients.restUATransp, clients.gqlHTTP))

return ghServer, nil
}
Expand Down Expand Up @@ -345,7 +358,7 @@ func createFeatureChecker(enabledFeatures []string, insidersMode bool) inventory
}
}

func addUserAgentsMiddleware(cfg github.MCPServerConfig, restClient *gogithub.Client, gqlHTTPClient *http.Client) func(next mcp.MethodHandler) mcp.MethodHandler {
func addUserAgentsMiddleware(cfg github.MCPServerConfig, restUATransp *transport.UserAgentTransport, gqlHTTPClient *http.Client) func(next mcp.MethodHandler) mcp.MethodHandler {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just need to be sure (I didn't re-check where it's passed from), that the transport is always created per request for http endpoint and is not going to be concurrently mutated or anything by parallel requests)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Should I add / is there a test for this ?

return func(next mcp.MethodHandler) mcp.MethodHandler {
return func(ctx context.Context, method string, request mcp.Request) (result mcp.Result, err error) {
if method != "initialize" {
Expand All @@ -368,7 +381,7 @@ func addUserAgentsMiddleware(cfg github.MCPServerConfig, restClient *gogithub.Cl
userAgent += " (insiders)"
}

restClient.UserAgent = userAgent
restUATransp.Agent = userAgent

gqlHTTPClient.Transport = &transport.UserAgentTransport{
Transport: gqlHTTPClient.Transport,
Expand Down
2 changes: 1 addition & 1 deletion pkg/errors/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"net/http"

"github.com/github/github-mcp-server/pkg/utils"
"github.com/google/go-github/v82/github"
"github.com/google/go-github/v87/github"
"github.com/modelcontextprotocol/go-sdk/mcp"
)

Expand Down
2 changes: 1 addition & 1 deletion pkg/errors/error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"net/http"
"testing"

"github.com/google/go-github/v82/github"
"github.com/google/go-github/v87/github"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand Down
6 changes: 3 additions & 3 deletions pkg/github/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
"github.com/github/github-mcp-server/pkg/scopes"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/github/github-mcp-server/pkg/utils"
"github.com/google/go-github/v82/github"
"github.com/google/go-github/v87/github"
"github.com/google/jsonschema-go/jsonschema"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
Expand Down Expand Up @@ -989,10 +989,10 @@ func runWorkflow(ctx context.Context, client *github.Client, owner, repo, workfl
var workflowType string

if workflowIDInt, parseErr := strconv.ParseInt(workflowID, 10, 64); parseErr == nil {
resp, err = client.Actions.CreateWorkflowDispatchEventByID(ctx, owner, repo, workflowIDInt, event)
_, resp, err = client.Actions.CreateWorkflowDispatchEventByID(ctx, owner, repo, workflowIDInt, event)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What is this newly omitted value?

workflowType = "workflow_id"
} else {
resp, err = client.Actions.CreateWorkflowDispatchEventByFileName(ctx, owner, repo, workflowID, event)
_, resp, err = client.Actions.CreateWorkflowDispatchEventByFileName(ctx, owner, repo, workflowID, event)
workflowType = "workflow_file"
}

Expand Down
26 changes: 13 additions & 13 deletions pkg/github/actions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

"github.com/github/github-mcp-server/internal/toolsnaps"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v82/github"
"github.com/google/go-github/v87/github"
"github.com/google/jsonschema-go/jsonschema"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -86,7 +86,7 @@ func Test_ActionsList_ListWorkflows(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
client := github.NewClient(tc.mockedClient)
client := mustNewGHClient(t, tc.mockedClient)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

❤️

deps := BaseDeps{
Client: client,
}
Expand Down Expand Up @@ -136,7 +136,7 @@ func Test_ActionsList_ListWorkflowRuns(t *testing.T) {
}),
})

client := github.NewClient(mockedClient)
client := mustNewGHClient(t, mockedClient)
deps := BaseDeps{
Client: client,
}
Expand Down Expand Up @@ -185,7 +185,7 @@ func Test_ActionsList_ListWorkflowRuns(t *testing.T) {
}),
})

client := github.NewClient(mockedClient)
client := mustNewGHClient(t, mockedClient)
deps := BaseDeps{
Client: client,
}
Expand Down Expand Up @@ -241,7 +241,7 @@ func Test_ActionsGet_GetWorkflow(t *testing.T) {
}),
})

client := github.NewClient(mockedClient)
client := mustNewGHClient(t, mockedClient)
deps := BaseDeps{
Client: client,
}
Expand Down Expand Up @@ -284,7 +284,7 @@ func Test_ActionsGet_GetWorkflowRun(t *testing.T) {
}),
})

client := github.NewClient(mockedClient)
client := mustNewGHClient(t, mockedClient)
deps := BaseDeps{
Client: client,
}
Expand Down Expand Up @@ -412,7 +412,7 @@ func Test_ActionsRunTrigger_RunWorkflow(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
client := github.NewClient(tc.mockedClient)
client := mustNewGHClient(t, tc.mockedClient)
deps := BaseDeps{
Client: client,
}
Expand Down Expand Up @@ -449,7 +449,7 @@ func Test_ActionsRunTrigger_CancelWorkflowRun(t *testing.T) {
}),
})

client := github.NewClient(mockedClient)
client := mustNewGHClient(t, mockedClient)
deps := BaseDeps{
Client: client,
}
Expand Down Expand Up @@ -480,7 +480,7 @@ func Test_ActionsRunTrigger_CancelWorkflowRun(t *testing.T) {
}),
})

client := github.NewClient(mockedClient)
client := mustNewGHClient(t, mockedClient)
deps := BaseDeps{
Client: client,
}
Expand All @@ -504,7 +504,7 @@ func Test_ActionsRunTrigger_CancelWorkflowRun(t *testing.T) {
t.Run("missing run_id for non-run_workflow methods", func(t *testing.T) {
mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{})

client := github.NewClient(mockedClient)
client := mustNewGHClient(t, mockedClient)
deps := BaseDeps{
Client: client,
}
Expand Down Expand Up @@ -556,7 +556,7 @@ func Test_ActionsGetJobLogs_SingleJob(t *testing.T) {
}),
})

client := github.NewClient(mockedClient)
client := mustNewGHClient(t, mockedClient)
deps := BaseDeps{
Client: client,
ContentWindowSize: 5000,
Expand Down Expand Up @@ -618,7 +618,7 @@ func Test_ActionsGetJobLogs_FailedJobs(t *testing.T) {
}),
})

client := github.NewClient(mockedClient)
client := mustNewGHClient(t, mockedClient)
deps := BaseDeps{
Client: client,
ContentWindowSize: 5000,
Expand Down Expand Up @@ -668,7 +668,7 @@ func Test_ActionsGetJobLogs_FailedJobs(t *testing.T) {
}),
})

client := github.NewClient(mockedClient)
client := mustNewGHClient(t, mockedClient)
deps := BaseDeps{
Client: client,
ContentWindowSize: 5000,
Expand Down
2 changes: 1 addition & 1 deletion pkg/github/code_scanning.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/github/github-mcp-server/pkg/scopes"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/github/github-mcp-server/pkg/utils"
"github.com/google/go-github/v82/github"
"github.com/google/go-github/v87/github"
"github.com/google/jsonschema-go/jsonschema"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
Expand Down
6 changes: 3 additions & 3 deletions pkg/github/code_scanning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

"github.com/github/github-mcp-server/internal/toolsnaps"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v82/github"
"github.com/google/go-github/v87/github"
"github.com/google/jsonschema-go/jsonschema"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -80,7 +80,7 @@ func Test_GetCodeScanningAlert(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
client := mustNewGHClient(t, tc.mockedClient)
deps := BaseDeps{
Client: client,
}
Expand Down Expand Up @@ -206,7 +206,7 @@ func Test_ListCodeScanningAlerts(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
client := mustNewGHClient(t, tc.mockedClient)
deps := BaseDeps{
Client: client,
}
Expand Down
16 changes: 8 additions & 8 deletions pkg/github/context_tools_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/github/github-mcp-server/internal/githubv4mock"
"github.com/github/github-mcp-server/internal/toolsnaps"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v82/github"
"github.com/google/go-github/v87/github"
"github.com/shurcooL/githubv4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -99,7 +99,7 @@ func Test_GetMe(t *testing.T) {
deps = stubDeps{clientFn: stubClientFnErr(tc.clientErr), obsv: stubExporters()}
} else {
obs := stubExporters()
deps = BaseDeps{Client: github.NewClient(tc.mockedClient), Obsv: obs}
deps = BaseDeps{Client: mustNewGHClient(t, tc.mockedClient), Obsv: obs}
}
handler := serverTool.Handler(deps)

Expand Down Expand Up @@ -155,7 +155,7 @@ func Test_GetMe_IFC_InsidersMode(t *testing.T) {

t.Run("insiders mode disabled omits ifc label from result meta", func(t *testing.T) {
deps := BaseDeps{
Client: github.NewClient(mockedHTTPClient),
Client: mustNewGHClient(t, mockedHTTPClient),
Flags: FeatureFlags{InsidersMode: false},
}
handler := serverTool.Handler(deps)
Expand All @@ -170,7 +170,7 @@ func Test_GetMe_IFC_InsidersMode(t *testing.T) {

t.Run("insiders mode enabled includes ifc label in result meta", func(t *testing.T) {
deps := BaseDeps{
Client: github.NewClient(mockedHTTPClient),
Client: mustNewGHClient(t, mockedHTTPClient),
Flags: FeatureFlags{InsidersMode: true},
}
handler := serverTool.Handler(deps)
Expand Down Expand Up @@ -326,7 +326,7 @@ func Test_GetTeams(t *testing.T) {
name: "successful get teams",
makeDeps: func() ToolDependencies {
return BaseDeps{
Client: github.NewClient(httpClientWithUser()),
Client: mustNewGHClient(t, httpClientWithUser()),
GQLClient: gqlClientForTestuser(),
}
},
Expand All @@ -351,7 +351,7 @@ func Test_GetTeams(t *testing.T) {
name: "no teams found",
makeDeps: func() ToolDependencies {
return BaseDeps{
Client: github.NewClient(httpClientWithUser()),
Client: mustNewGHClient(t, httpClientWithUser()),
GQLClient: gqlClientNoTeams(),
}
},
Expand All @@ -372,7 +372,7 @@ func Test_GetTeams(t *testing.T) {
name: "get user fails",
makeDeps: func() ToolDependencies {
return BaseDeps{
Client: github.NewClient(httpClientUserFails()),
Client: mustNewGHClient(t, httpClientUserFails()),
Obsv: stubExporters(),
}
},
Expand All @@ -384,7 +384,7 @@ func Test_GetTeams(t *testing.T) {
name: "getting GraphQL client fails",
makeDeps: func() ToolDependencies {
return stubDeps{
clientFn: stubClientFnFromHTTP(httpClientWithUser()),
clientFn: stubClientFnFromHTTP(t, httpClientWithUser()),
gqlClientFn: stubGQLClientFnErr("GraphQL client error"),
obsv: stubExporters(),
}
Expand Down
Loading
Loading