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

Commita6fb6ea

Browse files
committed
Add cache
1 parent2f7f3ab commita6fb6ea

File tree

11 files changed

+496
-91
lines changed

11 files changed

+496
-91
lines changed

‎cmd/github-mcp-server/generate_docs.go‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111

1212
"github.com/github/github-mcp-server/pkg/github"
13+
"github.com/github/github-mcp-server/pkg/lockdown"
1314
"github.com/github/github-mcp-server/pkg/raw"
1415
"github.com/github/github-mcp-server/pkg/toolsets"
1516
"github.com/github/github-mcp-server/pkg/translations"
@@ -64,7 +65,8 @@ func generateReadmeDocs(readmePath string) error {
6465
t,_:=translations.TranslationHelper()
6566

6667
// Create toolset group with mock clients
67-
tsg:=github.DefaultToolsetGroup(false,mockGetClient,mockGetGQLClient,mockGetRawClient,t,5000, github.FeatureFlags{})
68+
repoAccessCache:=lockdown.NewRepoAccessCache(nil)
69+
tsg:=github.DefaultToolsetGroup(false,mockGetClient,mockGetGQLClient,mockGetRawClient,t,5000, github.FeatureFlags{},repoAccessCache)
6870

6971
// Generate toolsets documentation
7072
toolsetsDoc:=generateToolsetsDoc(tsg)
@@ -302,7 +304,8 @@ func generateRemoteToolsetsDoc() string {
302304
t,_:=translations.TranslationHelper()
303305

304306
// Create toolset group with mock clients
305-
tsg:=github.DefaultToolsetGroup(false,mockGetClient,mockGetGQLClient,mockGetRawClient,t,5000, github.FeatureFlags{})
307+
repoAccessCache:=lockdown.NewRepoAccessCache(nil)
308+
tsg:=github.DefaultToolsetGroup(false,mockGetClient,mockGetGQLClient,mockGetRawClient,t,5000, github.FeatureFlags{},repoAccessCache)
306309

307310
// Generate table header
308311
buf.WriteString("| Name | Description | API URL | 1-Click Install (VS Code) | Read-only Link | 1-Click Read-only Install (VS Code) |\n")

‎cmd/github-mcp-server/main.go‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77
"strings"
8+
"time"
89

910
"github.com/github/github-mcp-server/internal/ghmcp"
1011
"github.com/github/github-mcp-server/pkg/github"
@@ -50,6 +51,7 @@ var (
5051
enabledToolsets= []string{github.ToolsetMetadataDefault.ID}
5152
}
5253

54+
ttl:=viper.GetDuration("repo-access-cache-ttl")
5355
stdioServerConfig:= ghmcp.StdioServerConfig{
5456
Version:version,
5557
Host:viper.GetString("host"),
@@ -62,6 +64,7 @@ var (
6264
LogFilePath:viper.GetString("log-file"),
6365
ContentWindowSize:viper.GetInt("content-window-size"),
6466
LockdownMode:viper.GetBool("lockdown-mode"),
67+
RepoAccessCacheTTL:&ttl,
6568
}
6669
returnghmcp.RunStdioServer(stdioServerConfig)
6770
},
@@ -84,6 +87,7 @@ func init() {
8487
rootCmd.PersistentFlags().String("gh-host","","Specify the GitHub hostname (for GitHub Enterprise etc.)")
8588
rootCmd.PersistentFlags().Int("content-window-size",5000,"Specify the content window size")
8689
rootCmd.PersistentFlags().Bool("lockdown-mode",false,"Enable lockdown mode")
90+
rootCmd.PersistentFlags().Duration("repo-access-cache-ttl",5*time.Minute,"Override the repo access cache TTL (e.g. 1m, 0s to disable)")
8791

8892
// Bind flag to viper
8993
_=viper.BindPFlag("toolsets",rootCmd.PersistentFlags().Lookup("toolsets"))
@@ -95,6 +99,7 @@ func init() {
9599
_=viper.BindPFlag("host",rootCmd.PersistentFlags().Lookup("gh-host"))
96100
_=viper.BindPFlag("content-window-size",rootCmd.PersistentFlags().Lookup("content-window-size"))
97101
_=viper.BindPFlag("lockdown-mode",rootCmd.PersistentFlags().Lookup("lockdown-mode"))
102+
_=viper.BindPFlag("repo-access-cache-ttl",rootCmd.PersistentFlags().Lookup("repo-access-cache-ttl"))
98103

99104
// Add subcommands
100105
rootCmd.AddCommand(stdioCmd)

‎internal/ghmcp/server.go‎

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
"github.com/github/github-mcp-server/pkg/errors"
1818
"github.com/github/github-mcp-server/pkg/github"
19+
"github.com/github/github-mcp-server/pkg/lockdown"
1920
mcplog"github.com/github/github-mcp-server/pkg/log"
2021
"github.com/github/github-mcp-server/pkg/raw"
2122
"github.com/github/github-mcp-server/pkg/translations"
@@ -54,6 +55,9 @@ type MCPServerConfig struct {
5455

5556
// LockdownMode indicates if we should enable lockdown mode
5657
LockdownModebool
58+
59+
// RepoAccessTTL overrides the default TTL for repository access cache entries.
60+
RepoAccessTTL*time.Duration
5761
}
5862

5963
conststdioServerLogPrefix="stdioserver"
@@ -80,6 +84,14 @@ func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) {
8084
},
8185
}// We're going to wrap the Transport later in beforeInit
8286
gqlClient:=githubv4.NewEnterpriseClient(apiHost.graphqlURL.String(),gqlHTTPClient)
87+
repoAccessOpts:= []lockdown.RepoAccessOption{}
88+
ifcfg.RepoAccessTTL!=nil {
89+
repoAccessOpts=append(repoAccessOpts,lockdown.WithTTL(*cfg.RepoAccessTTL))
90+
}
91+
varrepoAccessCache*lockdown.RepoAccessCache
92+
ifcfg.LockdownMode {
93+
repoAccessCache=lockdown.NewRepoAccessCache(gqlClient,repoAccessOpts...)
94+
}
8395

8496
// When a client send an initialize request, update the user agent to include the client info.
8597
beforeInit:=func(_ context.Context,_any,message*mcp.InitializeRequest) {
@@ -165,6 +177,7 @@ func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) {
165177
cfg.Translator,
166178
cfg.ContentWindowSize,
167179
github.FeatureFlags{LockdownMode:cfg.LockdownMode},
180+
repoAccessCache,
168181
)
169182
err=tsg.EnableToolsets(enabledToolsets,nil)
170183

@@ -219,6 +232,9 @@ type StdioServerConfig struct {
219232

220233
// LockdownMode indicates if we should enable lockdown mode
221234
LockdownModebool
235+
236+
// RepoAccessCacheTTL overrides the default TTL for repository access cache entries.
237+
RepoAccessCacheTTL*time.Duration
222238
}
223239

224240
// RunStdioServer is not concurrent safe.
@@ -229,23 +245,6 @@ func RunStdioServer(cfg StdioServerConfig) error {
229245

230246
t,dumpTranslations:=translations.TranslationHelper()
231247

232-
ghServer,err:=NewMCPServer(MCPServerConfig{
233-
Version:cfg.Version,
234-
Host:cfg.Host,
235-
Token:cfg.Token,
236-
EnabledToolsets:cfg.EnabledToolsets,
237-
DynamicToolsets:cfg.DynamicToolsets,
238-
ReadOnly:cfg.ReadOnly,
239-
Translator:t,
240-
ContentWindowSize:cfg.ContentWindowSize,
241-
LockdownMode:cfg.LockdownMode,
242-
})
243-
iferr!=nil {
244-
returnfmt.Errorf("failed to create MCP server: %w",err)
245-
}
246-
247-
stdioServer:=server.NewStdioServer(ghServer)
248-
249248
varslogHandler slog.Handler
250249
varlogOutput io.Writer
251250
ifcfg.LogFilePath!="" {
@@ -262,6 +261,24 @@ func RunStdioServer(cfg StdioServerConfig) error {
262261
logger:=slog.New(slogHandler)
263262
logger.Info("starting server","version",cfg.Version,"host",cfg.Host,"dynamicToolsets",cfg.DynamicToolsets,"readOnly",cfg.ReadOnly,"lockdownEnabled",cfg.LockdownMode)
264263
stdLogger:=log.New(logOutput,stdioServerLogPrefix,0)
264+
265+
ghServer,err:=NewMCPServer(MCPServerConfig{
266+
Version:cfg.Version,
267+
Host:cfg.Host,
268+
Token:cfg.Token,
269+
EnabledToolsets:cfg.EnabledToolsets,
270+
DynamicToolsets:cfg.DynamicToolsets,
271+
ReadOnly:cfg.ReadOnly,
272+
Translator:t,
273+
ContentWindowSize:cfg.ContentWindowSize,
274+
LockdownMode:cfg.LockdownMode,
275+
RepoAccessTTL:cfg.RepoAccessCacheTTL,
276+
})
277+
iferr!=nil {
278+
returnfmt.Errorf("failed to create MCP server: %w",err)
279+
}
280+
281+
stdioServer:=server.NewStdioServer(ghServer)
265282
stdioServer.SetErrorLogger(stdLogger)
266283

267284
ifcfg.ExportTranslations {

‎pkg/github/issues.go‎

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ func fragmentToIssue(fragment IssueFragment) *github.Issue {
228228
}
229229

230230
// GetIssue creates a tool to get details of a specific issue in a GitHub repository.
231-
funcIssueRead(getClientGetClientFn,getGQLClientGetGQLClientFn,t translations.TranslationHelperFunc,flagsFeatureFlags) (tool mcp.Tool,handler server.ToolHandlerFunc) {
231+
funcIssueRead(getClientGetClientFn,getGQLClientGetGQLClientFn,cache*lockdown.RepoAccessCache,t translations.TranslationHelperFunc,flagsFeatureFlags) (tool mcp.Tool,handler server.ToolHandlerFunc) {
232232
returnmcp.NewTool("issue_read",
233233
mcp.WithDescription(t("TOOL_ISSUE_READ_DESCRIPTION","Get information about a specific issue in a GitHub repository.")),
234234
mcp.WithToolAnnotation(mcp.ToolAnnotation{
@@ -297,11 +297,11 @@ Options are:
297297

298298
switchmethod {
299299
case"get":
300-
returnGetIssue(ctx,client,gqlClient,owner,repo,issueNumber,flags)
300+
returnGetIssue(ctx,client,cache,owner,repo,issueNumber,flags)
301301
case"get_comments":
302-
returnGetIssueComments(ctx,client,gqlClient,owner,repo,issueNumber,pagination,flags)
302+
returnGetIssueComments(ctx,client,cache,owner,repo,issueNumber,pagination,flags)
303303
case"get_sub_issues":
304-
returnGetSubIssues(ctx,client,gqlClient,owner,repo,issueNumber,pagination,flags)
304+
returnGetSubIssues(ctx,client,cache,owner,repo,issueNumber,pagination,flags)
305305
case"get_labels":
306306
returnGetIssueLabels(ctx,gqlClient,owner,repo,issueNumber)
307307
default:
@@ -310,7 +310,7 @@ Options are:
310310
}
311311
}
312312

313-
funcGetIssue(ctx context.Context,client*github.Client,gqlClient*githubv4.Client,ownerstring,repostring,issueNumberint,flagsFeatureFlags) (*mcp.CallToolResult,error) {
313+
funcGetIssue(ctx context.Context,client*github.Client,cache*lockdown.RepoAccessCache,ownerstring,repostring,issueNumberint,flagsFeatureFlags) (*mcp.CallToolResult,error) {
314314
issue,resp,err:=client.Issues.Get(ctx,owner,repo,issueNumber)
315315
iferr!=nil {
316316
returnnil,fmt.Errorf("failed to get issue: %w",err)
@@ -326,8 +326,12 @@ func GetIssue(ctx context.Context, client *github.Client, gqlClient *githubv4.Cl
326326
}
327327

328328
ifflags.LockdownMode {
329-
ifissue.User!=nil {
330-
isPrivate,hasPushAccess,err:=lockdown.GetRepoAccessInfo(ctx,gqlClient,*issue.User.Login,owner,repo)
329+
ifcache==nil {
330+
returnnil,fmt.Errorf("lockdown cache is not configured")
331+
}
332+
login:=issue.GetUser().GetLogin()
333+
iflogin!="" {
334+
isPrivate,hasPushAccess,err:=cache.GetRepoAccessInfo(ctx,login,owner,repo)
331335
iferr!=nil {
332336
returnmcp.NewToolResultError(fmt.Sprintf("failed to check lockdown mode: %v",err)),nil
333337
}
@@ -355,7 +359,7 @@ func GetIssue(ctx context.Context, client *github.Client, gqlClient *githubv4.Cl
355359
returnmcp.NewToolResultText(string(r)),nil
356360
}
357361

358-
funcGetIssueComments(ctx context.Context,client*github.Client,gqlClient*githubv4.Client,ownerstring,repostring,issueNumberint,paginationPaginationParams,flagsFeatureFlags) (*mcp.CallToolResult,error) {
362+
funcGetIssueComments(ctx context.Context,client*github.Client,cache*lockdown.RepoAccessCache,ownerstring,repostring,issueNumberint,paginationPaginationParams,flagsFeatureFlags) (*mcp.CallToolResult,error) {
359363
opts:=&github.IssueListCommentsOptions{
360364
ListOptions: github.ListOptions{
361365
Page:pagination.Page,
@@ -377,9 +381,20 @@ func GetIssueComments(ctx context.Context, client *github.Client, gqlClient *git
377381
returnmcp.NewToolResultError(fmt.Sprintf("failed to get issue comments: %s",string(body))),nil
378382
}
379383
ifflags.LockdownMode {
380-
filteredComments:= []*github.IssueComment{}
384+
ifcache==nil {
385+
returnnil,fmt.Errorf("lockdown cache is not configured")
386+
}
387+
filteredComments:=make([]*github.IssueComment,0,len(comments))
381388
for_,comment:=rangecomments {
382-
isPrivate,hasPushAccess,err:=lockdown.GetRepoAccessInfo(ctx,gqlClient,*comment.User.Login,owner,repo)
389+
user:=comment.User
390+
ifuser==nil {
391+
continue
392+
}
393+
login:=user.GetLogin()
394+
iflogin=="" {
395+
continue
396+
}
397+
isPrivate,hasPushAccess,err:=cache.GetRepoAccessInfo(ctx,login,owner,repo)
383398
iferr!=nil {
384399
returnmcp.NewToolResultError(fmt.Sprintf("failed to check lockdown mode: %v",err)),nil
385400
}
@@ -402,7 +417,7 @@ func GetIssueComments(ctx context.Context, client *github.Client, gqlClient *git
402417
returnmcp.NewToolResultText(string(r)),nil
403418
}
404419

405-
funcGetSubIssues(ctx context.Context,client*github.Client,gqlClient*githubv4.Client,ownerstring,repostring,issueNumberint,paginationPaginationParams,featureFlagsFeatureFlags) (*mcp.CallToolResult,error) {
420+
funcGetSubIssues(ctx context.Context,client*github.Client,cache*lockdown.RepoAccessCache,ownerstring,repostring,issueNumberint,paginationPaginationParams,featureFlagsFeatureFlags) (*mcp.CallToolResult,error) {
406421
opts:=&github.IssueListOptions{
407422
ListOptions: github.ListOptions{
408423
Page:pagination.Page,
@@ -430,9 +445,20 @@ func GetSubIssues(ctx context.Context, client *github.Client, gqlClient *githubv
430445
}
431446

432447
iffeatureFlags.LockdownMode {
433-
filteredSubIssues:= []*github.SubIssue{}
448+
ifcache==nil {
449+
returnnil,fmt.Errorf("lockdown cache is not configured")
450+
}
451+
filteredSubIssues:=make([]*github.SubIssue,0,len(subIssues))
434452
for_,subIssue:=rangesubIssues {
435-
isPrivate,hasPushAccess,err:=lockdown.GetRepoAccessInfo(ctx,gqlClient,*subIssue.User.Login,owner,repo)
453+
user:=subIssue.User
454+
ifuser==nil {
455+
continue
456+
}
457+
login:=user.GetLogin()
458+
iflogin=="" {
459+
continue
460+
}
461+
isPrivate,hasPushAccess,err:=cache.GetRepoAccessInfo(ctx,login,owner,repo)
436462
iferr!=nil {
437463
returnmcp.NewToolResultError(fmt.Sprintf("failed to check lockdown mode: %v",err)),nil
438464
}

‎pkg/github/issues_test.go‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func Test_GetIssue(t *testing.T) {
2323
// Verify tool definition once
2424
mockClient:=github.NewClient(nil)
2525
defaultGQLClient:=githubv4.NewClient(nil)
26-
tool,_:=IssueRead(stubGetClientFn(mockClient),stubGetGQLClientFn(defaultGQLClient),translations.NullTranslationHelper,stubFeatureFlags(map[string]bool{"lockdown-mode":false}))
26+
tool,_:=IssueRead(stubGetClientFn(mockClient),stubGetGQLClientFn(defaultGQLClient),stubRepoAccessCache(defaultGQLClient),translations.NullTranslationHelper,stubFeatureFlags(map[string]bool{"lockdown-mode":false}))
2727
require.NoError(t,toolsnaps.Test(tool.Name,tool))
2828

2929
assert.Equal(t,"issue_read",tool.Name)
@@ -212,7 +212,7 @@ func Test_GetIssue(t *testing.T) {
212212
}
213213

214214
flags:=stubFeatureFlags(map[string]bool{"lockdown-mode":tc.lockdownEnabled})
215-
_,handler:=IssueRead(stubGetClientFn(client),stubGetGQLClientFn(gqlClient),translations.NullTranslationHelper,flags)
215+
_,handler:=IssueRead(stubGetClientFn(client),stubGetGQLClientFn(gqlClient),stubRepoAccessCache(gqlClient),translations.NullTranslationHelper,flags)
216216

217217
request:=createMCPRequest(tc.requestArgs)
218218
result,err:=handler(context.Background(),request)
@@ -1710,7 +1710,7 @@ func Test_GetIssueComments(t *testing.T) {
17101710
// Verify tool definition once
17111711
mockClient:=github.NewClient(nil)
17121712
gqlClient:=githubv4.NewClient(nil)
1713-
tool,_:=IssueRead(stubGetClientFn(mockClient),stubGetGQLClientFn(gqlClient),translations.NullTranslationHelper,stubFeatureFlags(map[string]bool{"lockdown-mode":false}))
1713+
tool,_:=IssueRead(stubGetClientFn(mockClient),stubGetGQLClientFn(gqlClient),stubRepoAccessCache(gqlClient),translations.NullTranslationHelper,stubFeatureFlags(map[string]bool{"lockdown-mode":false}))
17141714
require.NoError(t,toolsnaps.Test(tool.Name,tool))
17151715

17161716
assert.Equal(t,"issue_read",tool.Name)
@@ -1816,7 +1816,7 @@ func Test_GetIssueComments(t *testing.T) {
18161816
// Setup client with mock
18171817
client:=github.NewClient(tc.mockedClient)
18181818
gqlClient:=githubv4.NewClient(nil)
1819-
_,handler:=IssueRead(stubGetClientFn(client),stubGetGQLClientFn(gqlClient),translations.NullTranslationHelper,stubFeatureFlags(map[string]bool{"lockdown-mode":false}))
1819+
_,handler:=IssueRead(stubGetClientFn(client),stubGetGQLClientFn(gqlClient),stubRepoAccessCache(gqlClient),translations.NullTranslationHelper,stubFeatureFlags(map[string]bool{"lockdown-mode":false}))
18201820

18211821
// Create call request
18221822
request:=createMCPRequest(tc.requestArgs)
@@ -1853,7 +1853,7 @@ func Test_GetIssueLabels(t *testing.T) {
18531853
// Verify tool definition
18541854
mockGQClient:=githubv4.NewClient(nil)
18551855
mockClient:=github.NewClient(nil)
1856-
tool,_:=IssueRead(stubGetClientFn(mockClient),stubGetGQLClientFn(mockGQClient),translations.NullTranslationHelper,stubFeatureFlags(map[string]bool{"lockdown-mode":false}))
1856+
tool,_:=IssueRead(stubGetClientFn(mockClient),stubGetGQLClientFn(mockGQClient),stubRepoAccessCache(mockGQClient),translations.NullTranslationHelper,stubFeatureFlags(map[string]bool{"lockdown-mode":false}))
18571857
require.NoError(t,toolsnaps.Test(tool.Name,tool))
18581858

18591859
assert.Equal(t,"issue_read",tool.Name)
@@ -1928,7 +1928,7 @@ func Test_GetIssueLabels(t *testing.T) {
19281928
t.Run(tc.name,func(t*testing.T) {
19291929
gqlClient:=githubv4.NewClient(tc.mockedClient)
19301930
client:=github.NewClient(nil)
1931-
_,handler:=IssueRead(stubGetClientFn(client),stubGetGQLClientFn(gqlClient),translations.NullTranslationHelper,stubFeatureFlags(map[string]bool{"lockdown-mode":false}))
1931+
_,handler:=IssueRead(stubGetClientFn(client),stubGetGQLClientFn(gqlClient),stubRepoAccessCache(gqlClient),translations.NullTranslationHelper,stubFeatureFlags(map[string]bool{"lockdown-mode":false}))
19321932

19331933
request:=createMCPRequest(tc.requestArgs)
19341934
result,err:=handler(context.Background(),request)
@@ -2619,7 +2619,7 @@ func Test_GetSubIssues(t *testing.T) {
26192619
// Verify tool definition once
26202620
mockClient:=github.NewClient(nil)
26212621
gqlClient:=githubv4.NewClient(nil)
2622-
tool,_:=IssueRead(stubGetClientFn(mockClient),stubGetGQLClientFn(gqlClient),translations.NullTranslationHelper,stubFeatureFlags(map[string]bool{"lockdown-mode":false}))
2622+
tool,_:=IssueRead(stubGetClientFn(mockClient),stubGetGQLClientFn(gqlClient),stubRepoAccessCache(gqlClient),translations.NullTranslationHelper,stubFeatureFlags(map[string]bool{"lockdown-mode":false}))
26232623
require.NoError(t,toolsnaps.Test(tool.Name,tool))
26242624

26252625
assert.Equal(t,"issue_read",tool.Name)
@@ -2816,7 +2816,7 @@ func Test_GetSubIssues(t *testing.T) {
28162816
// Setup client with mock
28172817
client:=github.NewClient(tc.mockedClient)
28182818
gqlClient:=githubv4.NewClient(nil)
2819-
_,handler:=IssueRead(stubGetClientFn(client),stubGetGQLClientFn(gqlClient),translations.NullTranslationHelper,stubFeatureFlags(map[string]bool{"lockdown-mode":false}))
2819+
_,handler:=IssueRead(stubGetClientFn(client),stubGetGQLClientFn(gqlClient),stubRepoAccessCache(gqlClient),translations.NullTranslationHelper,stubFeatureFlags(map[string]bool{"lockdown-mode":false}))
28202820

28212821
// Create call request
28222822
request:=createMCPRequest(tc.requestArgs)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp