Skip to content

Commit 3956a97

Browse files
committed
WIP
1 parent 44d9e13 commit 3956a97

File tree

12 files changed

+1893
-1536
lines changed

12 files changed

+1893
-1536
lines changed

go.mod

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,21 @@ require (
1515

1616
require (
1717
github.com/aymerick/douceur v0.2.0 // indirect
18+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
19+
github.com/felixge/httpsnoop v1.0.4 // indirect
20+
github.com/go-logr/logr v1.4.3 // indirect
21+
github.com/go-logr/stdr v1.2.2 // indirect
1822
github.com/go-openapi/jsonpointer v0.19.5 // indirect
1923
github.com/go-openapi/swag v0.21.1 // indirect
2024
github.com/gorilla/css v1.0.1 // indirect
2125
github.com/josharian/intern v1.0.0 // indirect
2226
github.com/mailru/easyjson v0.7.7 // indirect
2327
github.com/stretchr/objx v0.5.2 // indirect
2428
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
29+
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
30+
go.opentelemetry.io/otel v1.39.0 // indirect
31+
go.opentelemetry.io/otel/metric v1.39.0 // indirect
32+
go.opentelemetry.io/otel/trace v1.39.0 // indirect
2533
go.yaml.in/yaml/v3 v3.0.4 // indirect
2634
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
2735
golang.org/x/net v0.38.0 // indirect
@@ -31,13 +39,13 @@ require (
3139
require (
3240
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
3341
github.com/fsnotify/fsnotify v1.9.0 // indirect
42+
github.com/go-chi/chi v1.5.5
3443
github.com/go-viper/mapstructure/v2 v2.4.0
3544
github.com/google/go-querystring v1.1.0 // indirect
3645
github.com/inconshreveable/mousetrap v1.1.0 // indirect
3746
github.com/modelcontextprotocol/go-sdk v1.2.0
3847
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
3948
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
40-
github.com/rogpeppe/go-internal v1.13.1 // indirect
4149
github.com/sagikazarmark/locafero v0.11.0 // indirect
4250
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7
4351
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466
@@ -47,9 +55,9 @@ require (
4755
github.com/spf13/pflag v1.0.10
4856
github.com/subosito/gotenv v1.6.0 // indirect
4957
github.com/yosida95/uritemplate/v3 v3.0.2
58+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0
5059
golang.org/x/oauth2 v0.30.0 // indirect
51-
golang.org/x/sys v0.31.0 // indirect
60+
golang.org/x/sys v0.39.0 // indirect
5261
golang.org/x/text v0.28.0 // indirect
53-
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
5462
gopkg.in/yaml.v3 v3.0.1 // indirect
5563
)

go.sum

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
22
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
3+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
4+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
35
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
46
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
57
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
68
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
79
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
810
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11+
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
12+
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
913
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
1014
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
1115
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
1216
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
17+
github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE=
18+
github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw=
19+
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
20+
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
21+
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
22+
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
23+
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
1324
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
1425
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
1526
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
@@ -28,6 +39,8 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD
2839
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
2940
github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=
3041
github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
42+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
43+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
3144
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
3245
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
3346
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -37,7 +50,6 @@ github.com/josephburnett/jd v1.9.2/go.mod h1:bImDr8QXpxMb3SD+w1cDRHp97xP6UwI88xU
3750
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
3851
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
3952
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
40-
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
4153
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
4254
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
4355
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -61,8 +73,8 @@ github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8
6173
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
6274
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
6375
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
64-
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
65-
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
76+
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
77+
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
6678
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
6779
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
6880
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
@@ -96,6 +108,20 @@ github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zI
96108
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
97109
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
98110
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
111+
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
112+
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
113+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y=
114+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ=
115+
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
116+
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
117+
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
118+
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
119+
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
120+
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
121+
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
122+
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
123+
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
124+
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
99125
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
100126
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
101127
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
@@ -104,8 +130,8 @@ golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
104130
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
105131
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
106132
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
107-
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
108-
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
133+
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
134+
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
109135
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
110136
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
111137
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=

internal/ghmcp/http.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package ghmcp
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"log/slog"
8+
"net/http"
9+
"os"
10+
"os/signal"
11+
"strings"
12+
"syscall"
13+
"time"
14+
15+
"github.com/github/github-mcp-server/pkg/github"
16+
"github.com/github/github-mcp-server/pkg/translations"
17+
)
18+
19+
type HTTPServerConfig struct {
20+
// Version of the server
21+
Version string
22+
23+
// GitHub Host to target for API requests (e.g. github.com or github.enterprise.com)
24+
Host string
25+
26+
// GitHub Token to authenticate with the GitHub API
27+
Token string
28+
29+
// EnabledToolsets is a list of toolsets to enable
30+
// See: https://github.com/github/github-mcp-server?tab=readme-ov-file#tool-configuration
31+
EnabledToolsets []string
32+
33+
// EnabledTools is a list of specific tools to enable (additive to toolsets)
34+
// When specified, these tools are registered in addition to any specified toolset tools
35+
EnabledTools []string
36+
37+
// EnabledFeatures is a list of feature flags that are enabled
38+
// Items with FeatureFlagEnable matching an entry in this list will be available
39+
EnabledFeatures []string
40+
41+
// Whether to enable dynamic toolsets
42+
// See: https://github.com/github/github-mcp-server?tab=readme-ov-file#dynamic-tool-discovery
43+
DynamicToolsets bool
44+
45+
// ReadOnly indicates if we should only register read-only tools
46+
ReadOnly bool
47+
48+
// ExportTranslations indicates if we should export translations
49+
// See: https://github.com/github/github-mcp-server?tab=readme-ov-file#i18n--overriding-descriptions
50+
ExportTranslations bool
51+
52+
// EnableCommandLogging indicates if we should log commands
53+
EnableCommandLogging bool
54+
55+
// Path to the log file if not stderr
56+
LogFilePath string
57+
58+
// Content window size
59+
ContentWindowSize int
60+
61+
// LockdownMode indicates if we should enable lockdown mode
62+
LockdownMode bool
63+
64+
// RepoAccessCacheTTL overrides the default TTL for repository access cache entries.
65+
RepoAccessCacheTTL *time.Duration
66+
67+
http.Handler
68+
}
69+
70+
func RunHTTPServer(cfg HTTPServerConfig) error {
71+
// Create app context
72+
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
73+
defer stop()
74+
75+
t, dumpTranslations := translations.TranslationHelper()
76+
77+
var slogHandler slog.Handler
78+
var logOutput io.Writer
79+
if cfg.LogFilePath != "" {
80+
file, err := os.OpenFile(cfg.LogFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
81+
if err != nil {
82+
return fmt.Errorf("failed to open log file: %w", err)
83+
}
84+
logOutput = file
85+
slogHandler = slog.NewTextHandler(logOutput, &slog.HandlerOptions{Level: slog.LevelDebug})
86+
} else {
87+
logOutput = os.Stderr
88+
slogHandler = slog.NewTextHandler(logOutput, &slog.HandlerOptions{Level: slog.LevelInfo})
89+
}
90+
logger := slog.New(slogHandler)
91+
logger.Info("starting server", "version", cfg.Version, "host", cfg.Host, "dynamicToolsets", cfg.DynamicToolsets, "readOnly", cfg.ReadOnly, "lockdownEnabled", cfg.LockdownMode)
92+
93+
// Fetch token scopes for scope-based tool filtering (PAT tokens only)
94+
// Only classic PATs (ghp_ prefix) return OAuth scopes via X-OAuth-Scopes header.
95+
// Fine-grained PATs and other token types don't support this, so we skip filtering.
96+
var tokenScopes []string
97+
if strings.HasPrefix(cfg.Token, "ghp_") {
98+
fetchedScopes, err := github.FetchTokenScopesForHost(ctx, cfg.Token, cfg.Host)
99+
if err != nil {
100+
logger.Warn("failed to fetch token scopes, continuing without scope filtering", "error", err)
101+
} else {
102+
tokenScopes = fetchedScopes
103+
logger.Info("token scopes fetched for filtering", "scopes", tokenScopes)
104+
}
105+
} else {
106+
logger.Debug("skipping scope filtering for non-PAT token")
107+
}
108+
109+
ghServer, err := github.NewMCPServer(github.MCPServerConfig{
110+
Version: cfg.Version,
111+
Host: cfg.Host,
112+
Token: cfg.Token,
113+
EnabledToolsets: cfg.EnabledToolsets,
114+
EnabledTools: cfg.EnabledTools,
115+
EnabledFeatures: cfg.EnabledFeatures,
116+
DynamicToolsets: cfg.DynamicToolsets,
117+
ReadOnly: cfg.ReadOnly,
118+
Translator: t,
119+
ContentWindowSize: cfg.ContentWindowSize,
120+
LockdownMode: cfg.LockdownMode,
121+
Logger: logger,
122+
RepoAccessTTL: cfg.RepoAccessCacheTTL,
123+
TokenScopes: tokenScopes,
124+
})
125+
if err != nil {
126+
return fmt.Errorf("failed to create MCP server: %w", err)
127+
}
128+
129+
}

0 commit comments

Comments
 (0)