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

Added HTTP Server mode with "Bring your own OAuth / PAT token"#888

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

Open
Dreadnoth wants to merge6 commits intogithub:main
base:main
Choose a base branch
Loading
fromDreadnoth:main
Open
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
3 changes: 3 additions & 0 deletionsDockerfile
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,9 @@ FROM gcr.io/distroless/base-debian12
WORKDIR /server
# Copy the binary from the build stage
COPY --from=build /bin/github-mcp-server .

EXPOSE 8080

# Set the entrypoint to the server binary
ENTRYPOINT ["/server/github-mcp-server"]
# Default arguments for ENTRYPOINT
Expand Down
16 changes: 16 additions & 0 deletionsREADME.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -95,6 +95,22 @@ See [Remote Server Documentation](/docs/remote-server.md) on how to pass additio

---

## HTTP Server Mode

To run the server in HTTP mode, use the `http` command:

```bash
github-mcp-server http --port 8080
```

### HTTP Server with "Bring Your Own Token"

When running the server in HTTP mode, clients can provide their own GitHub token with each request using the `Authorization` header:

```http
Authorization: Bearer <github-token>
```

## Local GitHub MCP Server

[![Install with Docker in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=github&inputs=%5B%7B%22id%22%3A%22github_token%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22GitHub%20Personal%20Access%20Token%22%2C%22password%22%3Atrue%7D%5D&config=%7B%22command%22%3A%22docker%22%2C%22args%22%3A%5B%22run%22%2C%22-i%22%2C%22--rm%22%2C%22-e%22%2C%22GITHUB_PERSONAL_ACCESS_TOKEN%22%2C%22ghcr.io%2Fgithub%2Fgithub-mcp-server%22%5D%2C%22env%22%3A%7B%22GITHUB_PERSONAL_ACCESS_TOKEN%22%3A%22%24%7Binput%3Agithub_token%7D%22%7D%7D) [![Install with Docker in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install_Server-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=github&inputs=%5B%7B%22id%22%3A%22github_token%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22GitHub%20Personal%20Access%20Token%22%2C%22password%22%3Atrue%7D%5D&config=%7B%22command%22%3A%22docker%22%2C%22args%22%3A%5B%22run%22%2C%22-i%22%2C%22--rm%22%2C%22-e%22%2C%22GITHUB_PERSONAL_ACCESS_TOKEN%22%2C%22ghcr.io%2Fgithub%2Fgithub-mcp-server%22%5D%2C%22env%22%3A%7B%22GITHUB_PERSONAL_ACCESS_TOKEN%22%3A%22%24%7Binput%3Agithub_token%7D%22%7D%7D&quality=insiders)
Expand Down
32 changes: 32 additions & 0 deletionscmd/github-mcp-server/main.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -26,6 +26,34 @@ var (
Version: fmt.Sprintf("Version: %s\nCommit: %s\nBuild Date: %s", version, commit, date),
}

httpCmd = &cobra.Command{
Use: "http",
Short: "Start HTTP server",
Long: `Start a server that communicates via HTTP using the MCP protocol.`,
RunE: func(_ *cobra.Command, _ []string) error {
token := viper.GetString("personal_access_token")

var enabledToolsets []string
if err := viper.UnmarshalKey("toolsets", &enabledToolsets); err != nil {
return fmt.Errorf("failed to unmarshal toolsets: %w", err)
}

httpServerConfig := ghmcp.HTTPServerConfig{
Version: version,
Host: viper.GetString("host"),
Token: token,
EnabledToolsets: enabledToolsets,
DynamicToolsets: viper.GetBool("dynamic_toolsets"),
ReadOnly: viper.GetBool("read-only"),
ExportTranslations: viper.GetBool("export-translations"),
EnableCommandLogging: viper.GetBool("enable-command-logging"),
LogFilePath: viper.GetString("log-file"),
Port: viper.GetInt("port"),
}
return ghmcp.RunHTTPServer(httpServerConfig)
},
}

stdioCmd = &cobra.Command{
Use: "stdio",
Short: "Start stdio server",
Expand DownExpand Up@@ -87,6 +115,10 @@ func init() {

// Add subcommands
rootCmd.AddCommand(stdioCmd)
rootCmd.AddCommand(httpCmd)

httpCmd.Flags().Int("port", 8080, "Port to listen on for HTTP server")
_ = viper.BindPFlag("port", httpCmd.Flags().Lookup("port"))
}

func initConfig() {
Expand Down
137 changes: 129 additions & 8 deletionsinternal/ghmcp/server.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,6 +12,7 @@ import (
"os/signal"
"strings"
"syscall"
"time"

"github.com/github/github-mcp-server/pkg/errors"
"github.com/github/github-mcp-server/pkg/github"
Expand DownExpand Up@@ -115,12 +116,40 @@ func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) {
}
}

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

getGQLClient := func(_ context.Context) (*githubv4.Client, error) {
return gqlClient, nil // closing over client
getClient := func(ctx context.Context) (*gogithub.Client, error) {
if tokenVal := ctx.Value(githubTokenKey{}); tokenVal != nil {
if token, ok := tokenVal.(string); ok && token != "" {
client := gogithub.NewClient(nil).WithAuthToken(token)
client.UserAgent = restClient.UserAgent
client.BaseURL = apiHost.baseRESTURL
client.UploadURL = apiHost.uploadURL
return client, nil
}
}
return restClient, nil
}

getGQLClient := func(ctx context.Context) (*githubv4.Client, error) {
if tokenVal := ctx.Value(githubTokenKey{}); tokenVal != nil {
if token, ok := tokenVal.(string); ok && token != "" {
httpClient := &http.Client{
Transport: &bearerAuthTransport{
transport: http.DefaultTransport,
token: token,
},
}
if gqlHTTPClient.Transport != nil {
if uaTransport, ok := gqlHTTPClient.Transport.(*userAgentTransport); ok {
httpClient.Transport = &userAgentTransport{
transport: httpClient.Transport,
agent: uaTransport.agent,
}
}
}
return githubv4.NewEnterpriseClient(apiHost.graphqlURL.String(), httpClient), nil
}
}
return gqlClient, nil
}

getRawClient := func(ctx context.Context) (*raw.Client, error) {
Expand All@@ -131,15 +160,13 @@ func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) {
return raw.NewClient(client, apiHost.rawURL), nil // closing over client
}

// Create default toolsets
tsg := github.DefaultToolsetGroup(cfg.ReadOnly, getClient, getGQLClient, getRawClient, cfg.Translator)
err = tsg.EnableToolsets(enabledToolsets)

if err != nil {
return nil, fmt.Errorf("failed to enable toolsets: %w", err)
}

// Register all mcp functionality with the server
tsg.RegisterAll(ghServer)

if cfg.DynamicToolsets {
Expand All@@ -150,6 +177,21 @@ func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) {
return ghServer, nil
}

type githubTokenKey struct{}

type HTTPServerConfig struct {
Version string
Host string
Token string
EnabledToolsets []string
DynamicToolsets bool
ReadOnly bool
ExportTranslations bool
EnableCommandLogging bool
LogFilePath string
Port int
}

type StdioServerConfig struct {
// Version of the server
Version string
Expand DownExpand Up@@ -182,6 +224,76 @@ type StdioServerConfig struct {
LogFilePath string
}

func RunHTTPServer(cfg HTTPServerConfig) error {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()

t, dumpTranslations := translations.TranslationHelper()

ghServer, err := NewMCPServer(MCPServerConfig{
Version: cfg.Version,
Host: cfg.Host,
Token: cfg.Token,
EnabledToolsets: cfg.EnabledToolsets,
DynamicToolsets: cfg.DynamicToolsets,
ReadOnly: cfg.ReadOnly,
Translator: t,
})
if err != nil {
return fmt.Errorf("failed to create MCP server: %w", err)
}

logrusLogger := logrus.New()
if cfg.LogFilePath != "" {
file, err := os.OpenFile(cfg.LogFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
if err != nil {
return fmt.Errorf("failed to open log file: %w", err)
}

logrusLogger.SetLevel(logrus.DebugLevel)
logrusLogger.SetOutput(file)
}

httpOptions := []server.StreamableHTTPOption{
server.WithLogger(logrusLogger),
server.WithHeartbeatInterval(30 * time.Second),
server.WithHTTPContextFunc(extractTokenFromAuthHeader),
}

httpServer := server.NewStreamableHTTPServer(ghServer, httpOptions...)

if cfg.ExportTranslations {
dumpTranslations()
}

addr := fmt.Sprintf(":%d", cfg.Port)
srv := &http.Server{
Addr: addr,
Handler: httpServer,
}

_, _ = fmt.Fprintf(os.Stderr, "GitHub MCP Server running on HTTP at %s\n", addr)

errC := make(chan error, 1)
go func() {
errC <- srv.ListenAndServe()
}()

select {
case <-ctx.Done():
logrusLogger.Infof("shutting down server...")
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
return srv.Shutdown(shutdownCtx)
case err := <-errC:
if err != nil && err != http.ErrServerClosed {
return fmt.Errorf("error running server: %w", err)
}
}

return nil
}

// RunStdioServer is not concurrent safe.
func RunStdioServer(cfg StdioServerConfig) error {
// Create app context
Expand DownExpand Up@@ -414,3 +526,12 @@ func (t *bearerAuthTransport) RoundTrip(req *http.Request) (*http.Response, erro
req.Header.Set("Authorization", "Bearer "+t.token)
return t.transport.RoundTrip(req)
}

func extractTokenFromAuthHeader(ctx context.Context, r *http.Request) context.Context {
authHeader := r.Header.Get("Authorization")
if authHeader != "" && strings.HasPrefix(authHeader, "Bearer ") {
token := strings.TrimPrefix(authHeader, "Bearer ")
return context.WithValue(ctx, githubTokenKey{}, token)
}
return ctx
}

[8]ページ先頭

©2009-2025 Movatter.jp