Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Split PR review creation, commenting, submission and deletion#410

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Show file tree
Hide file tree
Changes fromall 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
658 changes: 633 additions & 25 deletionse2e/e2e_test.go
View file
Open in desktop

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletiongo.mod
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,7 @@ require (
require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1
github.com/google/go-github/v71 v71.0.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
Expand All@@ -25,13 +25,16 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/sagikazarmark/locafero v0.9.0 // indirect
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.14.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/oauth2 v0.29.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.5.0 // indirect
Expand Down
6 changes: 6 additions & 0 deletionsgo.sum
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,6 +45,10 @@ github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWN
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 h1:cYCy18SHPKRkvclm+pWm1Lk4YrREb4IOIb/YdFO0p2M=
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8=
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0=
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
Expand All@@ -69,6 +73,8 @@ github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zI
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
Expand Down
187 changes: 176 additions & 11 deletionsinternal/ghmcp/server.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,8 +5,11 @@ import (
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"os/signal"
"strings"
"syscall"

"github.com/github/github-mcp-server/pkg/github"
Expand All@@ -15,6 +18,7 @@ import (
gogithub"github.com/google/go-github/v69/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"github.com/shurcooL/githubv4"
"github.com/sirupsen/logrus"
)

Expand DownExpand Up@@ -44,25 +48,43 @@ type MCPServerConfig struct {
}

funcNewMCPServer(cfgMCPServerConfig) (*server.MCPServer,error) {
ghClient:=gogithub.NewClient(nil).WithAuthToken(cfg.Token)
ghClient.UserAgent=fmt.Sprintf("github-mcp-server/%s",cfg.Version)

ifcfg.Host!="" {
varerrerror
ghClient,err=ghClient.WithEnterpriseURLs(cfg.Host,cfg.Host)
iferr!=nil {
returnnil,fmt.Errorf("failed to create GitHub client with host: %w",err)
}
apiHost,err:=parseAPIHost(cfg.Host)
iferr!=nil {
returnnil,fmt.Errorf("failed to parse API host: %w",err)
}

// Construct our REST client
restClient:=gogithub.NewClient(nil).WithAuthToken(cfg.Token)
restClient.UserAgent=fmt.Sprintf("github-mcp-server/%s",cfg.Version)
restClient.BaseURL=apiHost.baseRESTURL
Copy link
CollaboratorAuthor

@williammartinwilliammartinMay 19, 2025
edited
Loading

Choose a reason for hiding this comment

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

I chose to go away fromWithEnterpriseURLS because there are hosts that are not enterprise but require different URLs e.g. review labs or development environments.

Sadly,githubv4 doesn't offer quite the same options, so I commented below why that is. It also doesn't export the internalgraphql.Client so we can't just set it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

That's a shame! Might need to propose exporting it.

restClient.UploadURL=apiHost.uploadURL

// Construct our GraphQL client
// We're using NewEnterpriseClient here unconditionally as opposed to NewClient because we already
// did the necessary API host parsing so that github.com will return the correct URL anyway.
gqlHTTPClient:=&http.Client{
Transport:&bearerAuthTransport{
transport:http.DefaultTransport,
token:cfg.Token,
},
}// We're going to wrap the Transport later in beforeInit
gqlClient:=githubv4.NewEnterpriseClient(apiHost.graphqlURL.String(),gqlHTTPClient)

// When a client send an initialize request, update the user agent to include the client info.
beforeInit:=func(_ context.Context,_any,message*mcp.InitializeRequest) {
ghClient.UserAgent=fmt.Sprintf(
userAgent:=fmt.Sprintf(
"github-mcp-server/%s (%s/%s)",
cfg.Version,
message.Params.ClientInfo.Name,
message.Params.ClientInfo.Version,
)

restClient.UserAgent=userAgent

gqlHTTPClient.Transport=&userAgentTransport{
transport:gqlHTTPClient.Transport,
agent:userAgent,
}
}

hooks:=&server.Hooks{
Expand All@@ -83,14 +105,19 @@ func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) {
}

getClient:=func(_ context.Context) (*gogithub.Client,error) {
returnghClient,nil// closing over client
returnrestClient,nil// closing over client
}

getGQLClient:=func(_ context.Context) (*githubv4.Client,error) {
returngqlClient,nil// closing over client
}

// Create default toolsets
toolsets,err:=github.InitToolsets(
enabledToolsets,
cfg.ReadOnly,
getClient,
getGQLClient,
cfg.Translator,
)
iferr!=nil {
Expand DownExpand Up@@ -213,3 +240,141 @@ func RunStdioServer(cfg StdioServerConfig) error {

returnnil
}

typeapiHoststruct {
baseRESTURL*url.URL
graphqlURL*url.URL
uploadURL*url.URL
}

funcnewDotcomHost() (apiHost,error) {
baseRestURL,err:=url.Parse("https://api.github.com/")
iferr!=nil {
returnapiHost{},fmt.Errorf("failed to parse dotcom REST URL: %w",err)
}

gqlURL,err:=url.Parse("https://api.github.com/graphql")
iferr!=nil {
returnapiHost{},fmt.Errorf("failed to parse dotcom GraphQL URL: %w",err)
}

uploadURL,err:=url.Parse("https://uploads.github.com")
iferr!=nil {
returnapiHost{},fmt.Errorf("failed to parse dotcom Upload URL: %w",err)
}

returnapiHost{
baseRESTURL:baseRestURL,
graphqlURL:gqlURL,
uploadURL:uploadURL,
},nil
}

funcnewGHECHost(hostnamestring) (apiHost,error) {
u,err:=url.Parse(hostname)
iferr!=nil {
returnapiHost{},fmt.Errorf("failed to parse GHEC URL: %w",err)
}

// Unsecured GHEC would be an error
ifu.Scheme=="http" {
returnapiHost{},fmt.Errorf("GHEC URL must be HTTPS")
}

restURL,err:=url.Parse(fmt.Sprintf("https://api.%s/",u.Hostname()))
iferr!=nil {
returnapiHost{},fmt.Errorf("failed to parse GHEC REST URL: %w",err)
}

gqlURL,err:=url.Parse(fmt.Sprintf("https://api.%s/graphql",u.Hostname()))
iferr!=nil {
returnapiHost{},fmt.Errorf("failed to parse GHEC GraphQL URL: %w",err)
}

uploadURL,err:=url.Parse(fmt.Sprintf("https://uploads.%s",u.Hostname()))
iferr!=nil {
returnapiHost{},fmt.Errorf("failed to parse GHEC Upload URL: %w",err)
}

returnapiHost{
baseRESTURL:restURL,
graphqlURL:gqlURL,
uploadURL:uploadURL,
},nil
}

funcnewGHESHost(hostnamestring) (apiHost,error) {
u,err:=url.Parse(hostname)
iferr!=nil {
returnapiHost{},fmt.Errorf("failed to parse GHES URL: %w",err)
}

restURL,err:=url.Parse(fmt.Sprintf("%s://%s/api/v3/",u.Scheme,u.Hostname()))
iferr!=nil {
returnapiHost{},fmt.Errorf("failed to parse GHES REST URL: %w",err)
}

gqlURL,err:=url.Parse(fmt.Sprintf("%s://%s/api/graphql",u.Scheme,u.Hostname()))
iferr!=nil {
returnapiHost{},fmt.Errorf("failed to parse GHES GraphQL URL: %w",err)
}

uploadURL,err:=url.Parse(fmt.Sprintf("%s://%s/api/uploads/",u.Scheme,u.Hostname()))
iferr!=nil {
returnapiHost{},fmt.Errorf("failed to parse GHES Upload URL: %w",err)
}

returnapiHost{
baseRESTURL:restURL,
graphqlURL:gqlURL,
uploadURL:uploadURL,
},nil
}

// Note that this does not handle ports yet, so development environments are out.
funcparseAPIHost(sstring) (apiHost,error) {
ifs=="" {
returnnewDotcomHost()
}

u,err:=url.Parse(s)
iferr!=nil {
returnapiHost{},fmt.Errorf("could not parse host as URL: %s",s)
}

ifu.Scheme=="" {
returnapiHost{},fmt.Errorf("host must have a scheme (http or https): %s",s)
}

ifstrings.HasSuffix(u.Hostname(),"github.com") {
returnnewDotcomHost()
}

ifstrings.HasSuffix(u.Hostname(),"ghe.com") {
returnnewGHECHost(s)
}

returnnewGHESHost(s)
}

typeuserAgentTransportstruct {
transport http.RoundTripper
agentstring
}

func (t*userAgentTransport)RoundTrip(req*http.Request) (*http.Response,error) {
req=req.Clone(req.Context())
req.Header.Set("User-Agent",t.agent)
returnt.transport.RoundTrip(req)
}

typebearerAuthTransportstruct {
transport http.RoundTripper
tokenstring
}

func (t*bearerAuthTransport)RoundTrip(req*http.Request) (*http.Response,error) {
req=req.Clone(req.Context())
req.Header.Set("Authorization","Bearer "+t.token)
returnt.transport.RoundTrip(req)
}
Loading

[8]ページ先頭

©2009-2025 Movatter.jp