fix: route Forgejo review comment updates to issue comment API

On Forgejo, the tracking comment for pull_request_review_comment events
is always created as a regular issue comment (because Forgejo's webhook
payload lacks the top-level comment object). The MCP server was
incorrectly setting isPullRequestReviewComment=true based on the event
name, causing every update to try pulls.updateReviewComment first —
which 404'd and silently swallowed Claude's responses.

Three fixes:
- MCP server: detect Forgejo and always use issue comment path
- update-comment-link: skip pulls.getReviewComment on Forgejo (saves
  one wasted 404 API call)
- update-claude-comment: expand fallback to also catch 422 (defense
  in depth for Forgejo edge cases)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vlad MocanuandClaude Opus 4.6 committed 2026-02-26 10:45:09 +02:00
1 parent 821cc86c6e
commit 84cf34ff1f
3 files changed
+26 -8

No files matched your search

+7 -5
View File
@@ -65,11 +65,13 @@ export async function updateCommentLink(
try { try {
// GitHub has separate ID namespaces for review comments and issue comments // GitHub has separate ID namespaces for review comments and issue comments
// We need to use the correct API based on the event type // We need to use the correct API based on the event type
if (isPullRequestReviewCommentEvent(context)) { //
// For PR review comments, try the pulls API first. // On Forgejo, the tracking comment is always an issue comment because
// On Forgejo, the initial comment may have been created as a regular // Forgejo's pull_request_review_comment payload lacks the top-level
// issue comment (because the payload lacks comment.id), so the pulls // `comment` object (so createInitialComment falls back to
// endpoint may 404. Fall through to issue comment fetch in that case. // issues.createComment). Skip the pulls API attempt on Forgejo to avoid
// a wasted 404 round-trip.
if (isPullRequestReviewCommentEvent(context) && platform !== "forgejo") {
try { try {
console.log(`Fetching PR review comment ${commentId}`); console.log(`Fetching PR review comment ${commentId}`);
const { data: prComment } = await octokit.rest.pulls.getReviewComment({ const { data: prComment } = await octokit.rest.pulls.getReviewComment({
@@ -49,8 +49,14 @@ export async function updateClaudeComment(
}); });
} }
} catch (error: any) { } catch (error: any) {
// If PR review comment update fails with 404, fall back to issue comment API // If PR review comment update fails, fall back to issue comment API.
if (isPullRequestReviewComment && error.status === 404) { // 404 = comment ID not in PR review namespace (expected on Forgejo where
// tracking comments are always issue comments).
// 422 = some Forgejo versions return Unprocessable Entity instead of 404.
if (
isPullRequestReviewComment &&
(error.status === 404 || error.status === 422)
) {
response = await octokit.rest.issues.updateComment({ response = await octokit.rest.issues.updateComment({
owner, owner,
repo, repo,
+11 -1
View File
@@ -52,8 +52,18 @@ server.tool(
baseUrl: GITHUB_API_URL, baseUrl: GITHUB_API_URL,
}); });
// The tracking comment is always created as a regular issue comment on
// Forgejo because Forgejo's pull_request_review_comment payload lacks the
// top-level `comment` object (so createInitialComment falls back to
// issues.createComment). On GitHub, the tracking comment IS a review
// comment reply (created via pulls.createReplyForReviewComment).
//
// Setting this wrong causes every MCP update to first try
// pulls.updateReviewComment (404), and if the fallback doesn't trigger,
// Claude's responses never reach the comment.
const isForgejo = (process.env.GITHUB_API_URL || "").includes("/api/v1");
const isPullRequestReviewComment = const isPullRequestReviewComment =
eventName === "pull_request_review_comment"; eventName === "pull_request_review_comment" && !isForgejo;
const sanitizedBody = sanitizeContent(body); const sanitizedBody = sanitizeContent(body);