Skip to content

Commit 2f4e96e

Browse files
committed
fix(get_file_contents): keep modern-protocol results under 1 MiB
1 parent 99d6d65 commit 2f4e96e

2 files changed

Lines changed: 52 additions & 2 deletions

File tree

‎pkg/github/repositories.go‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"strings"
1313
"time"
1414

15+
ghcontext "github.com/github/github-mcp-server/v2/pkg/context"
1516
ghErrors "github.com/github/github-mcp-server/v2/pkg/errors"
1617
"github.com/github/github-mcp-server/v2/pkg/ifc"
1718
"github.com/github/github-mcp-server/v2/pkg/inventory"
@@ -1103,6 +1104,16 @@ func GetFileContents(t translations.TranslationHelperFunc) inventory.ServerTool
11031104
}
11041105
const maxContentSize = 1024 * 1024 // 1MB
11051106

1107+
// From protocol 2026-07-28 on, the MCP SDK mirrors the file body in
1108+
// structuredContent, so an inlined file takes roughly twice its size
1109+
// in the encoded result. Halve the inline budget there so a file that
1110+
// fits the old limit does not push the whole result past common 1 MiB
1111+
// transport limits.
1112+
inlineContentLimit := maxContentSize
1113+
if info, ok := ghcontext.MCPMethod(ctx); ok && info.ProtocolVersion >= inventory.ProtocolVersionMultiRoundTrip {
1114+
inlineContentLimit = maxContentSize / 2
1115+
}
1116+
11061117
read, respInspect, err := inspectRepositoryFile(ctx, client, owner, repo, ref, path, fileContent)
11071118
if err != nil {
11081119
if respInspect != nil {
@@ -1134,7 +1145,7 @@ func GetFileContents(t translations.TranslationHelperFunc) inventory.ServerTool
11341145
}
11351146

11361147
// For files >= 1MB, return a ResourceLink instead of content
1137-
if fileSize >= maxContentSize {
1148+
if fileSize >= inlineContentLimit {
11381149
size := int64(fileSize)
11391150
resourceLink := &mcp.ResourceLink{
11401151
URI: resourceURI,

‎pkg/github/repositories_test.go‎

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/github/github-mcp-server/v2/internal/githubv4mock"
1414
"github.com/github/github-mcp-server/v2/internal/requeststate"
1515
"github.com/github/github-mcp-server/v2/internal/toolsnaps"
16+
ghcontext "github.com/github/github-mcp-server/v2/pkg/context"
1617
"github.com/github/github-mcp-server/v2/pkg/inventory"
1718
"github.com/github/github-mcp-server/v2/pkg/raw"
1819
"github.com/github/github-mcp-server/v2/pkg/translations"
@@ -40,6 +41,7 @@ type repositoryReadFixture struct {
4041
blobs map[string][]byte
4142
trees map[string]*github.Tree
4243
inspect func(*http.Request)
44+
ctx context.Context
4345
}
4446

4547
func runRepositoryReadFixture(t *testing.T, fixture repositoryReadFixture, args map[string]any) (*mcp.CallToolResult, int) {
@@ -73,7 +75,11 @@ func runRepositoryReadFixture(t *testing.T, fixture repositoryReadFixture, args
7375
tool := GetFileContents(translations.NullTranslationHelper)
7476
handler := tool.Handler(deps)
7577
request := createMCPRequest(args)
76-
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
78+
ctx := fixture.ctx
79+
if ctx == nil {
80+
ctx = context.Background()
81+
}
82+
result, err := handler(ContextWithDeps(ctx, deps), &request)
7783
require.NoError(t, err)
7884
return result, counter.count
7985
}
@@ -772,6 +778,39 @@ func Test_GetFileContents_SymlinkDisclosure(t *testing.T) {
772778
})
773779
}
774780

781+
func Test_GetFileContents_ModernProtocolInlineLimit(t *testing.T) {
782+
commitSHA := strings.Repeat("c", 40)
783+
content := strings.Repeat("a", 600*1024)
784+
encoded := base64.StdEncoding.EncodeToString([]byte(content))
785+
fileSHA := gitBlobSHA([]byte(content))
786+
args := map[string]any{"owner": "owner", "repo": "repo", "path": "medium.txt", "sha": commitSHA}
787+
788+
fixture := repositoryReadFixture{
789+
contents: &github.RepositoryContent{
790+
Type: new("file"), Path: new("medium.txt"), SHA: new(fileSHA), Size: new(len(content)),
791+
Content: new(encoded), Encoding: new("base64"),
792+
},
793+
}
794+
795+
t.Run("legacy protocol inlines the file", func(t *testing.T) {
796+
result, _ := runRepositoryReadFixture(t, fixture, args)
797+
require.False(t, result.IsError)
798+
_, ok := result.Content[1].(*mcp.EmbeddedResource)
799+
require.True(t, ok, "expected an inline resource on the legacy protocol")
800+
})
801+
802+
t.Run("modern protocol returns a ResourceLink", func(t *testing.T) {
803+
modern := fixture
804+
modern.ctx = ghcontext.WithMCPMethodInfo(context.Background(), &ghcontext.MCPMethodInfo{
805+
ProtocolVersion: inventory.ProtocolVersionMultiRoundTrip,
806+
})
807+
result, _ := runRepositoryReadFixture(t, modern, args)
808+
require.False(t, result.IsError)
809+
_, ok := result.Content[1].(*mcp.ResourceLink)
810+
require.True(t, ok, "expected a ResourceLink on the modern protocol")
811+
})
812+
}
813+
775814
func Test_GetFileContents_ContentlessRequestCounts(t *testing.T) {
776815
commitSHA := strings.Repeat("c", 40)
777816
fileSHA := strings.Repeat("a", 40)

0 commit comments

Comments
 (0)