Skip to content
Conduit

mcp server

pr-guardian

AI-assisted code review and release automation for Python repos — built to learn every Claude Code feature (agents, hooks, skills, slash commands, MCP).

SumitCodesAI/pr-guardiancommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • uv (`curl -LsSf https://astral.sh/uv/install.sh | sh`)
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "pr-guardian": {
      "command": "uvx",
      "args": [
        "pr-guardian"
      ]
    }
  }
}

same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12

Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs

README

synced Oct 25, 2026

PR Guardian

AI-assisted code review and release automation for Python repos — built to teach every major Claude Code feature by doing.

PR Guardian reviews a Python diff across four specialized passes — security, style, test coverage, docs — each with a pass/fail verdict, and generates Keep-a-Changelog release notes from git history. The same review categories run in two cooperating layers: a fast, offline, fully unit-tested deterministic Python CLI, and a Claude Code AI layer that drives the same passes with subagents, skills, slash commands, hooks, and an MCP server.

Why this project exists. It is a hands-on playground for learning Claude Code. Every feature below does real work in this repo — nothing is a toy — so by the time you've read this and run it, you will have used CLAUDE.md, agents, hooks, slash commands, skills, and MCP in one coherent project, and you'll understand why each feature exists and when to reach for it.


Table of contents

  1. The big picture: two layers, one format
  2. Quick start
  3. Setting up on a new machine
  4. Feature-by-feature tour
  5. How the two layers mirror each other
  6. End-to-end flows
  7. Project layout
  8. Usage reference
  9. The demo repository
  10. Testing
  11. Troubleshooting
  12. Further reading

The big picture: two layers, one format

PR GUARDIAN — one codebase, two layers, one finding format
==========================================================

  LAYER 1 · DETERMINISTIC ENGINE          LAYER 2 · CLAUDE CODE AI LAYER
  Python · offline · fully tested         Claude running in your editor

  pr-guardian CLI                         /review
    ├─ review                             /security-scan
    ├─ scan                               /release-notes
    └─ release-notes

  src/pr_guardian/                        .claude/
    ├─ diff_parser.py                       ├─ agents/    4 scoped review subagents
    ├─ git_utils.py                         ├─ skills/    security-finding, changelog-entry
    ├─ reviews/     4 review passes         ├─ commands/  the slash commands above
    ├─ changelog.py                         ├─ hooks/     guard_bash · format_on_save · summary
    └─ output.py                            └─ CLAUDE.md  session context + conventions

                                          MCP:  .mcp.json → filesystem server tools

  both layers emit the SAME finding shape:
  category · severity · file:line · explanation · suggested fix
  • Layer 1 — deterministic engine (src/pr_guardian/): pure Python, no API keys, no network. It parses git history and unified diffs, runs static analyzers for each review category, and renders Rich terminal reports. Fully covered by unit + end-to-end tests.
  • Layer 2 — Claude Code AI layer (.claude/): the richer version of the same categories, run by Claude. Each category is a scoped subagent; findings are formatted by reusable skills; slash commands orchestrate the pipeline; hooks gate destructive commands and auto-format on save; an MCP server gives Claude filesystem tools; CLAUDE.md tells Claude how to behave in this repo.

Why two layers? You get the best of both: the CLI is instant, offline, and testable (great for CI), while the AI layer understands intent, reads surrounding context, and can fix issues — not just report them. And because both layers speak the same "finding shape," a human can move between a pr-guardian scan report and a /security-scan report without relearning the format.


Quick start

Prerequisites

  • Python 3.11+ and git on your PATH
  • Claude Code (for the AI layer)
  • Node.js + npx (only for the MCP server — see MCP)

1. Install

python -m pip install -e ".[dev]"

2. Try the deterministic CLI against a throwaway demo repo that is deliberately full of issues:

python tests/fixtures/build_demo_repo.py demo-repo-build/
cd demo-repo-build
python -m pr_guardian.cli review main      # or `pr-guardian review main` once installed
python -m pr_guardian.cli release-notes v0.1.0 v0.2.0
cd ..

