CLAUDE LABJP
MCP — Support for the 2026-07-28 spec is rolling out across Claude. The protocol moves from bidirectional and stateful to request/response, so MCP servers can now live on serverless and edge infrastructureEXTENSIONS — Three official extensions have landed: MCP Apps for server-rendered UI, Tasks for async and long-running work, and Enterprise Managed Auth for IdP-based org-wide provisioningADOPTION — MCP passed 400 million monthly SDK downloads, roughly 4x growth this year, settling into its role as the standard way to connect agents to applicationsQUOTA — Today, August 19, is the last day of the 50 percent weekly usage boost for Claude Code subscribers. If you have long agent runs queued, this is the windowPRICING — Claude Sonnet 5's introductory rate of $2 per million input tokens and $10 output ends August 31; standard pricing of $3 and $15 takes over on September 1, twelve days outFIX — A bug where MCP v2 connections endlessly reopened subscriptions against servers with fixed timeouts is resolved, and a forward_user_identity setting was added for user attributionMCP — Support for the 2026-07-28 spec is rolling out across Claude. The protocol moves from bidirectional and stateful to request/response, so MCP servers can now live on serverless and edge infrastructureEXTENSIONS — Three official extensions have landed: MCP Apps for server-rendered UI, Tasks for async and long-running work, and Enterprise Managed Auth for IdP-based org-wide provisioningADOPTION — MCP passed 400 million monthly SDK downloads, roughly 4x growth this year, settling into its role as the standard way to connect agents to applicationsQUOTA — Today, August 19, is the last day of the 50 percent weekly usage boost for Claude Code subscribers. If you have long agent runs queued, this is the windowPRICING — Claude Sonnet 5's introductory rate of $2 per million input tokens and $10 output ends August 31; standard pricing of $3 and $15 takes over on September 1, twelve days outFIX — A bug where MCP v2 connections endlessly reopened subscriptions against servers with fixed timeouts is resolved, and a forward_user_identity setting was added for user attribution
Articles/API & SDK
API & SDK/2026-04-17Advanced

Building a GitHub PR Review Bot with Claude API — From Webhook Signature Verification to Structured Reviews with Tool Use

Build a production PR review bot with Claude API and GitHub Webhooks. Enforce structured scoring, security scanning, and suggestions via Tool Use — with signature verification, debouncing, rate limits, and cost tracking drawn from real-world operation.

Claude API117GitHub6PR ReviewTool Use9Webhook3TypeScript24Automation42Code Review2

Premium Article

Once your team grows past five engineers, code review consistency becomes a real problem. Some reviewers catch every instance of a particular bug pattern; others let the same thing slide. Review quality drops when people are tired. And more fundamentally, nobody has agreed on what "good" actually looks like.

Claude API can help. But asking it to "review this code" in the simplest possible way won't give you something you can rely on — the output varies too much. What makes this work at production scale is two things: using Tool Use to enforce structured output, and building a clean pipeline from GitHub Webhook all the way to posted review comments.

This article builds a bot that automatically posts review comments when a PR is opened or updated. We'll go from architecture design to deployment, explaining the why behind each implementation choice along the way.

System Architecture Overview

The flow is straightforward:

GitHub PR opened/updated
    ↓
Webhook event (POST /webhook)
    ↓
Express server (signature verification)
    ↓
Fetch PR diff via GitHub API
    ↓
Claude API (Tool Use) generates structured review
    ↓
Post review comment via GitHub API

The tech stack:

  • Runtime: Node.js 22 + TypeScript
  • Web framework: Express.js 4.x
  • Claude API: @anthropic-ai/sdk v1.x
  • GitHub API: @octokit/rest v21
  • Deployment: Railway / Render (always-on server required)

Why You Need an Always-On Server

GitHub Webhooks deliver POST requests in real time. While Cloudflare Workers or Vercel Serverless Functions technically work, architectures with cold starts are risky here. GitHub sets a 10-second timeout on webhook delivery, and a cold start that delays your response can trigger a retry loop — meaning the same PR gets reviewed twice or more.

Railway starts at around $5/month for an always-on server. That's more than sufficient for most teams.

