diff --git a/pkg/github/issues.go b/pkg/github/issues.go index f1de84eb27..3cd86c3d76 100644 --- a/pkg/github/issues.go +++ b/pkg/github/issues.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "net/http" + "net/url" "strconv" "strings" "time" @@ -1002,6 +1003,39 @@ func isSafeRefContent(ctx context.Context, cache *lockdown.RepoAccessCache, repo return safe } +type issueCommentMinimized struct { + Reason string `json:"reason"` +} + +type issueCommentResponse struct { + github.IssueComment + Minimized *issueCommentMinimized `json:"minimized,omitempty"` +} + +func listIssueComments(ctx context.Context, client *github.Client, owner string, repo string, issueNumber int, pagination PaginationParams) ([]issueCommentResponse, *github.Response, error) { + query := url.Values{} + if pagination.Page > 0 { + query.Set("page", strconv.Itoa(pagination.Page)) + } + if pagination.PerPage > 0 { + query.Set("per_page", strconv.Itoa(pagination.PerPage)) + } + + apiURL := fmt.Sprintf("repos/%s/%s/issues/%d/comments", owner, repo, issueNumber) + if encoded := query.Encode(); encoded != "" { + apiURL += "?" + encoded + } + req, err := client.NewRequest(ctx, http.MethodGet, apiURL, nil) + if err != nil { + return nil, nil, err + } + req.Header.Set("Accept", "application/vnd.github+json") + + var comments []issueCommentResponse + resp, err := client.Do(req, &comments) + return comments, resp, err +} + func GetIssueComments(ctx context.Context, client *github.Client, deps ToolDependencies, owner string, repo string, issueNumber int, pagination PaginationParams) (*mcp.CallToolResult, error) { cache, err := deps.GetRepoAccessCache(ctx) if err != nil { @@ -1009,14 +1043,7 @@ func GetIssueComments(ctx context.Context, client *github.Client, deps ToolDepen } flags := deps.GetFlags(ctx) - opts := &github.IssueListCommentsOptions{ - ListOptions: github.ListOptions{ - Page: pagination.Page, - PerPage: pagination.PerPage, - }, - } - - comments, resp, err := client.Issues.ListComments(ctx, owner, repo, issueNumber, opts) + comments, resp, err := listIssueComments(ctx, client, owner, repo, issueNumber, pagination) if err != nil { return nil, fmt.Errorf("failed to get issue comments: %w", err) } @@ -1033,7 +1060,7 @@ func GetIssueComments(ctx context.Context, client *github.Client, deps ToolDepen if cache == nil { return nil, fmt.Errorf("lockdown cache is not configured") } - filteredComments := make([]*github.IssueComment, 0, len(comments)) + filteredComments := make([]issueCommentResponse, 0, len(comments)) for _, comment := range comments { user := comment.User if user == nil { @@ -1056,7 +1083,7 @@ func GetIssueComments(ctx context.Context, client *github.Client, deps ToolDepen minimalComments := make([]MinimalIssueComment, 0, len(comments)) for _, comment := range comments { - minimalComments = append(minimalComments, convertToMinimalIssueComment(comment)) + minimalComments = append(minimalComments, convertToMinimalIssueCommentWithMinimized(&comment.IssueComment, comment.Minimized)) } return MarshalledTextResult(minimalComments), nil diff --git a/pkg/github/issues_test.go b/pkg/github/issues_test.go index 7138148c21..2b5577cd7a 100644 --- a/pkg/github/issues_test.go +++ b/pkg/github/issues_test.go @@ -5483,6 +5483,43 @@ func Test_GetIssueComments(t *testing.T) { } } +func TestGetIssueCommentsIncludesMinimizedReason(t *testing.T) { + serverTool := IssueRead(translations.NullTranslationHelper) + client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposIssuesCommentsByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, []map[string]any{ + { + "id": 123, + "body": "This comment is marked as spam", + "user": map[string]any{"login": "user1"}, + "minimized": map[string]any{ + "reason": "spam", + }, + }, + }), + })) + deps := BaseDeps{ + Client: client, + RepoAccessCache: stubRepoAccessCache(nil, 15*time.Minute), + Flags: stubFeatureFlags(nil), + } + handler := serverTool.Handler(deps) + request := createMCPRequest(map[string]any{ + "method": "get_comments", + "owner": "owner", + "repo": "repo", + "issue_number": float64(42), + }) + + result, err := handler(ContextWithDeps(context.Background(), deps), &request) + require.NoError(t, err) + require.False(t, result.IsError) + + var comments []MinimalIssueComment + require.NoError(t, json.Unmarshal([]byte(getTextResult(t, result).Text), &comments)) + require.Len(t, comments, 1) + assert.Equal(t, "spam", comments[0].MinimizedReason) +} + func Test_GetIssueLabels(t *testing.T) { t.Parallel() diff --git a/pkg/github/minimal_types.go b/pkg/github/minimal_types.go index 2eba9a1628..464f47fa81 100644 --- a/pkg/github/minimal_types.go +++ b/pkg/github/minimal_types.go @@ -675,6 +675,7 @@ type MinimalIssueComment struct { User *MinimalUser `json:"user,omitempty"` AuthorAssociation string `json:"author_association,omitempty"` Reactions *MinimalReactions `json:"reactions,omitempty"` + MinimizedReason string `json:"minimized_reason,omitempty"` CreatedAt string `json:"created_at,omitempty"` UpdatedAt string `json:"updated_at,omitempty"` } @@ -1013,6 +1014,10 @@ func convertToMinimalIssuesResponseWithoutFieldValues(fragment issueQueryFragmen } func convertToMinimalIssueComment(comment *github.IssueComment) MinimalIssueComment { + return convertToMinimalIssueCommentWithMinimized(comment, nil) +} + +func convertToMinimalIssueCommentWithMinimized(comment *github.IssueComment, minimized *issueCommentMinimized) MinimalIssueComment { m := MinimalIssueComment{ ID: comment.GetID(), Body: sanitize.Content(comment.GetBody()), @@ -1020,6 +1025,9 @@ func convertToMinimalIssueComment(comment *github.IssueComment) MinimalIssueComm User: convertToMinimalUser(comment.GetUser()), AuthorAssociation: comment.GetAuthorAssociation(), } + if minimized != nil { + m.MinimizedReason = minimized.Reason + } if comment.CreatedAt != nil { m.CreatedAt = comment.CreatedAt.Format(time.RFC3339)