Building an AI-Powered Automated Code Reviewer in Python
- SEO Title: Build a Custom AI Code Reviewer Using Python and MCP
- Meta Description: Learn how to write an automated GitHub Action that leverages Python, MCP, and AI models to perform automated security and style code reviews.
- Target Audience: CS Students, Python Developers, Open-Source Contributors.
- Primary Focus: Building an automated GitHub Action that uses MCP and LLMs to review pull requests against custom style and security rules.

Introduction: The Noise Problem in Off-the-Shelf AI Code Reviews
Out-of-the-box AI code review tools often bombard pull requests with low-value, generic feedback. They flag standard naming conventions or invent non-existent security flaws because they lack context about your team’s specific architecture, internal APIs, and style guidelines.
By building a custom AI code reviewer in Python, you bridge this context gap. Utilizing the Model Context Protocol (MCP), your review agent queries your repository’s type definitions, linting rules, and historical security policies dynamically before evaluating code. Paired with a simple GitHub Action, you get a tailored, automated reviewer that flags high-priority vulnerabilities without the noise.
Project Prerequisites
Ensure you have the following installed and configured before starting:
- Python 3.11+ (for native async capabilities and static typing support)
- GitHub Repository with GitHub Actions enabled
- MCP Python SDK (
pip install "mcp[cli]"oruv add "mcp[cli]") - LLM API Access (OpenAI or Anthropic API key stored as a GitHub Repository Secret)
Step 1: Setting Up the GitHub Action Workflow
Create a GitHub Action workflow file at .github/workflows/ai-code-review.yml. This workflow triggers on every pull request event, checks out the PR diff, sets up Python, and invokes your review script.
YAML
name: AI Code Reviewer
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install mcp openai requests
- name: Run AI Code Review
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPOSITORY: ${{ github.repository }}
run: python scripts/review_agent.py
Step 2: Building the MCP Server for Code Analysis
Next, build an inline MCP server in Python using FastMCP. This server exposes internal context tools (fetching git diffs, parsing linting rules, and retrieving type definitions) to the AI agent.
Python
# scripts/mcp_server.py
import subprocess
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("CodeReviewContext")
@mcp.tool()
def get_pr_diff() -> str:
"""Fetches the unified git diff for the current pull request against main."""
result = subprocess.run(
["git", "diff", "origin/main...HEAD"],
capture_output=True, text=True, check=True
)
return result.stdout[:12000] # Truncate to preserve context window
@mcp.tool()
def get_style_rules() -> str:
"""Returns local project formatting and security policy rules."""
return """
1. OWASP Risk Check: Never execute raw SQL queries; use Parameterized queries.
2. Exception Handling: Never use bare 'except:'; catch specific Exception subclasses.
3. Type Hinting: All public functions must declare explicit return type hints.
"""
if __name__ == "__main__":
mcp.run()
Step 3: Constructing the Review Agent
Now construct the execution agent (scripts/review_agent.py) using the official Python SDK. The agent queries tools on your local MCP server, gathers contextual rules, evaluates the PR diff, and returns structured JSON suggestions.
Python
# scripts/review_agent.py
import os
import json
import openai
from scripts.mcp_server import get_pr_diff, get_style_rules
def generate_review() -> list[dict]:
client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
diff = get_pr_diff()
rules = get_style_rules()
prompt = f"""
You are an expert Security and Code Quality Auditor.
Review the following Git Diff against our custom Project Rules:
--- PROJECT RULES ---
{rules}
--- GIT DIFF ---
{diff}
Return feedback strictly as a JSON list of objects. Each object MUST contain:
- "path": (string) relative file path
- "line": (integer) exact modified line number in the diff
- "body": (string) markdown feedback with severity emoji (🔴 Critical, 🟡 Minor)
"""
response = client.chat.completions.create(
model="gpt-4o",
response_format={"type": "json_object"},
messages=[{"role": "user", "content": prompt}]
)
result = json.loads(response.choices[0].message.content)
return result.get("comments", [])
Step 4: Posting Inline Comments to GitHub
Finally, map the structured JSON output directly onto PR lines using the GitHub REST API (/repos/{owner}/{repo}/pulls/{pr_number}/reviews).
Python
# scripts/post_comments.py
import os
import requests
from scripts.review_agent import generate_review
def post_github_review():
token = os.getenv("GITHUB_TOKEN")
repo = os.getenv("REPOSITORY")
pr_number = os.getenv("PR_NUMBER")
comments = generate_review()
if not comments:
print("No issues detected.")
return
# Get the head commit SHA for the PR
pr_url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github.v3+json"
}
commit_sha = requests.get(pr_url, headers=headers).json()["head"]["sha"]
review_url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}/reviews"
payload = {
"commit_id": commit_sha,
"event": "COMMENT",
"comments": comments
}
res = requests.post(review_url, headers=headers, json=payload)
if res.status_code == 200:
print("Successfully posted inline code review comments.")
else:
print(f"Failed to post review: {res.status_code} - {res.text}")
if __name__ == "__main__":
post_github_review()
Frequently Asked Questions (FAQ)
How do you keep API costs low when running code reviews on every commit?
To optimize token usage and reduce API expenses across frequent commits:
- Diff Truncation & Filtering: Ignore non-code assets (e.g.,
.md,.json, package lockfiles) in your git diff script before sending payloads to the LLM. - Run on Event Conditions: Configure your GitHub Action trigger to run only when PRs are opened or marked ready for review, rather than on every minor push:YAML
on: pull_request: types: [opened, ready_for_review] - Use Tiered Models: Route routine style checks to fast, cost-effective models (e.g.,
gpt-4o-minior Claude 3.5 Haiku) and reserve premier models exclusively for security-critical modules.
