diff --git a/pkg/github/pullrequests.go b/pkg/github/pullrequests.go index ae7d04331..32d0a0605 100644 --- a/pkg/github/pullrequests.go +++ b/pkg/github/pullrequests.go @@ -383,7 +383,45 @@ func GetPullRequestFiles(ctx context.Context, client *github.Client, owner, repo minimalFiles := convertToMinimalPRFiles(files) - return MarshalledTextResult(minimalFiles), nil + type fileMeta struct { + Filename string `json:"filename"` + Status string `json:"status,omitempty"` + Additions int `json:"additions,omitempty"` + Deletions int `json:"deletions,omitempty"` + Changes int `json:"changes,omitempty"` + PreviousFilename string `json:"previous_filename,omitempty"` + } + + metas := make([]fileMeta, len(minimalFiles)) + for i, f := range minimalFiles { + metas[i] = fileMeta{ + Filename: f.Filename, + Status: f.Status, + Additions: f.Additions, + Deletions: f.Deletions, + Changes: f.Changes, + PreviousFilename: f.PreviousFilename, + } + } + + metaJSON, err := json.Marshal(metas) + if err != nil { + return nil, fmt.Errorf("failed to marshal file metadata: %w", err) + } + + contents := []mcp.Content{ + &mcp.TextContent{Text: string(metaJSON)}, + } + + for _, f := range minimalFiles { + if f.Patch != "" { + contents = append(contents, &mcp.TextContent{ + Text: fmt.Sprintf("filename: %s\n%s", f.Filename, f.Patch), + }) + } + } + + return &mcp.CallToolResult{Content: contents}, nil } func GetPullRequestCommits(ctx context.Context, client *github.Client, owner, repo string, pullNumber int, pagination PaginationParams) (*mcp.CallToolResult, error) { diff --git a/pkg/github/pullrequests_test.go b/pkg/github/pullrequests_test.go index 2b911636a..26da8ef51 100644 --- a/pkg/github/pullrequests_test.go +++ b/pkg/github/pullrequests_test.go @@ -12,6 +12,7 @@ import ( "github.com/github/github-mcp-server/pkg/translations" "github.com/google/go-github/v87/github" "github.com/google/jsonschema-go/jsonschema" + "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -1242,20 +1243,45 @@ func Test_GetPullRequestFiles(t *testing.T) { require.NoError(t, err) require.False(t, result.IsError) - // Parse the result and get the text content if no error - textContent := getTextResult(t, result) + // Verify multi-content response + assert.NotNil(t, result) + require.Len(t, result.Content, 3) // 1 metadata + 2 patches - // Unmarshal and verify the result - var returnedFiles []MinimalPRFile - err = json.Unmarshal([]byte(textContent.Text), &returnedFiles) + // Verify Content[0] is metadata JSON + metaContent, ok := result.Content[0].(*mcp.TextContent) + require.True(t, ok, "expected first content to be TextContent") + + var returnedMetas []map[string]any + err = json.Unmarshal([]byte(metaContent.Text), &returnedMetas) require.NoError(t, err) - assert.Len(t, returnedFiles, len(tc.expectedFiles)) - for i, file := range returnedFiles { - assert.Equal(t, tc.expectedFiles[i].GetFilename(), file.Filename) - assert.Equal(t, tc.expectedFiles[i].GetStatus(), file.Status) - assert.Equal(t, tc.expectedFiles[i].GetAdditions(), file.Additions) - assert.Equal(t, tc.expectedFiles[i].GetDeletions(), file.Deletions) - } + assert.Len(t, returnedMetas, len(tc.expectedFiles)) + + // Verify metadata content for first file + assert.Equal(t, "file1.go", returnedMetas[0]["filename"]) + assert.Equal(t, "modified", returnedMetas[0]["status"]) + assert.Equal(t, float64(10), returnedMetas[0]["additions"]) + assert.Equal(t, float64(5), returnedMetas[0]["deletions"]) + assert.Equal(t, float64(15), returnedMetas[0]["changes"]) + assert.Nil(t, returnedMetas[0]["patch"], "metadata should not contain patch field") + + // Verify metadata content for second file + assert.Equal(t, "file2.go", returnedMetas[1]["filename"]) + assert.Equal(t, "added", returnedMetas[1]["status"]) + assert.Equal(t, float64(20), returnedMetas[1]["additions"]) + assert.Equal(t, float64(20), returnedMetas[1]["changes"]) + assert.Nil(t, returnedMetas[1]["patch"], "metadata should not contain patch field") + + // Verify Content[1] is patch for file1.go + patch1Content, ok := result.Content[1].(*mcp.TextContent) + require.True(t, ok, "expected second content to be TextContent") + assert.Contains(t, patch1Content.Text, "filename: file1.go") + assert.Contains(t, patch1Content.Text, "@@ -1,5 +1,10 @@") + + // Verify Content[2] is patch for file2.go + patch2Content, ok := result.Content[2].(*mcp.TextContent) + require.True(t, ok, "expected third content to be TextContent") + assert.Contains(t, patch2Content.Text, "filename: file2.go") + assert.Contains(t, patch2Content.Text, "@@ -0,0 +1,20 @@") }) } }