A free server can become a pull request reviewer. This tutorial builds a webhook-driven bot in thirty minutes. The bot reads a PR diff, asks a free model for feedback, and posts a comment.
MonkeyCode is an open-source project. It provides a free server and free model tokens. Quotas rotate, so the dashboard is the source of truth. Disclosure: This article was prepared as part of MonkeyCode's product outreach.
The stack is simple. FastAPI receives webhooks. The OpenAI client calls the model. Requests posts the comment. The free server runs everything.
Step 1: Reach the free server
Connect to the sandbox. The dashboard lists the host and credentials. Verify Python first.
ssh user@your-free-server-host
python3 --version
Python 3.11 or newer is required. The bot uses modern async syntax. Older versions will fail.
Step 2: Create the bot
Make a directory and install dependencies.
mkdir -p pr-bot && cd pr-bot
python3 -m venv .venv
source .venv/bin/activate
pip install fastapi uvicorn openai requests
Verify the installation with a one-liner.
python -c "import fastapi, openai, requests; print('stack ready')"
The output "stack ready" confirms the environment. Now create the main file.
nano bot.py
Paste the complete bot below.
import os
import json
import hmac
import hashlib
import requests
from fastapi import FastAPI, Request, HTTPException
from openai import OpenAI
app = FastAPI()
client = OpenAI(
base_url=os.environ["MC_BASE_URL"],
api_key=os.environ["MC_API_KEY"],
)
GITHUB_SECRET = os.environ["GITHUB_SECRET"].encode()
GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
def verify_signature(payload, signature):
mac = hmac.new(GITHUB_SECRET, payload, hashlib.sha1)
expected = f"sha1={mac.hexdigest()}"
return hmac.compare_digest(expected, signature)
@app.post("/webhook")
async def webhook(request: Request):
payload = await request.body()
signature = request.headers.get("X-Hub-Signature", "")
if not verify_signature(payload, signature):
raise HTTPException(403, "Invalid signature")
event = request.headers.get("X-GitHub-Event")
if event != "pull_request":
return {"status": "ignored"}
data = json.loads(payload)
if data.get("action") != "opened":
return {"status": "ignored"}
pr = data["pull_request"]
repo = data["repository"]["full_name"]
number = pr["number"]
diff_url = pr["diff_url"]
headers = {"Authorization": f"token {GITHUB_TOKEN}"}
diff = requests.get(diff_url, headers=headers).text[:3000]
response = client.chat.completions.create(
model=os.environ["MC_MODEL"],
messages=[
{"role": "system", "content": "You are a code reviewer. Be concise."},
{"role": "user", "content": f"Review this diff:\n{diff}"}
],
max_tokens=200,
)
comment = response.choices[0].message.content
comments_url = f"https://api.github.com/repos/{repo}/issues/{number}/comments"
requests.post(comments_url, headers=headers, json={"body": comment})
return {"status": "commented"}
Save the file. The bot verifies the webhook signature. It ignores non-PR events. It only acts on opened PRs.
The diff is truncated to 3000 characters. That keeps the model call cheap. The comment is posted to the PR thread.
Step 3: Run the bot
Export the required environment variables.
export MC_BASE_URL="https://your-endpoint.example"
export MC_API_KEY="your-key"
export MC_MODEL="your-model"
export GITHUB_SECRET="your-webhook-secret"
export GITHUB_TOKEN="your-github-token"
Start the server.
uvicorn bot:app --host 0.0.0.0 --port 8000
Verify the server is alive with a simple request.
curl http://localhost:8000/webhook -X POST
The server returns a 403 because the signature is missing. That response proves the endpoint is reachable.
Step 4: Configure GitHub
Open the repository settings. Navigate to Webhooks. Add a new webhook.
Set the payload URL to http://your-free-server-host:8000/webhook. Set the content type to application/json. Paste the secret.
Select "Let me select individual events." Check "Pull requests." Save the webhook.
GitHub sends a test ping. The bot ignores it because the event type is not pull_request. The ping confirms the connection.
Step 5: Test with a real PR
Create a new branch. Push a small change. Open a pull request.
The bot receives the event. It fetches the diff. It calls the model. It posts a comment.
The comment appears within seconds. The PR thread shows the review. That is the final verification.
Limitations
The bot reviews only the first 3000 characters of the diff. Large PRs get partial reviews. The free model may miss subtle bugs. The comment is a suggestion, not a guarantee.
The webhook secret protects against forgery. The GitHub token limits access to one repository. A production bot needs a queue and retry logic.
Free tiers change. The dashboard lists current quotas. The bot works until the quota runs out.
Who should not use this
Teams with strict code review standards should not rely on this bot. It is a triage tool, not a senior reviewer. Security-sensitive projects need human review.
The bot fits side projects and open-source repositories. It gives instant feedback. It catches obvious issues. It starts a conversation.
The verdict
A free server turns a webhook into a useful bot. The setup takes thirty minutes. The code is short and auditable. The model adds a second opinion to every PR.
Run it on MonkeyCode's free tier. The dashboard has the current limits. The next PR might get a comment you did not expect.
Top comments (0)