Repository navigation
feat(github-mcp): add comment_edit to edit existing comments - #21
Merged
Merged
Conversation
Agents could create comments but not correct them: the `api` write tool sends no request body, and the `gh` hook blocks `gh pr comment --edit-last`. `comment_edit` takes the comment's URL and a new body. The URL anchor decides the call, because GitHub keeps issue comments, inline review comments and review summaries in separate id sequences, so a bare numeric id cannot identify the kind. `#issuecomment-ID` goes to `PATCH issues/comments/ID`, `#pullrequestreview-ID` to `PUT pulls/N/reviews/ID`, and `#discussion_rID` or `/files#rID` / `/changes#rID` to `PATCH pulls/comments/ID`. REST answers 404 for a comment in the caller's unsubmitted review, so for inline anchors the tool first queries the caller's pending review over GraphQL and, on a match, edits it with `updatePullRequestReviewComment`. Every failure, including a lookup error, a missing URL in GitHub's response, or more than 100 pending comments without a match, returns an error. No route falls back to another. The tool declares no `fallback` or `suppress_errors`, since `fallback` would report a failed edit as a success. `check-api-tools.sh` redirects the three edit endpoints on the `api` tool to `comment_edit`, and the `gh pr comment` / `gh issue comment` block messages name it. Co-Authored-By: Claude <noreply@anthropic.com>
`comment_edit` used the issue or PR number in the URL only to build the review-summary endpoint, so `issues/7#issuecomment-555` edited comment 555 even when it belonged to issue 99. The tool now reads a conversation or inline comment first and edits it only when its `issue_url` ends in `/issues/N` or its `pull_request_url` in `/pulls/N`. Otherwise it errors and writes nothing. The read also picks the route for inline comments. The previous version queried the pending review over GraphQL before every inline edit and failed hard on a lookup error or on more than 100 pending comments, which blocked edits of submitted comments that REST alone would complete. Now a successful read leads to the REST `PATCH`, and only a 404 leads to the pending-review lookup. The lookup matches `fullDatabaseId` as a string, takes the caller's own review via `viewerDidAuthor` among up to 100 pending reviews, and errors when nothing matches. Every gh call captures stdout and stderr apart through `_gh_capture_split`, so a stderr warning on a successful mutation no longer reports an applied edit as failed. One helper, `_comment_edit_graphql`, classifies GraphQL failures for the lookup and the mutation, and every error starts with `Error:`. The `check-api-tools.sh` message for `PATCH issues/comments/ID` pointed to `pr_comments`, which never returns conversation comments. It now points to `issue_view` or `pr_view` with `fields=comments`. Three tests that only checked a static prompt or message for a word are removed. Co-Authored-By: Claude <noreply@anthropic.com>
…it URLs A pending review with more than 100 comments made `comment_edit` return an error instead of finding the comment, so an edit the tool could complete failed. The lookup now reads further pages of the caller's pending review by `endCursor`, and errors when a page fails, when `hasNextPage` comes with no cursor, or when a cursor it already used comes back, which stops both a repeated cursor and a cycle such as A, B, A. The URL parser rejected owners with `_`, which Enterprise Managed User accounts carry (`mona_octocorp`), and accepted numbers with leading zeros that then failed the ownership check with a contradictory message. Owners may now contain `_`, and a number or ID starting with `0` is rejected before any request. When the inline-comment read answers 404 and the lookup fails or finds nothing, the error now states the 404 as well, so a typo in the repository name no longer shows up only as a GraphQL message. Every gh call runs through one helper that builds the error text in one place, and the mutation's jq call is guarded so the output always starts with `Error:`. The tool description names all accepted URL prefixes and says it returns the edited comment's URL, which matches the pending-review path. Two tests that only matched query text are removed. Co-Authored-By: Claude <noreply@anthropic.com>
…thor is set
With a maintainer token, `comment_edit` edited any comment on the issue or PR named in the URL, so a wrong URL silently rewrote a contributor's text under their name. The tool now compares the comment's author with the caller's login from GraphQL `viewer { login }` and refuses a mismatch unless the new boolean `allow_other_author` is true, which keeps moderation possible on request. The review summary is now read before its `PUT` for the same check, and a comment in the caller's own pending review needs no check.
A write that GitHub accepted but answered without a URL was reported as a failed edit, which invites a retry of an edit that already landed. It stays an error but now says the edit was sent.
Links copied from GitHub now work. A query string such as `?notification_referrer_id=` is ignored, and inline comments are accepted on `pull/N/files/<sha>..<sha>` and `pull/N/commits/<sha>`. The README, CHANGELOG and SessionStart wording no longer promise every comment kind. Commit and Discussions comments are not supported. The two inline anchor branches are merged, and a test that could not fail is removed.
Co-Authored-By: Claude <noreply@anthropic.com>
Comparing the comment's REST `user.login` with GraphQL `viewer.login` may disagree for App and Actions tokens, which would refuse a bot's edit of its own comment. `comment_edit` now asks GitHub directly through `viewerDidAuthor` on the comment's node and refuses anything but `true` unless `allow_other_author` is set. Several guards protected against responses GitHub does not send or against URLs it never produces, and each would have failed loudly without them. Cursor cycle detection is replaced by a 50-page limit, the pending-review author filter and the more-than-100-pending-reviews error are gone (whether another user's pending comment may be edited is left to GitHub's mutation), malformed-response branches collapse into fewer errors, and `.git` and leading-zero rejections are dropped. The `.`/`..` repo rejection stays, since `repos/o/../...` would reach a different endpoint. Links copied from GitHub's Files and Changes views of a single commit or a range now work, and the host is matched case-insensitively. `check-gh-tools.sh` now redirects raw `gh api` edits of comments and review summaries to `comment_edit` under `block_api_commands`. Before, these calls were not redirected to `comment_edit`, and one form of the review `PUT` went to `pr_reviews`. The CHANGELOG entries describe what users can do instead of how the tool works inside, REFERENCE.md lost claims the code no longer backs, the plugin-setup CHANGELOG records its new permission pattern, and the URL-form tests are one table. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The plugin could create comments but not correct them.
issue_comment,pr_commentandpr_review_replyonly post new ones, the write server'sapitool sends no request body, and the hook blocksgh pr comment --edit-last. This adds the write toolcomment_edit, which replaces the body of an existing comment given its URL.Changes
tool_comment_editinlib/review_write.sh): GitHub numbers issue comments, inline review comments and reviews in separate sequences, so a bare ID does not say which endpoint to call. The anchor does:#issuecomment-IDgoes toPATCH issues/comments/ID,#discussion_rID(or#rIDin the Files, Changes and commit views of a PR) toPATCH pulls/comments/ID, and#pullrequestreview-IDtoPUT pulls/N/reviews/ID. The URL works with or withouthttps://github.com/, a notification link's query string is ignored, and any other URL fails before a request.pulls/N/reviews/ID, which already carries the PR number.viewerDidAuthorwhether the caller wrote it, and refuses otherwise.allow_other_author: trueskips the check, so moderation edits stay possible.updatePullRequestReviewCommentmutation. The 404 and the lookup's result both appear in the error when nothing matches.fallbackorsuppress_errors: every other tool declares both, butfallbackwould turn a failed edit into a success, socomment_editdeclares neither and every failure is a tool error.check-api-tools.sh(theapitool, underblock_api_tool_write) andcheck-gh-tools.sh(rawgh api, underblock_api_commands) sendPATCHonissues/comments/IDandpulls/comments/IDandPUTonpulls/N/reviews/IDtocomment_edit. The block messages forgh pr commentandgh issue commentname it for editing an existing comment.tool_searchrankspr_reviewsbelow its allowed rank.REFERENCE.mduses the plain terms.REFERENCE.mddocuments the URL forms, the checks and the errors; the README,SETUP.mdand its plugin-setup copy, the SessionStart directive and the MCP issue template list the tool. The plugin-setupCHANGELOG.mdrecords the new permission pattern.Known limits
viewerDidAuthoris untested; if it refuses the token's own comment,allow_other_author: trueis the way around it.