Skip to content

Commit 7c53cde

Browse files
Support issue comment reactions in add_issue_comment
Add optional comment_id support so the default add_issue_comment tool can react to a specific issue or pull request comment without exposing a separate default reaction tool. Keep body creation tied to issue_number and require reaction targets to provide either issue_number or comment_id. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent e6276cf commit 7c53cde

4 files changed

Lines changed: 90 additions & 23 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,8 @@ The following sets of tools are available:
842842
- **add_issue_comment** - Add comment to issue or pull request
843843
- **Required OAuth Scopes**: `repo`
844844
- `body`: Comment content. Required unless reaction is provided. (string, optional)
845-
- `issue_number`: Issue number to comment on or react to (number, required)
845+
- `comment_id`: The numeric ID of the issue or pull request comment to react to. Use this for reactions to comments; omit it to react to the issue or pull request itself. (number, optional)
846+
- `issue_number`: Issue or pull request number to comment on or react to. Required when body is provided, or when reaction targets an issue or pull request. (number, optional)
846847
- `owner`: Repository owner (string, required)
847848
- `reaction`: Emoji reaction to add. Required unless body is provided. (string, optional)
848849
- `repo`: Repository name (string, required)

pkg/github/__toolsnaps__/add_issue_comment.snap

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,20 @@
22
"annotations": {
33
"title": "Add comment to issue or pull request"
44
},
5-
"description": "Add a comment and/or reaction to a specific issue in a GitHub repository. Use this tool with pull requests as well (in this case pass pull request number as issue_number), but only if user is not asking specifically to add or react to review comments. At least one of body or reaction is required.",
5+
"description": "Add a comment and/or reaction to a specific issue or issue comment in a GitHub repository. Use this tool with pull requests as well (in this case pass pull request number as issue_number), but only if user is not asking specifically to add or react to review comments. At least one of body or reaction is required.",
66
"inputSchema": {
77
"properties": {
88
"body": {
99
"description": "Comment content. Required unless reaction is provided.",
1010
"type": "string"
1111
},
12+
"comment_id": {
13+
"description": "The numeric ID of the issue or pull request comment to react to. Use this for reactions to comments; omit it to react to the issue or pull request itself.",
14+
"minimum": 1,
15+
"type": "number"
16+
},
1217
"issue_number": {
13-
"description": "Issue number to comment on or react to",
18+
"description": "Issue or pull request number to comment on or react to. Required when body is provided, or when reaction targets an issue or pull request.",
1419
"type": "number"
1520
},
1621
"owner": {
@@ -38,8 +43,7 @@
3843
},
3944
"required": [
4045
"owner",
41-
"repo",
42-
"issue_number"
46+
"repo"
4347
],
4448
"type": "object"
4549
},

pkg/github/issues.go

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,7 +1057,7 @@ func AddIssueComment(t translations.TranslationHelperFunc) inventory.ServerTool
10571057
ToolsetMetadataIssues,
10581058
mcp.Tool{
10591059
Name: "add_issue_comment",
1060-
Description: t("TOOL_ADD_ISSUE_COMMENT_DESCRIPTION", "Add a comment and/or reaction to a specific issue in a GitHub repository. Use this tool with pull requests as well (in this case pass pull request number as issue_number), but only if user is not asking specifically to add or react to review comments. At least one of body or reaction is required."),
1060+
Description: t("TOOL_ADD_ISSUE_COMMENT_DESCRIPTION", "Add a comment and/or reaction to a specific issue or issue comment in a GitHub repository. Use this tool with pull requests as well (in this case pass pull request number as issue_number), but only if user is not asking specifically to add or react to review comments. At least one of body or reaction is required."),
10611061
Annotations: &mcp.ToolAnnotations{
10621062
Title: t("TOOL_ADD_ISSUE_COMMENT_USER_TITLE", "Add comment to issue or pull request"),
10631063
ReadOnlyHint: false,
@@ -1075,7 +1075,12 @@ func AddIssueComment(t translations.TranslationHelperFunc) inventory.ServerTool
10751075
},
10761076
"issue_number": {
10771077
Type: "number",
1078-
Description: "Issue number to comment on or react to",
1078+
Description: "Issue or pull request number to comment on or react to. Required when body is provided, or when reaction targets an issue or pull request.",
1079+
},
1080+
"comment_id": {
1081+
Type: "number",
1082+
Description: "The numeric ID of the issue or pull request comment to react to. Use this for reactions to comments; omit it to react to the issue or pull request itself.",
1083+
Minimum: jsonschema.Ptr(1.0),
10791084
},
10801085
"body": {
10811086
Type: "string",
@@ -1087,7 +1092,7 @@ func AddIssueComment(t translations.TranslationHelperFunc) inventory.ServerTool
10871092
Enum: []any{"+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", "eyes"},
10881093
},
10891094
},
1090-
Required: []string{"owner", "repo", "issue_number"},
1095+
Required: []string{"owner", "repo"},
10911096
},
10921097
},
10931098
[]scopes.Scope{scopes.Repo},
@@ -1100,9 +1105,23 @@ func AddIssueComment(t translations.TranslationHelperFunc) inventory.ServerTool
11001105
if err != nil {
11011106
return utils.NewToolResultError(err.Error()), nil, nil
11021107
}
1103-
issueNumber, err := RequiredInt(args, "issue_number")
1104-
if err != nil {
1105-
return utils.NewToolResultError(err.Error()), nil, nil
1108+
var issueNumber int
1109+
hasIssueNumber := false
1110+
if _, ok := args["issue_number"]; ok {
1111+
issueNumber, err = RequiredInt(args, "issue_number")
1112+
if err != nil {
1113+
return utils.NewToolResultError(err.Error()), nil, nil
1114+
}
1115+
hasIssueNumber = true
1116+
}
1117+
var commentID int64
1118+
hasCommentID := false
1119+
if _, ok := args["comment_id"]; ok {
1120+
commentID, err = RequiredBigInt(args, "comment_id")
1121+
if err != nil {
1122+
return utils.NewToolResultError(err.Error()), nil, nil
1123+
}
1124+
hasCommentID = true
11061125
}
11071126
body, hasBody, err := OptionalParamOK[string](args, "body")
11081127
if err != nil {
@@ -1115,6 +1134,12 @@ func AddIssueComment(t translations.TranslationHelperFunc) inventory.ServerTool
11151134
if !hasBody && !hasReaction {
11161135
return utils.NewToolResultError("at least one of body or reaction is required"), nil, nil
11171136
}
1137+
if hasBody && !hasIssueNumber {
1138+
return utils.NewToolResultError("issue_number is required when body is provided"), nil, nil
1139+
}
1140+
if hasReaction && !hasIssueNumber && !hasCommentID {
1141+
return utils.NewToolResultError("issue_number or comment_id is required when reaction is provided"), nil, nil
1142+
}
11181143
if hasBody && body == "" {
11191144
return utils.NewToolResultError("body cannot be empty when provided"), nil, nil
11201145
}
@@ -1154,15 +1179,28 @@ func AddIssueComment(t translations.TranslationHelperFunc) inventory.ServerTool
11541179

11551180
var reactionResponse *MinimalResponse
11561181
if hasReaction {
1157-
reaction, resp, err := client.Reactions.CreateIssueReaction(ctx, owner, repo, issueNumber, reactionContent)
1158-
if err != nil {
1159-
return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to add reaction to issue", resp, err), nil, nil
1160-
}
1161-
defer func() { _ = resp.Body.Close() }()
1182+
if hasCommentID {
1183+
reaction, resp, err := client.Reactions.CreateIssueCommentReaction(ctx, owner, repo, commentID, reactionContent)
1184+
if err != nil {
1185+
return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to add reaction to issue comment", resp, err), nil, nil
1186+
}
1187+
defer func() { _ = resp.Body.Close() }()
1188+
1189+
reactionResponse = &MinimalResponse{
1190+
ID: fmt.Sprintf("%d", reaction.GetID()),
1191+
URL: fmt.Sprintf("%srepos/%s/%s/issues/comments/%d/reactions/%d", client.BaseURL(), owner, repo, commentID, reaction.GetID()),
1192+
}
1193+
} else {
1194+
reaction, resp, err := client.Reactions.CreateIssueReaction(ctx, owner, repo, issueNumber, reactionContent)
1195+
if err != nil {
1196+
return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to add reaction to issue", resp, err), nil, nil
1197+
}
1198+
defer func() { _ = resp.Body.Close() }()
11621199

1163-
reactionResponse = &MinimalResponse{
1164-
ID: fmt.Sprintf("%d", reaction.GetID()),
1165-
URL: fmt.Sprintf("%srepos/%s/%s/issues/%d/reactions/%d", client.BaseURL(), owner, repo, issueNumber, reaction.GetID()),
1200+
reactionResponse = &MinimalResponse{
1201+
ID: fmt.Sprintf("%d", reaction.GetID()),
1202+
URL: fmt.Sprintf("%srepos/%s/%s/issues/%d/reactions/%d", client.BaseURL(), owner, repo, issueNumber, reaction.GetID()),
1203+
}
11661204
}
11671205
}
11681206

pkg/github/issues_test.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -571,9 +571,10 @@ func Test_AddIssueComment(t *testing.T) {
571571
assert.Contains(t, tool.InputSchema.(*jsonschema.Schema).Properties, "owner")
572572
assert.Contains(t, tool.InputSchema.(*jsonschema.Schema).Properties, "repo")
573573
assert.Contains(t, tool.InputSchema.(*jsonschema.Schema).Properties, "issue_number")
574+
assert.Contains(t, tool.InputSchema.(*jsonschema.Schema).Properties, "comment_id")
574575
assert.Contains(t, tool.InputSchema.(*jsonschema.Schema).Properties, "body")
575576
assert.Contains(t, tool.InputSchema.(*jsonschema.Schema).Properties, "reaction")
576-
assert.ElementsMatch(t, tool.InputSchema.(*jsonschema.Schema).Required, []string{"owner", "repo", "issue_number"})
577+
assert.ElementsMatch(t, tool.InputSchema.(*jsonschema.Schema).Required, []string{"owner", "repo"})
577578

578579
// Setup mock comment for success case
579580
mockComment := &github.IssueComment{
@@ -4185,9 +4186,10 @@ func TestAddIssueComment(t *testing.T) {
41854186
assert.Contains(t, schema.Properties, "owner")
41864187
assert.Contains(t, schema.Properties, "repo")
41874188
assert.Contains(t, schema.Properties, "issue_number")
4189+
assert.Contains(t, schema.Properties, "comment_id")
41884190
assert.Contains(t, schema.Properties, "body")
41894191
assert.Contains(t, schema.Properties, "reaction")
4190-
assert.ElementsMatch(t, schema.Required, []string{"owner", "repo", "issue_number"})
4192+
assert.ElementsMatch(t, schema.Required, []string{"owner", "repo"})
41914193

41924194
mockComment := &github.IssueComment{
41934195
ID: github.Ptr(int64(456)),
@@ -4230,6 +4232,18 @@ func TestAddIssueComment(t *testing.T) {
42304232
"reaction": "heart",
42314233
},
42324234
},
4235+
{
4236+
name: "successful reaction to issue comment",
4237+
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
4238+
PostReposIssuesCommentsReactionsByOwnerByRepoByCommentID: mockResponse(t, http.StatusCreated, mockReaction),
4239+
}),
4240+
requestArgs: map[string]any{
4241+
"owner": "owner",
4242+
"repo": "repo",
4243+
"comment_id": float64(999),
4244+
"reaction": "heart",
4245+
},
4246+
},
42334247
{
42344248
name: "successful comment and reaction to issue",
42354249
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
@@ -4255,14 +4269,24 @@ func TestAddIssueComment(t *testing.T) {
42554269
expectedToolErrMsg: "at least one of body or reaction is required",
42564270
},
42574271
{
4258-
name: "missing required parameter issue_number",
4272+
name: "missing target for reaction",
42594273
requestArgs: map[string]any{
42604274
"owner": "owner",
42614275
"repo": "repo",
42624276
"reaction": "heart",
42634277
},
42644278
expectToolError: true,
4265-
expectedToolErrMsg: "missing required parameter: issue_number",
4279+
expectedToolErrMsg: "issue_number or comment_id is required when reaction is provided",
4280+
},
4281+
{
4282+
name: "missing issue_number for body",
4283+
requestArgs: map[string]any{
4284+
"owner": "owner",
4285+
"repo": "repo",
4286+
"body": "This is a comment",
4287+
},
4288+
expectToolError: true,
4289+
expectedToolErrMsg: "issue_number is required when body is provided",
42664290
},
42674291
}
42684292

0 commit comments

Comments
 (0)