Skip to content

Commit 7bfe9ae

Browse files
Copilottimrogers
andauthored
Fix granular review findings
Co-authored-by: timrogers <116134+timrogers@users.noreply.github.com>
1 parent 143f3f4 commit 7bfe9ae

7 files changed

Lines changed: 169 additions & 37 deletions
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"annotations": {
3+
"idempotentHint": false,
4+
"readOnlyHint": false,
5+
"title": "Add comment to issue"
6+
},
7+
"description": "Add a comment and/or reaction to a specific issue or issue comment in a GitHub repository. Only works on issues; for pull requests use add_pull_request_comment. At least one of body or reaction is required.",
8+
"inputSchema": {
9+
"properties": {
10+
"body": {
11+
"description": "Comment content. Required unless reaction is provided.",
12+
"minLength": 1,
13+
"type": "string"
14+
},
15+
"comment_id": {
16+
"description": "The numeric ID of the issue comment to react to. Use this for reactions to comments; omit it to react to the issue itself. Cannot be combined with body.",
17+
"minimum": 1,
18+
"type": "integer"
19+
},
20+
"issue_number": {
21+
"description": "Issue number to comment on or react to.",
22+
"type": "number"
23+
},
24+
"owner": {
25+
"description": "Repository owner",
26+
"type": "string"
27+
},
28+
"reaction": {
29+
"description": "Emoji reaction to add. Required unless body is provided.",
30+
"enum": [
31+
"+1",
32+
"-1",
33+
"laugh",
34+
"confused",
35+
"heart",
36+
"hooray",
37+
"rocket",
38+
"eyes"
39+
],
40+
"type": "string"
41+
},
42+
"repo": {
43+
"description": "Repository name",
44+
"type": "string"
45+
}
46+
},
47+
"required": [
48+
"owner",
49+
"repo",
50+
"issue_number"
51+
],
52+
"type": "object"
53+
},
54+
"name": "add_issue_comment"
55+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"annotations": {
3+
"idempotentHint": false,
4+
"readOnlyHint": false,
5+
"title": "Update issue comment"
6+
},
7+
"description": "Update the body of an existing issue comment. Only works on issue comments; for pull request conversation comments use update_pull_request_comment.",
8+
"inputSchema": {
9+
"properties": {
10+
"body": {
11+
"description": "New comment content",
12+
"minLength": 1,
13+
"type": "string"
14+
},
15+
"comment_id": {
16+
"description": "The numeric ID of the issue comment to update.",
17+
"minimum": 1,
18+
"type": "integer"
19+
},
20+
"owner": {
21+
"description": "Repository owner",
22+
"type": "string"
23+
},
24+
"repo": {
25+
"description": "Repository name",
26+
"type": "string"
27+
}
28+
},
29+
"required": [
30+
"owner",
31+
"repo",
32+
"comment_id",
33+
"body"
34+
],
35+
"type": "object"
36+
},
37+
"name": "update_issue_comment"
38+
}

‎pkg/github/granular_tools_test.go‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ func TestGranularToolSnaps(t *testing.T) {
8888
require.NoError(t, toolsnaps.Test(serverTool.Tool.Name, serverTool.Tool))
8989
})
9090
}
91+
92+
for _, constructor := range []func(translations.TranslationHelperFunc) inventory.ServerTool{
93+
GranularAddIssueComment,
94+
GranularUpdateIssueComment,
95+
} {
96+
serverTool := constructor(translations.NullTranslationHelper)
97+
t.Run(serverTool.Tool.Name+"_ff_"+FeatureFlagIssuesGranular, func(t *testing.T) {
98+
require.NoError(t, toolsnaps.Test(serverTool.Tool.Name+"_ff_"+FeatureFlagIssuesGranular, serverTool.Tool))
99+
})
100+
}
91101
}
92102

