# Build a PR reviewer with Agent SDK

You'll build a reviewer that rates each pull request as `trivial`, `moderate`, or `large`, then approves safe changes or asks for a human review.

When it's deployed, you'll manage it in [Deployed Agents](https://cursor.com/dashboard/deployed-agents).

## Build it with a coding agent

Paste this prompt into your coding agent to build the reviewer from this page. The sections after
it walk through the same steps by hand.

```text
Build the PR reviewer from https://cursor.com/docs/agent-sdk/quickstart.md
in ./pr-reviewer. Fetch that page and follow each step in order.

- If agent-sdk isn't on PATH, install it with
  npm install --global @grok/agent-sdk. Wait while I finish agent-sdk login.
- Use the code from the page as written. Ask me for a GitHub repository and
  a pull request I didn't author, and use them in place of acme/checkout#42.
- Run both tool-call checks, and confirm the complexity each one reports.
- Ask me before any step that posts a review on GitHub. Skip the deploy step.

Finish with agent-sdk validate --dir . and the project's TypeScript check,
then tell me what passed.
```

## Prerequisites

You need Node 22.13 or newer and a Cursor login for model turns:

```bash
npm install -g @grok/agent-sdk
agent-sdk login
```

## Create and run the project

```bash
agent-sdk init ./pr-reviewer
cd pr-reviewer
agent-sdk dev
```

In a second terminal, confirm the agent replies:

```bash
agent-sdk run --dir . --message "Introduce yourself in one sentence."
```

## Add review instructions

Replace `bot/instructions.md`:

```md
# PR reviewer

Call `inspect_pr` before you judge a pull request, then act on the
complexity it reports:

- `trivial`: approve with `submit_review` if the diff matches the title
  and nothing looks risky.
- `moderate`: approve only when you understand every patch and see no
  risk. Otherwise request a human review and name the files that worry you.
- `large`: request a human review right away and point the reviewer at
  the biggest files.

Never approve a draft. End with one sentence that gives the verdict and why.
```

## Share the URL parser

Both tools parse a pull request URL, so put the parser in `bot/lib/github.ts`, where Agent SDK doesn't look for tools.

```ts
export function parsePrUrl(prUrl: string) {
  const [owner, repo, pulls, number] = new URL(prUrl).pathname.split("/").filter(Boolean);
  const pull_number = Number.parseInt(number ?? "", 10);
  if (pulls !== "pull" || owner === undefined || repo === undefined || Number.isNaN(pull_number)) {
    throw new Error(`Not a pull request URL: ${prUrl}`);
  }
  return { owner, repo, pull_number };
}
```

## Add the inspect tool

Create `bot/tools/inspect_pr.ts`. It skips patches on a large pull request and caps the rest at 3,000 characters.

```ts
import { defineTool } from "@grok/agent-sdk/tools";
import { z } from "zod";
import { parsePrUrl } from "../lib/github.js";

function rateComplexity(linesChanged: number, changedFiles: number) {
  if (linesChanged <= 25 && changedFiles <= 2) {
    return "trivial";
  }
  if (linesChanged <= 400 && changedFiles <= 15) {
    return "moderate";
  }
  return "large";
}

export default defineTool({
  description: "Fetch a pull request's title, patches, and complexity.",
  inputSchema: z.object({ prUrl: z.string().describe("GitHub pull request URL") }),
  async execute({ prUrl }, ctx) {
    const pull = parsePrUrl(prUrl);
    const octokit = await ctx.host.github.getOctokit();
    const { data: pr } = await octokit.rest.pulls.get(pull);
    const { data: files } = await octokit.rest.pulls.listFiles({ ...pull, per_page: 100 });
    const complexity = rateComplexity(pr.additions + pr.deletions, pr.changed_files);

    return {
      title: pr.title,
      draft: pr.draft ?? false,
      complexity,
      files: files.map((file) => ({
        path: file.filename,
        additions: file.additions,
        deletions: file.deletions,
        patch: complexity === "large" ? undefined : file.patch?.slice(0, 3000),
      })),
    };
  },
});
```

## Verify complexity classification

Call the tool without a model turn:

```bash
# Expect "complexity": "trivial"
agent-sdk tool call inspect_pr --dir . --input '{"prUrl":"https://github.com/facebook/react/pull/35623"}'

# Expect "complexity": "large" and no patch fields
agent-sdk tool call inspect_pr --dir . --input '{"prUrl":"https://github.com/facebook/react/pull/25229"}'
```

## Add the review tool

Create `bot/tools/submit_review.ts`:

```ts
import { defineTool } from "@grok/agent-sdk/tools";
import { z } from "zod";
import { parsePrUrl } from "../lib/github.js";

export default defineTool({
  description: "Approve the pull request, or comment asking for a human review.",
  inputSchema: z.object({
    prUrl: z.string().describe("GitHub pull request URL"),
    verdict: z.enum(["approve", "request_human_review"]),
    summary: z.string().describe("One or two sentences on why."),
  }),
  async execute({ prUrl, verdict, summary }, ctx) {
    const approve = verdict === "approve";
    const octokit = await ctx.host.github.getOctokit();
    await octokit.rest.pulls.createReview({
      ...parsePrUrl(prUrl),
      event: approve ? "APPROVE" : "COMMENT",
      body: approve ? summary : `Human review needed. ${summary}`,
    });
    return { posted: true, verdict };
  },
});
```

You need GitHub write access and a pull request you didn't author.

## Review a pull request

```bash
agent-sdk run --dir . --message "Review https://github.com/acme/checkout/pull/42"
```

Confirm `inspect_pr` runs before `submit_review`, then check the review on GitHub.

## Trigger reviews from GitHub

Create `bot/channels/github.ts`:

```ts
import { defaultGitHubAuth, githubChannel } from "@grok/agent-sdk/channels/github";

const REVIEW_ACTIONS = new Set(["opened", "reopened", "ready_for_review"]);

export default githubChannel({
  botName: "agent-sdk-pr-reviewer",
  cursorAccount: { repos: ["acme/checkout"] },
  onPullRequest: (ctx, pr) => {
    if (!REVIEW_ACTIONS.has(pr.action) || pr.draft) {
      return null;
    }

    return {
      auth: defaultGitHubAuth(ctx),
      title: `Review ${ctx.repository.fullName}#${pr.number}`,
      context: [`Review ${pr.url}.`],
    };
  },
});
```

With `agent-sdk dev` running, replay an open pull request. Pull access is enough:

```bash
agent-sdk github replay https://github.com/acme/checkout/pull/42 --dir . --action opened
```

Open the playground and confirm `Review acme/checkout#42` shows both tool calls and the verdict.

## Deploy the reviewer

Follow [Deployment](https://cursor.com/docs/agent-sdk/deployment.md) to run `agent-sdk deploy`, then manage the reviewer in [Deployed Agents](https://cursor.com/dashboard/deployed-agents).

## Where to go next

- Turn these two PRs into regression checks with [Evals](https://cursor.com/docs/agent-sdk/evals.md).
- Read about typed tools in [Tools](https://cursor.com/docs/agent-sdk/reference/tools.md).
- Set up fixtures and forwarding with the [GitHub guide](https://cursor.com/docs/agent-sdk/guides/github.md).


---

## Sitemap

[Overview of all docs pages](/llms.txt)