You'll see FAIL verdicts on every category — that is the point. Each finding is a real, deliberate bug in the demo app.

3. Try the Claude Code AI layer — from inside this repo, ask Claude Code:

  • /review — full four-pass review of the current diff
  • /security-scan — fast security-only pass
  • /release-notes — release notes from a commit range

4. Try MCP — see the MCP section below for the one-time approval step, then just ask Claude: "list the files in this project with the filesystem MCP server."


Setting up on a new machine

PR Guardian is designed so a fresh clone works with as little setup as possible. How much you need depends on which layer you want:

You want to…What you needExtra step
Use the CLI and run the testsPython 3.11+ and gitnone — works immediately
Run the AI review passesClaude Code, logged instart claude from repo root
Use the MCP filesystem toolsNode.js + npx (network once)approve the server on first run

1. Clone and install

git clone <your-repo-url>
cd pr-guardian
python -m pip install -e ".[dev]"

2. Verify the deterministic layer — no keys, no network, no extra config:

pytest               # full suite (engine + CLI end-to-end)
ruff check .         # lint clean
pr-guardian --help   # CLI installed as a `pr-guardian` command

3. The AI layer runs inside Claude Code. The .claude/ agents, commands, skills, and hooks ship with the repo, but they only execute when you're in a Claude Code session — and they run on your Claude Code account, not the repo author's. Install Claude Code, log in, then start a session from the repo root (the hooks use project-relative paths):

claude
/review

On your first session Claude will ask you to approve the filesystem MCP server — this is the standard security prompt for a project-scoped server, and it can only read/write this project directory. First use downloads the server via npx, so you need network once.

4. CI needs nothing from you. .github/workflows/ci.yml runs the same ruff + pytest checks automatically on every push and PR.


Feature-by-feature tour

For each feature: what it is → where it lives → what this project does with it → try it.

CLAUDE.md

What it is. A project-instructions file that Claude Code reads at session start and treats as standing context. It's how you tell Claude who you are, what the project is, how code should be written, and what not to touch — no need to re-explain in every prompt.

Where it lives. CLAUDE.md at the repo root.

What this project does with it. It defines the whole project: the two-layer architecture, the directory layout, the enforced Python conventions (type hints, docstrings, no bare except:, line length 100 — which ruff also enforces in CI and the style-reviewer subagent enforces in the AI layer), and how to run everything.

Try it. Open CLAUDE.md and note how style-reviewer reads it before reviewing — a subagent that pulls its rules from the same file humans read is the reason conventions stay consistent. Ask Claude Code "what are the coding conventions here?" and watch it answer from this file.

Agents

What they are. Scoped, reusable Claude sessions with a specific role, narrowed tools, and a focused system prompt. Agents keep each task small, safe, and parallelizable — instead of one giant Claude doing everything, you dispatch specialists.