93103
func TestIssuesGranularToolset(t *testing.T) {

‎pkg/github/issues.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,7 +1491,7 @@ func addIssueComment(t translations.TranslationHelperFunc, issueOnly bool) inven
14911491
if issueOnly {
14921492
// A comment_id is validated against issue_number below, so checking
14931493
// issue_number also covers reactions to comments.
1494-
if result := EnsureIssue(ctx, client, owner, repo, issueNumber); result != nil {
1494+
if result := EnsureIssue(ctx, client, owner, repo, issueNumber, "add_issue_comment"); result != nil {
14951495
return result, nil, nil
14961496
}
14971497
}
@@ -1676,7 +1676,7 @@ func updateIssueComment(t translations.TranslationHelperFunc, issueOnly bool) in
16761676
}
16771677

16781678
if issueOnly {
1679-
if result := EnsureIssueComment(ctx, client, owner, repo, commentID); result != nil {
1679+
if result := EnsureIssueComment(ctx, client, owner, repo, commentID, "update_issue_comment"); result != nil {
16801680
return result, nil, nil
16811681
}
16821682
}

‎pkg/github/issues_granular.go‎

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,33 +27,43 @@ func normalizeConfidence(confidence string) string {
2727
// rather than an issue. GitHub's issues API accepts pull request numbers, so tools
2828
// that should only act on issues must check this explicitly. A nil result means
2929
// the number refers to an issue.
30-
func EnsureIssue(ctx context.Context, client *github.Client, owner, repo string, issueNumber int) *mcp.CallToolResult {
31-
return ensureIssueKind(ctx, client, owner, repo, issueNumber, false)
30+
func EnsureIssue(ctx context.Context, client *github.Client, owner, repo string, issueNumber int, companionTool ...string) *mcp.CallToolResult {
31+
return ensureIssueKind(ctx, client, owner, repo, issueNumber, false, firstOrDefault(companionTool, "the pull request tools"))
3232
}
3333

3434
// EnsurePullRequest returns an error result if the given number refers to an issue
3535
// rather than a pull request. A nil result means the number refers to a pull request.
36-
func EnsurePullRequest(ctx context.Context, client *github.Client, owner, repo string, pullNumber int) *mcp.CallToolResult {
37-
return ensureIssueKind(ctx, client, owner, repo, pullNumber, true)
36+
func EnsurePullRequest(ctx context.Context, client *github.Client, owner, repo string, pullNumber int, companionTool ...string) *mcp.CallToolResult {
37+
return ensureIssueKind(ctx, client, owner, repo, pullNumber, true, firstOrDefault(companionTool, "the issue tools"))
3838
}
3939

4040
// EnsureIssueComment returns an error result if the given conversation comment
4141
// belongs to a pull request rather than an issue. A nil result means the comment
4242
// belongs to an issue.
43-
func EnsureIssueComment(ctx context.Context, client *github.Client, owner, repo string, commentID int64) *mcp.CallToolResult {
44-
return ensureCommentKind(ctx, client, owner, repo, commentID, false)
43+
func EnsureIssueComment(ctx context.Context, client *github.Client, owner, repo string, commentID int64, companionTool ...string) *mcp.CallToolResult {
44+
return ensureCommentKind(ctx, client, owner, repo, commentID, false, firstOrDefault(companionTool, "the pull request comment tools"))
4545
}
4646

4747
// EnsurePullRequestComment returns an error result if the given conversation
4848
// comment belongs to an issue rather than a pull request. A nil result means the
4949
// comment belongs to a pull request.
50-
func EnsurePullRequestComment(ctx context.Context, client *github.Client, owner, repo string, commentID int64) *mcp.CallToolResult {
51-
return ensureCommentKind(ctx, client, owner, repo, commentID, true)
50+
func EnsurePullRequestComment(ctx context.Context, client *github.Client, owner, repo string, commentID int64, companionTool ...string) *mcp.CallToolResult {
51+
return ensureCommentKind(ctx, client, owner, repo, commentID, true, firstOrDefault(companionTool, "the issue comment tools"))
5252
}
5353

54-
func ensureCommentKind(ctx context.Context, client *github.Client, owner, repo string, commentID int64, wantPullRequest bool) *mcp.CallToolResult {
54+
func firstOrDefault(values []string, fallback string) string {
55+
if len(values) > 0 {
56+
return values[0]
57+
}
58+
return fallback
59+
}
60+
61+
func ensureCommentKind(ctx context.Context, client *github.Client, owner, repo string, commentID int64, wantPullRequest bool, companionTool string) *mcp.CallToolResult {
5562
comment, resp, err := client.Issues.GetComment(ctx, owner, repo, commentID)
5663
if err != nil {
64+
if resp != nil && resp.Body != nil {
65+
_ = resp.Body.Close()
66+
}
5767
return ghErrors.NewGitHubAPIErrorResponse(ctx, fmt.Sprintf("failed to get comment %d", commentID), resp, err)
5868
}
5969
_ = resp.Body.Close()
@@ -62,22 +72,25 @@ func ensureCommentKind(ctx context.Context, client *github.Client, owner, repo s
6272
if err != nil {
6373
return utils.NewToolResultErrorFromErr(fmt.Sprintf("failed to determine the issue or pull request for comment %d", commentID), err)
6474
}
65-
return ensureIssueKind(ctx, client, owner, repo, number, wantPullRequest)
75+
return ensureIssueKind(ctx, client, owner, repo, number, wantPullRequest, companionTool)
6676
}
6777

68-
func ensureIssueKind(ctx context.Context, client *github.Client, owner, repo string, number int, wantPullRequest bool) *mcp.CallToolResult {
78+
func ensureIssueKind(ctx context.Context, client *github.Client, owner, repo string, number int, wantPullRequest bool, companionTool string) *mcp.CallToolResult {
6979
issue, resp, err := client.Issues.Get(ctx, owner, repo, number)
7080
if err != nil {
81+
if resp != nil && resp.Body != nil {
82+
_ = resp.Body.Close()
83+
}
7184
return ghErrors.NewGitHubAPIErrorResponse(ctx, fmt.Sprintf("failed to get #%d", number), resp, err)
7285
}
7386
_ = resp.Body.Close()
7487

7588
isPullRequest := issue.IsPullRequest()
7689
switch {
7790
case isPullRequest && !wantPullRequest:
78-
return utils.NewToolResultError(fmt.Sprintf("#%d in %s/%s is a pull request, not an issue. Use the pull request tools to modify it.", number, owner, repo))
91+
return utils.NewToolResultError(fmt.Sprintf("#%d in %s/%s is a pull request, not an issue. Use %s to modify it.", number, owner, repo, companionTool))
7992
case !isPullRequest && wantPullRequest:
80-
return utils.NewToolResultError(fmt.Sprintf("#%d in %s/%s is an issue, not a pull request. Use the issue tools to modify it.", number, owner, repo))
93+
return utils.NewToolResultError(fmt.Sprintf("#%d in %s/%s is an issue, not a pull request. Use %s to modify it.", number, owner, repo, companionTool))
8194
}
8295
return nil
8396
}
@@ -151,7 +164,7 @@ func issueUpdateTool(
151164
return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), nil, nil
152165
}
153166

154-
if result := EnsureIssue(ctx, client, owner, repo, issueNumber); result != nil {
167+
if result := EnsureIssue(ctx, client, owner, repo, issueNumber, strings.Replace(name, "update_issue_", "update_pull_request_", 1)); result != nil {
155168
return result, nil, nil
156169
}
157170

@@ -488,7 +501,7 @@ func GranularUpdateIssueAssignees(t translations.TranslationHelperFunc) inventor
488501
return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), nil, nil
489502
}
490503

491-
if result := EnsureIssue(ctx, client, owner, repo, issueNumber); result != nil {
504+
if result := EnsureIssue(ctx, client, owner, repo, issueNumber, "update_issue_assignees"); result != nil {
492505
return result, nil, nil
493506
}
494507

@@ -710,7 +723,7 @@ func GranularUpdateIssueLabels(t translations.TranslationHelperFunc) inventory.S
710723
return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), nil, nil
711724
}
712725

713-
if result := EnsureIssue(ctx, client, owner, repo, issueNumber); result != nil {
726+
if result := EnsureIssue(ctx, client, owner, repo, issueNumber, "update_issue_labels"); result != nil {
714727
return result, nil, nil
715728
}
716729

@@ -897,7 +910,7 @@ func GranularUpdateIssueType(t translations.TranslationHelperFunc) inventory.Ser
897910
return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), nil, nil
898911
}
899912

900-
if result := EnsureIssue(ctx, client, owner, repo, issueNumber); result != nil {
913+
if result := EnsureIssue(ctx, client, owner, repo, issueNumber, "update_issue_type"); result != nil {
901914
return result, nil, nil
902915
}
903916

@@ -1087,7 +1100,7 @@ func GranularUpdateIssueState(t translations.TranslationHelperFunc) inventory.Se
10871100
return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), nil, nil
10881101
}
10891102

1090-
if result := EnsureIssue(ctx, client, owner, repo, issueNumber); result != nil {
1103+
if result := EnsureIssue(ctx, client, owner, repo, issueNumber, "update_issue_state"); result != nil {
10911104
return result, nil, nil
10921105
}
10931106

@@ -1725,7 +1738,7 @@ func GranularAddIssueReaction(t translations.TranslationHelperFunc) inventory.Se
17251738
return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), nil, nil
17261739
}
17271740

1728-
if result := EnsureIssue(ctx, client, owner, repo, issueNumber); result != nil {
1741+
if result := EnsureIssue(ctx, client, owner, repo, issueNumber, "add_issue_reaction"); result != nil {
17291742
return result, nil, nil
17301743
}
17311744

@@ -1811,7 +1824,7 @@ func GranularRemoveIssueReaction(t translations.TranslationHelperFunc) inventory
18111824
return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), nil, nil
18121825
}
18131826

1814-
if result := EnsureIssue(ctx, client, owner, repo, issueNumber); result != nil {
1827+
if result := EnsureIssue(ctx, client, owner, repo, issueNumber, "remove_issue_reaction"); result != nil {
18151828
return result, nil, nil
18161829
}
18171830

@@ -1892,7 +1905,7 @@ func GranularAddIssueCommentReaction(t translations.TranslationHelperFunc) inven
18921905
return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), nil, nil
18931906
}
18941907

1895-
if result := EnsureIssueComment(ctx, client, owner, repo, commentID); result != nil {
1908+
if result := EnsureIssueComment(ctx, client, owner, repo, commentID, "add_issue_comment_reaction"); result != nil {
18961909
return result, nil, nil
18971910
}
18981911

@@ -1978,7 +1991,7 @@ func GranularRemoveIssueCommentReaction(t translations.TranslationHelperFunc) in
19781991
return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), nil, nil
19791992
}
19801993

1981-
if result := EnsureIssueComment(ctx, client, owner, repo, commentID); result != nil {
1994+
if result := EnsureIssueComment(ctx, client, owner, repo, commentID, "remove_issue_comment_reaction"); result != nil {
19821995
return result, nil, nil
19831996
}
19841997

0 commit comments

Comments
 (0)