From d879c6b5f9604812d7f5c3e9329dadac418e4e69 Mon Sep 17 00:00:00 2001 From: tommaso-moro Date: Mon, 16 Feb 2026 19:25:33 +0000 Subject: [PATCH 1/3] introduce minimal pr type --- pkg/github/minimal_types.go | 130 ++++++++++++++++++++++++++++++++ pkg/github/pullrequests.go | 18 ++--- pkg/github/pullrequests_test.go | 28 +++---- 3 files changed, 153 insertions(+), 23 deletions(-) diff --git a/pkg/github/minimal_types.go b/pkg/github/minimal_types.go index a33bcec7a..1e1ec7260 100644 --- a/pkg/github/minimal_types.go +++ b/pkg/github/minimal_types.go @@ -134,8 +134,138 @@ type MinimalProject struct { OwnerType string `json:"owner_type,omitempty"` } +// MinimalPullRequest is the trimmed output type for pull request objects to reduce verbosity. +type MinimalPullRequest struct { + Number int `json:"number"` + Title string `json:"title"` + Body string `json:"body,omitempty"` + State string `json:"state"` + Draft bool `json:"draft"` + Merged bool `json:"merged"` + MergeableState string `json:"mergeable_state,omitempty"` + HTMLURL string `json:"html_url"` + User *MinimalUser `json:"user,omitempty"` + Labels []string `json:"labels,omitempty"` + Assignees []string `json:"assignees,omitempty"` + RequestedReviewers []string `json:"requested_reviewers,omitempty"` + MergedBy string `json:"merged_by,omitempty"` + Head *MinimalPRBranch `json:"head,omitempty"` + Base *MinimalPRBranch `json:"base,omitempty"` + Additions int `json:"additions,omitempty"` + Deletions int `json:"deletions,omitempty"` + ChangedFiles int `json:"changed_files,omitempty"` + Commits int `json:"commits,omitempty"` + Comments int `json:"comments,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + ClosedAt string `json:"closed_at,omitempty"` + MergedAt string `json:"merged_at,omitempty"` + Milestone string `json:"milestone,omitempty"` +} + +// MinimalPRBranch is the trimmed output type for pull request branch references. +type MinimalPRBranch struct { + Ref string `json:"ref"` + SHA string `json:"sha"` + Repo *MinimalPRBranchRepo `json:"repo,omitempty"` +} + +// MinimalPRBranchRepo is the trimmed repo info nested inside a PR branch. +type MinimalPRBranchRepo struct { + FullName string `json:"full_name"` + Description string `json:"description,omitempty"` +} + // Helper functions +func convertToMinimalPullRequest(pr *github.PullRequest) MinimalPullRequest { + m := MinimalPullRequest{ + Number: pr.GetNumber(), + Title: pr.GetTitle(), + Body: pr.GetBody(), + State: pr.GetState(), + Draft: pr.GetDraft(), + Merged: pr.GetMerged(), + MergeableState: pr.GetMergeableState(), + HTMLURL: pr.GetHTMLURL(), + User: convertToMinimalUser(pr.GetUser()), + Additions: pr.GetAdditions(), + Deletions: pr.GetDeletions(), + ChangedFiles: pr.GetChangedFiles(), + Commits: pr.GetCommits(), + Comments: pr.GetComments(), + } + + if pr.CreatedAt != nil { + m.CreatedAt = pr.CreatedAt.Format("2006-01-02T15:04:05Z") + } + if pr.UpdatedAt != nil { + m.UpdatedAt = pr.UpdatedAt.Format("2006-01-02T15:04:05Z") + } + if pr.ClosedAt != nil { + m.ClosedAt = pr.ClosedAt.Format("2006-01-02T15:04:05Z") + } + if pr.MergedAt != nil { + m.MergedAt = pr.MergedAt.Format("2006-01-02T15:04:05Z") + } + + for _, label := range pr.Labels { + if label != nil { + m.Labels = append(m.Labels, label.GetName()) + } + } + + for _, assignee := range pr.Assignees { + if assignee != nil { + m.Assignees = append(m.Assignees, assignee.GetLogin()) + } + } + + for _, reviewer := range pr.RequestedReviewers { + if reviewer != nil { + m.RequestedReviewers = append(m.RequestedReviewers, reviewer.GetLogin()) + } + } + + if mergedBy := pr.GetMergedBy(); mergedBy != nil { + m.MergedBy = mergedBy.GetLogin() + } + + if head := pr.Head; head != nil { + m.Head = convertToMinimalPRBranch(head) + } + + if base := pr.Base; base != nil { + m.Base = convertToMinimalPRBranch(base) + } + + if milestone := pr.GetMilestone(); milestone != nil { + m.Milestone = milestone.GetTitle() + } + + return m +} + +func convertToMinimalPRBranch(branch *github.PullRequestBranch) *MinimalPRBranch { + if branch == nil { + return nil + } + + b := &MinimalPRBranch{ + Ref: branch.GetRef(), + SHA: branch.GetSHA(), + } + + if repo := branch.GetRepo(); repo != nil { + b.Repo = &MinimalPRBranchRepo{ + FullName: repo.GetFullName(), + Description: repo.GetDescription(), + } + } + + return b +} + func convertToMinimalProject(fullProject *github.ProjectV2) *MinimalProject { if fullProject == nil { return nil diff --git a/pkg/github/pullrequests.go b/pkg/github/pullrequests.go index 1043870f1..838734976 100644 --- a/pkg/github/pullrequests.go +++ b/pkg/github/pullrequests.go @@ -186,12 +186,9 @@ func GetPullRequest(ctx context.Context, client *github.Client, deps ToolDepende } } - r, err := json.Marshal(pr) - if err != nil { - return nil, fmt.Errorf("failed to marshal response: %w", err) - } + minimalPR := convertToMinimalPullRequest(pr) - return utils.NewToolResultText(string(r)), nil + return MarshalledTextResult(minimalPR), nil } func GetPullRequestDiff(ctx context.Context, client *github.Client, owner, repo string, pullNumber int) (*mcp.CallToolResult, error) { @@ -1171,12 +1168,15 @@ func ListPullRequests(t translations.TranslationHelperFunc) inventory.ServerTool } } - r, err := json.Marshal(prs) - if err != nil { - return utils.NewToolResultErrorFromErr("failed to marshal response", err), nil, nil + minimalPRs := make([]MinimalPullRequest, 0, len(prs)) + for _, pr := range prs { + if pr == nil { + continue + } + minimalPRs = append(minimalPRs, convertToMinimalPullRequest(pr)) } - return utils.NewToolResultText(string(r)), nil, nil + return MarshalledTextResult(minimalPRs), nil, nil }) } diff --git a/pkg/github/pullrequests_test.go b/pkg/github/pullrequests_test.go index 52dbb74a0..63562d1f0 100644 --- a/pkg/github/pullrequests_test.go +++ b/pkg/github/pullrequests_test.go @@ -127,14 +127,14 @@ func Test_GetPullRequest(t *testing.T) { // Parse the result and get the text content if no error textContent := getTextResult(t, result) - // Unmarshal and verify the result - var returnedPR github.PullRequest + // Unmarshal and verify the minimal result + var returnedPR MinimalPullRequest err = json.Unmarshal([]byte(textContent.Text), &returnedPR) require.NoError(t, err) - assert.Equal(t, *tc.expectedPR.Number, *returnedPR.Number) - assert.Equal(t, *tc.expectedPR.Title, *returnedPR.Title) - assert.Equal(t, *tc.expectedPR.State, *returnedPR.State) - assert.Equal(t, *tc.expectedPR.HTMLURL, *returnedPR.HTMLURL) + assert.Equal(t, tc.expectedPR.GetNumber(), returnedPR.Number) + assert.Equal(t, tc.expectedPR.GetTitle(), returnedPR.Title) + assert.Equal(t, tc.expectedPR.GetState(), returnedPR.State) + assert.Equal(t, tc.expectedPR.GetHTMLURL(), returnedPR.HTMLURL) }) } } @@ -670,17 +670,17 @@ func Test_ListPullRequests(t *testing.T) { // Parse the result and get the text content if no error textContent := getTextResult(t, result) - // Unmarshal and verify the result - var returnedPRs []*github.PullRequest + // Unmarshal and verify the minimal result + var returnedPRs []MinimalPullRequest err = json.Unmarshal([]byte(textContent.Text), &returnedPRs) require.NoError(t, err) assert.Len(t, returnedPRs, 2) - assert.Equal(t, *tc.expectedPRs[0].Number, *returnedPRs[0].Number) - assert.Equal(t, *tc.expectedPRs[0].Title, *returnedPRs[0].Title) - assert.Equal(t, *tc.expectedPRs[0].State, *returnedPRs[0].State) - assert.Equal(t, *tc.expectedPRs[1].Number, *returnedPRs[1].Number) - assert.Equal(t, *tc.expectedPRs[1].Title, *returnedPRs[1].Title) - assert.Equal(t, *tc.expectedPRs[1].State, *returnedPRs[1].State) + assert.Equal(t, tc.expectedPRs[0].GetNumber(), returnedPRs[0].Number) + assert.Equal(t, tc.expectedPRs[0].GetTitle(), returnedPRs[0].Title) + assert.Equal(t, tc.expectedPRs[0].GetState(), returnedPRs[0].State) + assert.Equal(t, tc.expectedPRs[1].GetNumber(), returnedPRs[1].Number) + assert.Equal(t, tc.expectedPRs[1].GetTitle(), returnedPRs[1].Title) + assert.Equal(t, tc.expectedPRs[1].GetState(), returnedPRs[1].State) }) } } From 3ce66658deeea701b5c1c658845afdfff0ff9a1b Mon Sep 17 00:00:00 2001 From: tommaso-moro Date: Mon, 16 Feb 2026 19:35:48 +0000 Subject: [PATCH 2/3] update to use time.RFC3339 --- pkg/github/minimal_types.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/github/minimal_types.go b/pkg/github/minimal_types.go index 1e1ec7260..f1dcfe06e 100644 --- a/pkg/github/minimal_types.go +++ b/pkg/github/minimal_types.go @@ -1,6 +1,8 @@ package github import ( + "time" + "github.com/google/go-github/v82/github" ) @@ -197,16 +199,16 @@ func convertToMinimalPullRequest(pr *github.PullRequest) MinimalPullRequest { } if pr.CreatedAt != nil { - m.CreatedAt = pr.CreatedAt.Format("2006-01-02T15:04:05Z") + m.CreatedAt = pr.CreatedAt.Format(time.RFC3339) } if pr.UpdatedAt != nil { - m.UpdatedAt = pr.UpdatedAt.Format("2006-01-02T15:04:05Z") + m.UpdatedAt = pr.UpdatedAt.Format(time.RFC3339) } if pr.ClosedAt != nil { - m.ClosedAt = pr.ClosedAt.Format("2006-01-02T15:04:05Z") + m.ClosedAt = pr.ClosedAt.Format(time.RFC3339) } if pr.MergedAt != nil { - m.MergedAt = pr.MergedAt.Format("2006-01-02T15:04:05Z") + m.MergedAt = pr.MergedAt.Format(time.RFC3339) } for _, label := range pr.Labels { From 3f0fa1d61d594852911677e95b59f6c61c2453b4 Mon Sep 17 00:00:00 2001 From: tommaso-moro Date: Mon, 16 Feb 2026 19:51:35 +0000 Subject: [PATCH 3/3] confine change to single PR --- pkg/github/pullrequests.go | 11 ++++------- pkg/github/pullrequests_test.go | 16 ++++++++-------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/pkg/github/pullrequests.go b/pkg/github/pullrequests.go index 838734976..58edc07dc 100644 --- a/pkg/github/pullrequests.go +++ b/pkg/github/pullrequests.go @@ -1168,15 +1168,12 @@ func ListPullRequests(t translations.TranslationHelperFunc) inventory.ServerTool } } - minimalPRs := make([]MinimalPullRequest, 0, len(prs)) - for _, pr := range prs { - if pr == nil { - continue - } - minimalPRs = append(minimalPRs, convertToMinimalPullRequest(pr)) + r, err := json.Marshal(prs) + if err != nil { + return utils.NewToolResultErrorFromErr("failed to marshal response", err), nil, nil } - return MarshalledTextResult(minimalPRs), nil, nil + return utils.NewToolResultText(string(r)), nil, nil }) } diff --git a/pkg/github/pullrequests_test.go b/pkg/github/pullrequests_test.go index 63562d1f0..570b1906f 100644 --- a/pkg/github/pullrequests_test.go +++ b/pkg/github/pullrequests_test.go @@ -670,17 +670,17 @@ func Test_ListPullRequests(t *testing.T) { // Parse the result and get the text content if no error textContent := getTextResult(t, result) - // Unmarshal and verify the minimal result - var returnedPRs []MinimalPullRequest + // Unmarshal and verify the result + var returnedPRs []*github.PullRequest err = json.Unmarshal([]byte(textContent.Text), &returnedPRs) require.NoError(t, err) assert.Len(t, returnedPRs, 2) - assert.Equal(t, tc.expectedPRs[0].GetNumber(), returnedPRs[0].Number) - assert.Equal(t, tc.expectedPRs[0].GetTitle(), returnedPRs[0].Title) - assert.Equal(t, tc.expectedPRs[0].GetState(), returnedPRs[0].State) - assert.Equal(t, tc.expectedPRs[1].GetNumber(), returnedPRs[1].Number) - assert.Equal(t, tc.expectedPRs[1].GetTitle(), returnedPRs[1].Title) - assert.Equal(t, tc.expectedPRs[1].GetState(), returnedPRs[1].State) + assert.Equal(t, *tc.expectedPRs[0].Number, *returnedPRs[0].Number) + assert.Equal(t, *tc.expectedPRs[0].Title, *returnedPRs[0].Title) + assert.Equal(t, *tc.expectedPRs[0].State, *returnedPRs[0].State) + assert.Equal(t, *tc.expectedPRs[1].Number, *returnedPRs[1].Number) + assert.Equal(t, *tc.expectedPRs[1].Title, *returnedPRs[1].Title) + assert.Equal(t, *tc.expectedPRs[1].State, *returnedPRs[1].State) }) } }