Start by installing dependencies:

npm init -y
npm install express @anthropic-ai/sdk @octokit/rest
npm install -D typescript @types/express @types/node ts-node

Step 1: The Webhook Server

Receiving webhooks is where most implementations first go wrong: signature verification. GitHub attaches an X-Hub-Signature-256 header to every webhook request. Without verifying it, anyone can send forged requests to your endpoint.

// src/webhook.ts
import express from 'express';
import crypto from 'crypto';
import { handlePullRequestEvent } from './handlers/pullRequest';
 
const app = express();
 
// ⚠️ Critical: raw body required — do NOT use express.json()
app.use(
  express.raw({ type: 'application/json' })
);
 
function verifyWebhookSignature(
  rawBody: Buffer,
  signature: string,
  secret: string
): boolean {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(rawBody);
  const expectedSignature = `sha256=${hmac.digest('hex')}`;
 
  // Use timingSafeEqual to prevent timing attacks
  try {
    return crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(expectedSignature)
    );
  } catch {
    // timingSafeEqual requires equal-length buffers
    return false;
  }
}
 
app.post('/webhook', async (req, res) => {
  const signature = req.headers['x-hub-signature-256'] as string;
  const eventType = req.headers['x-github-event'] as string;
 
  if (!signature) {
    return res.status(401).json({ error: 'Missing signature' });
  }
 
  const isValid = verifyWebhookSignature(
    req.body as Buffer,
    signature,
    process.env.GITHUB_WEBHOOK_SECRET!
  );
 
  if (!isValid) {
    console.error('Invalid webhook signature — possible spoofed request');
    return res.status(401).json({ error: 'Invalid signature' });
  }
 
  // Respond immediately — GitHub's timeout is 10 seconds
  res.status(202).json({ accepted: true });
 
  const payload = JSON.parse((req.body as Buffer).toString());
 
  if (
    eventType === 'pull_request' &&
    ['opened', 'synchronize'].includes(payload.action)
  ) {
    // Process asynchronously after responding
    handlePullRequestEvent(payload).catch(err => {
      console.error('[PR handler] Unhandled error:', err);
    });
  }
});
 
app.get('/health', (_req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
 
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Webhook server listening on port ${PORT}`);
});

Two things worth explaining here.

Why express.raw() instead of express.json(): The HMAC signature is computed against the raw bytes of the request body. Once Express parses the JSON, you've lost the original bytes. Even re-serializing with JSON.stringify() won't work reliably — property ordering and whitespace can differ. This is the single most common source of 401 errors in webhook integrations.

Why respond before processing: GitHub's webhook timeout is 10 seconds. Claude API calls can take several seconds. If you wait for the review to complete before responding, you'll hit the timeout, GitHub will retry, and the same PR gets reviewed multiple times. Send 202 immediately, then process asynchronously.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Prevent GitHub's 10s timeout and retry loops with express.raw() signature verification and an immediate 202 response
Force structured overall_score / security_issues through Tool Use (tool_choice: any) with exponential backoff for production stability
Operational metrics: a 30→90s debounce cut duplicate reviews ~70%; output tokens run 600-900 (clean) vs 2,500-3,500 (critical found)
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

API & SDK2026-05-06
Building an Autonomous Research Agent with Claude API: Web Search, Summarization, and Knowledge Management
A complete guide to designing and implementing an autonomous research agent using Claude API and web search tools. Covers budget control, quality assurance, and knowledge base storage for production use.
API & SDK2026-08-01
Swapping Tools Mid-Conversation Without Losing Your Prompt Cache
Tools can now be added and removed between turns, but the tool block sits at the very front of the cache prefix. I measured 12 mutation patterns with fingerprints and built a stable-core plus volatile-tail registry with a guard that refuses unsafe changes.
API & SDK2026-07-12
When You Give an API Key an Expiration Date, Expiry Becomes a Plan Instead of an Accident
The Console now lets you set expiration dates on API keys. Here is how to fold planned expiry into unattended operations — with overlapping dual keys and a local expiry ledger — so your nightly jobs never go dark.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →