@@ -10,6 +10,7 @@ import (
1010 "strings"
1111
1212 ghErrors "github.com/github/github-mcp-server/pkg/errors"
13+ "github.com/github/github-mcp-server/pkg/ifc"
1314 "github.com/github/github-mcp-server/pkg/inventory"
1415 "github.com/github/github-mcp-server/pkg/octicons"
1516 "github.com/github/github-mcp-server/pkg/scopes"
@@ -681,6 +682,17 @@ func FetchRepoCollaborators(ctx context.Context, client *github.Client, owner, r
681682 return logins , nil
682683}
683684
685+ // FetchRepoIsPrivate returns the visibility of a repository. It is a thin
686+ // wrapper around the GitHub Repositories.Get endpoint provided as a shared
687+ // helper for IFC label computation across tools.
688+ func FetchRepoIsPrivate (ctx context.Context , client * github.Client , owner , repo string ) (bool , error ) {
689+ r , _ , err := client .Repositories .Get (ctx , owner , repo )
690+ if err != nil {
691+ return false , err
692+ }
693+ return r .GetPrivate (), nil
694+ }
695+
684696// GetFileContents creates a tool to get the contents of a file or directory from a GitHub repository.
685697func GetFileContents (t translations.TranslationHelperFunc ) inventory.ServerTool {
686698 return NewTool (
@@ -753,6 +765,42 @@ func GetFileContents(t translations.TranslationHelperFunc) inventory.ServerTool
753765 return utils .NewToolResultError ("failed to get GitHub client" ), nil , nil
754766 }
755767
768+ // attachIFC adds the IFC label to a successful tool result when
769+ // InsidersMode is enabled. The visibility and (for private
770+ // repositories) collaborators lookups are performed lazily on
771+ // first use and are best-effort: if they fail we still attach
772+ // the label, falling back to the repository owner so the reader
773+ // set is never empty for a private repo.
774+ var (
775+ ifcLabelLoaded bool
776+ ifcIsPrivate bool
777+ ifcReaders []string
778+ )
779+ attachIFC := func (r * mcp.CallToolResult ) * mcp.CallToolResult {
780+ if r == nil || r .IsError || ! deps .GetFlags (ctx ).InsidersMode {
781+ return r
782+ }
783+ if ! ifcLabelLoaded {
784+ if isPrivate , err := FetchRepoIsPrivate (ctx , client , owner , repo ); err == nil {
785+ ifcIsPrivate = isPrivate
786+ }
787+ if ifcIsPrivate {
788+ if collaborators , err := FetchRepoCollaborators (ctx , client , owner , repo ); err == nil {
789+ ifcReaders = collaborators
790+ }
791+ if len (ifcReaders ) == 0 {
792+ ifcReaders = []string {owner }
793+ }
794+ }
795+ ifcLabelLoaded = true
796+ }
797+ if r .Meta == nil {
798+ r .Meta = mcp.Meta {}
799+ }
800+ r .Meta ["ifc" ] = ifc .LabelGetFileContents (ifcIsPrivate , ifcReaders )
801+ return r
802+ }
803+
756804 rawOpts , fallbackUsed , err := resolveGitReference (ctx , client , owner , repo , ref , sha )
757805 if err != nil {
758806 return utils .NewToolResultError (fmt .Sprintf ("failed to resolve git reference: %s" , err )), nil , nil
@@ -774,7 +822,8 @@ func GetFileContents(t translations.TranslationHelperFunc) inventory.ServerTool
774822 // The path does not point to a file or directory.
775823 // Instead let's try to find it in the Git Tree by matching the end of the path.
776824 if err != nil || (fileContent == nil && dirContent == nil ) {
777- return matchFiles (ctx , client , owner , repo , ref , path , rawOpts , 0 )
825+ res , data , err := matchFiles (ctx , client , owner , repo , ref , path , rawOpts , 0 )
826+ return attachIFC (res ), data , err
778827 }
779828
780829 if fileContent != nil && fileContent .SHA != nil {
@@ -804,7 +853,7 @@ func GetFileContents(t translations.TranslationHelperFunc) inventory.ServerTool
804853 Text : "" ,
805854 MIMEType : "text/plain" ,
806855 }
807- return utils .NewToolResultResource (fmt .Sprintf ("successfully downloaded empty file (SHA: %s)%s" , fileSHA , successNote ), result ), nil , nil
856+ return attachIFC ( utils .NewToolResultResource (fmt .Sprintf ("successfully downloaded empty file (SHA: %s)%s" , fileSHA , successNote ), result ) ), nil , nil
808857 }
809858
810859 // For files >= 1MB, return a ResourceLink instead of content
@@ -817,10 +866,10 @@ func GetFileContents(t translations.TranslationHelperFunc) inventory.ServerTool
817866 Title : fmt .Sprintf ("File: %s" , path ),
818867 Size : & size ,
819868 }
820- return utils .NewToolResultResourceLink (
869+ return attachIFC ( utils .NewToolResultResourceLink (
821870 fmt .Sprintf ("File %s is too large to display (%d bytes). Use the download URL to fetch the content: %s (SHA: %s)%s" ,
822871 path , fileSize , fileContent .GetDownloadURL (), fileSHA , successNote ),
823- resourceLink ), nil , nil
872+ resourceLink )) , nil , nil
824873 }
825874
826875 // For files < 1MB, get content directly from Contents API
@@ -848,7 +897,7 @@ func GetFileContents(t translations.TranslationHelperFunc) inventory.ServerTool
848897 Text : content ,
849898 MIMEType : contentType ,
850899 }
851- return utils .NewToolResultResource (fmt .Sprintf ("successfully downloaded text file (SHA: %s)%s" , fileSHA , successNote ), result ), nil , nil
900+ return attachIFC ( utils .NewToolResultResource (fmt .Sprintf ("successfully downloaded text file (SHA: %s)%s" , fileSHA , successNote ), result ) ), nil , nil
852901 }
853902
854903 // Binary content - encode as base64 blob
@@ -858,14 +907,14 @@ func GetFileContents(t translations.TranslationHelperFunc) inventory.ServerTool
858907 Blob : []byte (blobContent ),
859908 MIMEType : contentType ,
860909 }
861- return utils .NewToolResultResource (fmt .Sprintf ("successfully downloaded binary file (SHA: %s)%s" , fileSHA , successNote ), result ), nil , nil
910+ return attachIFC ( utils .NewToolResultResource (fmt .Sprintf ("successfully downloaded binary file (SHA: %s)%s" , fileSHA , successNote ), result ) ), nil , nil
862911 } else if dirContent != nil {
863912 // file content or file SHA is nil which means it's a directory
864913 r , err := json .Marshal (dirContent )
865914 if err != nil {
866915 return utils .NewToolResultError ("failed to marshal response" ), nil , nil
867916 }
868- return utils .NewToolResultText (string (r )), nil , nil
917+ return attachIFC ( utils .NewToolResultText (string (r ) )), nil , nil
869918 }
870919
871920 return utils .NewToolResultError ("failed to get file contents" ), nil , nil
0 commit comments