Where they live. .claude/agents/*.md. Each file's frontmatter declares the agent's name, description (when Claude auto-selects it), and the tools it may use.

What this project does with it. Four review subagents, one per pass:

AgentJobTools
security-reviewersecrets, eval/exec, shell=True, SQL injection, unsafe deserialization, missing validationRead, Glob, Grep
test-coverage-reviewerchanged code vs. its test file; flags untested branchesRead, Glob, Grep
style-reviewerCLAUDE.md conventions (type hints, docstrings, bare excepts, naming)Read, Glob, Grep
docs-writermissing docstrings / README gaps on changed public API — may editRead, Glob, Grep, Edit, Write

/review dispatches all four in parallel and consolidates their findings. Note the intentional tool restriction: three agents are read-only (a reviewer should report, not modify), while docs-writer gets Edit/Write because its job is to fix documentation.

Try it. Run /review and watch four agents fan out. Or ask directly: "use the security-reviewer agent on the current diff."

Slash commands

What they are. Reusable prompt templates you invoke with /name. They turn a one-word command into a precise, multi-step instruction — so a routine task always runs the same way.

Where they live. .claude/commands/*.md (the filename without .md is the command name; the frontmatter description shows in the command picker).

What this project does with it.

CommandWhat it doesLayers used
/reviewFinds the base branch, captures the diff, dispatches all four review subagents in parallel, consolidates into a verdict reportagents, skills, (docs-writer may edit)
/security-scanSecurity-only fast passsecurity-reviewer + security-finding skill
/release-notesResolves a commit range, formats each commit via a skill, assembles a Keep-a-Changelog docchangelog-entry skill

Try it. Run /review now. Then open .claude/commands/review.md and see exactly what steps Claude follows.

Skills

What they are. Reusable procedures — packaged instructions for a repeatable piece of work. A skill is invoked explicitly by Claude when the task calls for it, and can be shared across commands and projects. Think: "how to do X well," not "do X once."

Where they live. .claude/skills/<name>/SKILL.md.

What this project does with it. Two skills that keep output consistent across the AI layer:

  • security-finding — turns any flagged security issue into the standard finding shape (severity, location, category, explanation, suggested fix) with a severity rubric. Every agent that reports a security issue formats it through this skill, so a /review finding and a pr-guardian scan finding read identically.
  • changelog-entry — turns one commit (message + diff) into exactly one Keep-a-Changelog line: parses conventional-commit prefixes, handles breaking changes, writes from behavior not code. /release-notes invokes it once per commit.

Try it. Run /release-notes, then compare a line Claude produced with the skill's documented output format — they match, because the skill drove it.

Hooks

What they are. Scripts Claude Code runs automatically around tool calls and session events. This is the feature that makes behavior deterministic: hooks fire on real events (before a tool runs, after it runs, on stop), regardless of what Claude "feels" like doing. Configured in .claude/settings.json.

Where they live. .claude/settings.json (config) + .claude/hooks/*.py (the scripts). Each hook reads a JSON payload from stdin and writes a JSON response to stdout.

What this project does with it. Three hooks, one per lifecycle stage:

EventHookBehavior
PreToolUse (Bash)guard_bash.pyDetects destructive commands (rm -rf, git push --force, git reset --hard, git clean -f, …) and routes them through the permission prompt with a warning instead of letting them run blindly. Fail-open: safe commands pass straight through.
PostToolUse (Edit/Write)format_on_save.pyRuns ruff format + ruff check --fix on every Python file Claude edits, and reports back what changed. Fail-open: missing ruff, non-Python files, or bad payloads are no-ops.
Stopsummary.pyTails the session transcript and prints a short "✓ Done: …" summary of the last thing Claude said.

Try it. Make a trivial edit to any Python file and watch the auto-format hook report what it reformatted. Then try to convince Claude to run git reset --hard and watch guard_bash intercept it. (Note: hook commands use project-relative paths, so launch Claude Code from the repo root.)

MCP

What it is. Model Context Protocol — a standard way to plug external tools and data into Claude. An MCP server exposes a set of tools (plus optionally resources and prompts) over a simple protocol; Claude Code discovers them and calls them like any other tool. It's how Claude gets access to things it can't do natively — in this project, filesystem operations via a well-known community server.

Where it lives. MCP servers are registered in .mcp.json at the project root. This file is read by Claude Code at session start, so after editing it you must start a new session.

What this project does with it. We connect the official reference filesystem server, scoped to this project directory:

{
  "mcpServers": {
    "filesystem": {
      "type": "stdio",
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "C:/Claude_Projects/pr-guardian"
      ]
    }
  }
}
  • type: "stdio" — the server speaks over standard input/output (the default for local servers).
  • command: "npx" + -y — Node's package runner downloads and launches the server (first run needs network; later runs are instant).
  • The last argument is an allowlist of directories the server may touch. We scope it to the repo root so Claude can read and write files inside the project only.

Once connected, Claude gains tools named mcp__filesystem__*, including: read_file, write_file, edit_file, list_directory, directory_tree, search_files, create_directory, move_file, and more.

Try it.

  1. Verify the server is registered — from a terminal in this repo:
    claude mcp list          # should list "filesystem"
    claude mcp get filesystem
    
    The first time a session sees a project-scoped server it asks you to approve it (a cloned repo must never silently launch processes on your machine). If you declined earlier, reset with claude mcp reset-project-choices.
  2. Inside a Claude Code session, the /mcp panel shows connected servers and their tool counts.
  3. Use it — ask: "Use the filesystem MCP server to list the top-level files in this project." Claude will call mcp__filesystem__list_directory and show you the tree.

Gotchas. MCP tools only appear after the session that reads .mcp.json is started — restart after editing the file. If npx isn't found, install Node.js or point command at its full path. The server can only reach the directories you listed, so it's safe by construction.


How the two layers mirror each other

One idea drives the whole repo: a finding from the CLI and a finding from Claude should be readable the same way. The table shows the mirrors:

ConceptDeterministic engine (src/pr_guardian/)Claude Code layer (.claude/)
Review commandpr-guardian review/review
Security passreviews/security.pyagents/security-reviewer.md + skills/security-finding
Style passreviews/style.pyagents/style-reviewer.md
Test-coverage passreviews/test_coverage.pyagents/test-coverage-reviewer.md
Docs passreviews/docs.pyagents/docs-writer.md
Release notespr-guardian release-notes/release-notes + skills/changelog-entry
Finding shapeFinding dataclass (reviews/base.py)security-finding skill's YAML fields
Conventionsruff + CLAUDE.mdstyle-reviewer (reads CLAUDE.md)

End-to-end flows

Deterministic review (CLI)

pr-guardian review main
        │
        ▼
  discover repo (git) ──► git diff main...HEAD ──► parse unified diff (diff_parser.py)
        │
        ▼
  run 4 reviewers over the added lines (reviews/)
   ├─ security        (regex + SQL-injection heuristics)
   ├─ test-coverage   (does the test file exist / changed?)
   ├─ style           (AST: type hints, bare excepts, naming)
   └─ docs            (AST: docstrings on new public API)
        │
        ▼
  render verdict table + findings (Rich) ──► exit code 0 (pass) / 1 (fail)

AI review (Claude Code)

/review
   │
   ▼
find base branch ──► git diff <base>...HEAD ──► list changed files
   │
   ▼
dispatch 4 subagents in PARALLEL
   ├─ security-reviewer        findings formatted with the security-finding skill
   ├─ test-coverage-reviewer
   ├─ style-reviewer           reads CLAUDE.md for the conventions
   └─ docs-writer              may Edit docstrings/README — surface edits for approval
   │
   ▼
consolidate into one verdict table (PASS/FAIL per category) + findings

Release notes (both layers)

CLI  (changelog.py)                 AI  (/release-notes)
  classify_commit()                   changelog-entry skill, once per commit
  feat→Added · fix→Fixed              parses conventional-commit prefixes
  refactor!→Changed (BREAKING)        handles breaking changes
  docs/chore→skipped                  writes from behavior, not code
         │                                     │
         └───────────────► assemble the same Keep-a-Changelog layout:
                              ## [version] - date
                              ### Added  /  ### Changed  /  ### Fixed

MCP flow

.mcp.json  (project root — read by Claude Code at session start)
   │
   ▼
Claude Code spawns:  npx @modelcontextprotocol/server-filesystem <dir>
   │
   ▼
Claude gains tools named  mcp__filesystem__*
   │   read_file · write_file · edit_file · list_directory
   │   directory_tree · search_files · create_directory · move_file
   │
   ▼
you ask "list the project files" ──► Claude calls a tool ──►
   server reads the allowlisted directory ──► result shown to you

Project layout

pr-guardian/
├── CLAUDE.md                  # session context for Claude Code (this is a feature!)
├── README.md                  # you are here
├── pyproject.toml             # packaging + ruff + pytest config
├── .mcp.json                  # MCP server registration (the MCP feature)
├── .gitignore
├── .claude/                   # the Claude Code AI layer
│   ├── settings.json          # hooks config (PreToolUse / PostToolUse / Stop)
│   ├── agents/                # 4 scoped review subagents
│   ├── skills/                # security-finding, changelog-entry
│   ├── commands/              # /review, /security-scan, /release-notes
│   └── hooks/                 # guard_bash.py, format_on_save.py, summary.py
├── src/pr_guardian/           # the deterministic engine
│   ├── cli.py                 # Typer app: review, scan, release-notes
│   ├── diff_parser.py         # unified diff → structured FileChange/Hunk/Line
│   ├── git_utils.py           # GitPython: diff + commit history extraction
│   ├── changelog.py           # commits → categorized changelog
│   ├── output.py              # Rich rendering of reports
│   └── reviews/               # one analyzer module per review category
│       ├── base.py            # Reviewer protocol + Finding dataclass
│       ├── security.py / style.py / test_coverage.py / docs.py
└── tests/
    ├── conftest.py            # demo_repo fixture (builds real git history)
    ├── test_*.py              # unit + CLI end-to-end tests
    └── fixtures/
        ├── demo_repo/         # final-state working tree (deliberate issues)
        └── build_demo_repo.py # materializes history, tags, a feature branch

Usage reference

CLI

pr-guardian review [BASE]          # full four-pass review vs BASE (default: main)
pr-guardian scan [BASE]            # security-only pass
pr-guardian release-notes FROM TO  # Keep-a-Changelog from a commit range
pr-guardian --help                 # all commands/options

Options: review --categories security,style limits passes; release-notes --date YYYY-MM-DD sets the release date. Exit code is 0 when no ERROR-level findings, 1 when there are — CI-friendly.

Slash commands (Claude Code)

/review · /security-scan · /release-notes

MCP tools (Claude Code)

mcp__filesystem__* — read_file, write_file, edit_file, list_directory, directory_tree, search_files, create_directory, move_file, …


The demo repository

tests/fixtures/build_demo_repo.py materializes a throwaway git repo — real commits, two tags (v0.1.0, v0.2.0), and a feature/ai-review branch whose diff is deliberately full of issues. It is the review pipeline's own test data: this is exactly the kind of code a reviewer is supposed to catch.

Demo fileDeliberate issuePass that flags it
auth.pyhardcoded API key + eval()security
review_pipeline.pysubprocess.run(..., shell=True), bare except:, missing type hints/docstringssecurity, style, docs
slack.pyhardcoded webhook URL, no tests, missing hints/docstringssecurity, test-coverage, style, docs
db.pySQL built by f-string interpolationsecurity

The fixture tree is excluded from ruff and pytest (pyproject.toml) — linting it would fail by design. A nested .git isn't committed (git treats it as an embedded repo), so the repo ships the final-state working tree (tests/fixtures/demo_repo/) plus the builder script that recreates the full history on demand.


Testing

python -m pytest            # 35 tests: unit + CLI end-to-end (green)
python -m ruff check .      # lint (src + tests) — green
python -m ruff format --check .   # formatting — green

Tests cover the diff parser, changelog categorization, each reviewer pass, and the full CLI via typer.testing.CliRunner against the real built demo repo (the demo_repo fixture in tests/conftest.py).


Troubleshooting

SymptomFix
MCP server shows ⏸ Pending approvalApprove it in your session, or claude mcp reset-project-choices and re-approve
MCP server shows ✘ Failed to connectclaude mcp get filesystem for the failure detail; make sure npx is on PATH and you have network on first run
MCP tools not visible.mcp.json is read at session start — restart Claude Code after editing it
spawn ... ENOENT for MCP/hooksThe command isn't on PATH — use the full path (e.g. .venv\Scripts\python.exe)
Hook errors mention a subdirectoryHook commands use project-relative paths — launch Claude Code from the repo root
pr-guardian: command not foundThe console script is created by pip install -e ".[dev]"; or run python -m pr_guardian.cli
python opens the Microsoft StoreUse the full path to your Python, or py -3 -m ...

Further reading

  • Low-level design (LLD) — this README is the what/how; the LLD is the how it works: data structures, control flow, the AI-layer contract, and the testing strategy
  • Claude Code docs — CLAUDE.md, agents, hooks, slash commands, skills
  • Claude Code MCP — .mcp.json, approval flow, troubleshooting
  • MCP: build a server — the official MCPServer walkthrough (a great next step: turn PR Guardian's engine into custom MCP tools)
  • Keep a Changelog — the release-notes format
↑↓ navigate openesc close