@@ -16,8 +16,13 @@ import (
16
16
"github.com/google/go-github/v72/github"
17
17
"github.com/mark3labs/mcp-go/mcp"
18
18
"github.com/mark3labs/mcp-go/server"
19
+ "github.com/shurcooL/githubv4"
19
20
)
20
21
22
+ // getFileSHAFunc is a package-level variable that holds the getFileSHA function
23
+ // This allows tests to mock the behavior
24
+ var getFileSHAFunc = getFileSHA
25
+
21
26
func GetCommit (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
22
27
return mcp .NewTool ("get_commit" ,
23
28
mcp .WithDescription (t ("TOOL_GET_COMMITS_DESCRIPTION" , "Get details for a commit from a GitHub repository" )),
@@ -446,7 +451,7 @@ func CreateRepository(getClient GetClientFn, t translations.TranslationHelperFun
446
451
}
447
452
448
453
// GetFileContents creates a tool to get the contents of a file or directory from a GitHub repository.
449
- func GetFileContents (getClient GetClientFn , getRawClient raw.GetRawClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
454
+ func GetFileContents (getClient GetClientFn , getRawClient raw.GetRawClientFn , getGQLClient GetGQLClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
450
455
return mcp .NewTool ("get_file_contents" ,
451
456
mcp .WithDescription (t ("TOOL_GET_FILE_CONTENTS_DESCRIPTION" , "Get the contents of a file or directory from a GitHub repository" )),
452
457
mcp .WithToolAnnotation (mcp.ToolAnnotation {
@@ -507,15 +512,13 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t
507
512
// If the path is (most likely) not to be a directory, we will
508
513
// first try to get the raw content from the GitHub raw content API.
509
514
if path != "" && ! strings .HasSuffix (path , "/" ) {
510
- // First, get file info from Contents API to retrieve SHA
511
- var fileSHA string
512
- opts := & github.RepositoryContentGetOptions {Ref : ref }
513
- fileContent , _ , respContents , err := client .Repositories .GetContents (ctx , owner , repo , path , opts )
514
- if respContents != nil {
515
- defer func () { _ = respContents .Body .Close () }()
515
+ gqlClient , err := getGQLClient (ctx )
516
+ if err != nil {
517
+ return mcp .NewToolResultError ("failed to get GitHub GraphQL client" ), nil
516
518
}
517
- if err == nil && respContents .StatusCode == http .StatusOK && fileContent != nil && fileContent .SHA != nil {
518
- fileSHA = * fileContent .SHA
519
+ fileSHA , err := getFileSHAFunc (ctx , gqlClient , owner , repo , path , rawOpts )
520
+ if err != nil {
521
+ return mcp .NewToolResultError (fmt .Sprintf ("failed to get file SHA: %s" , err )), nil
519
522
}
520
523
521
524
rawClient , err := getRawClient (ctx )
@@ -1383,3 +1386,34 @@ func resolveGitReference(ctx context.Context, githubClient *github.Client, owner
1383
1386
// Use provided ref, or it will be empty which defaults to the default branch
1384
1387
return & raw.ContentOpts {Ref : ref , SHA : sha }, nil
1385
1388
}
1389
+
1390
+ // getFileSHA retrieves the Blob SHA of a file.
1391
+ func getFileSHA (ctx context.Context , client * githubv4.Client , owner , repo , path string , opts * raw.ContentOpts ) (string , error ) {
1392
+ var query struct {
1393
+ Repository struct {
1394
+ Object struct {
1395
+ Blob struct {
1396
+ OID string
1397
+ } `graphql:"... on Blob"`
1398
+ } `graphql:"object(expression: $expression)"`
1399
+ } `graphql:"repository(owner: $owner, name: $repo)"`
1400
+ }
1401
+
1402
+ // Prepare the expression based on the provided options
1403
+ expression := githubv4 .String (path )
1404
+ if opts != nil && opts .SHA != "" {
1405
+ expression = githubv4 .String (fmt .Sprintf ("%s:%s" , opts .SHA , path ))
1406
+ } else if opts != nil && opts .Ref != "" {
1407
+ expression = githubv4 .String (fmt .Sprintf ("%s:%s" , opts .Ref , path ))
1408
+ }
1409
+
1410
+ variables := map [string ]interface {}{
1411
+ "owner" : githubv4 .String (owner ),
1412
+ "repo" : githubv4 .String (repo ),
1413
+ "expression" : expression ,
1414
+ }
1415
+ if err := client .Query (ctx , & query , variables ); err != nil {
1416
+ return "" , fmt .Errorf ("failed to query file SHA: %w" , err )
1417
+ }
1418
+ return query .Repository .Object .Blob .OID , nil
1419
+ }
0 commit comments