This tutorial walks through setting up an AI agent to produce structured code reviews that render as formatted PR comments via the Agent Message Protocol (AMP).
By the end, you'll have:
- Agent instructions that produce AMP JSON
- A GitHub Actions workflow that collects and renders agent output
- Formatted PR comments with severity badges, file links, and diff suggestions
Prerequisites
- A GitHub repository
- An AI coding agent: Claude Code, GitHub Copilot, or Cursor
- The Kida GitHub Action (
lbliii/kida)
Step 1: Add agent instructions
Agent instruction files tell your AI agent how to produce AMP-formatted output. Each agent reads instructions from a different location.
Claude
Create.claude/CLAUDE.md(or add to your existing file):
When performing code reviews, wrap findings in a JSON code block tagged `amp:code-review`:
~~~
```amp:code-review
{
"agent": "Claude",
"model": "<your model>",
"confidence": 0.85,
"summary": "One-line review summary.",
"findings": [
{
"file": "path/to/file.py",
"line": 42,
"severity": "warn",
"category": "security",
"title": "Short finding title",
"message": "Detailed explanation.",
"suggestion": "How to fix.",
"diff": "- old\n+ new",
"confidence": 0.9
}
],
"stats": {
"files_reviewed": 5,
"findings_count": 3,
"by_severity": {"error": 0, "warn": 2, "info": 1, "suggestion": 0}
}
}
```
A full instruction file is available at `examples/amp/agent-instructions/CLAUDE.md` covering code-review, pr-summary, and security-scan message types.
### GitHub Copilot
Create `.github/copilot-instructions.md` with the same JSON schema. Copilot reads this file automatically. See `examples/amp/agent-instructions/copilot-instructions.md`.
### Cursor
Create `.cursorrules` at your repo root. See `examples/amp/agent-instructions/cursorrules.md`.
### Severity guide
All agents should follow the same severity levels:
| Severity | Meaning | When to use |
| --- | --- | --- |
| `error` | Blocking | Security vulnerabilities, data loss, crashes. Must fix. |
| `warn` | Should fix | Bugs, race conditions, missing validation. |
| `info` | FYI | Naming, style, minor improvements. |
| `suggestion` | Optional | Alternative approaches, nice-to-haves. |
Instruct agents to set `confidence` honestly. Findings below 0.5 are auto-collapsed in the rendered output so reviewers can focus on high-signal items.
## Step 2: Create the GitHub Actions workflow
Create `.github/workflows/amp-review.yml`:
```yaml
name: AMP Review
on:
pull_request:
types: [opened, synchronize]
permissions:
contents: read
pull-requests: write
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.14'
- name: Install kida
run: python -m pip install --quiet kida-templates
- name: Collect AMP messages
id: collect
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
mkdir -p .amp/collected
# Source 1: .amp/ directory files
for f in .amp/*.json; do
[ -f "$f" ] || continue
cp "$f" ".amp/collected/$(basename "$f")"
done
# Source 2: PR body amp: code blocks
PR_BODY=$(gh pr view "${{ github.event.pull_request.number }}" \
--json body --jq '.body // ""')
export PR_BODY
if [ -n "$PR_BODY" ]; then
python3 -c "
import re, json, os
body = os.environ['PR_BODY']
for msg_type, content in re.findall(r'\`\`\`amp:(\w[\w-]*)\s*\n(.*?)\`\`\`', body, re.DOTALL):
try:
data = json.loads(content.strip())
path = f'.amp/collected/{msg_type}.json'
if not os.path.exists(path):
json.dump(data, open(path, 'w'), indent=2)
except json.JSONDecodeError:
pass
"
fi
# Count collected messages
shopt -s nullglob
FILES=(.amp/collected/*.json)
echo "count=${#FILES[@]}" >> "$GITHUB_OUTPUT"
- name: Render AMP messages
if: steps.collect.outputs.count != '0'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
declare -A TEMPLATE_MAP=(
["code-review"]="code-review"
["pr-summary"]="pr-summary"
["security-scan"]="security-scan"
["dependency-review"]="dependency-review"
["deploy-preview"]="deploy-preview"
)
PR_NUMBER="${{ github.event.pull_request.number }}"
REPO="${{ github.repository }}"
SHA="${{ github.event.pull_request.head.sha }}"
for f in .amp/collected/*.json; do
[ -f "$f" ] || continue
BASENAME=$(basename "$f" .json)
MSG_TYPE="${BASENAME%-comment}"
TEMPLATE="${TEMPLATE_MAP[$MSG_TYPE]:-}"
[ -z "$TEMPLATE" ] && continue
# Inject repo context
python3 -c "
import json
with open('$f') as fh: data = json.load(fh)
data.setdefault('repository', '$REPO')
data.setdefault('sha', '$SHA')
data.setdefault('pr_number', $PR_NUMBER)
json.dump(data, open('$f', 'w'), indent=2)
"
# Render
OUTPUT=$(kida render "templates/${TEMPLATE}-report.md" \
--data "$f" --mode markdown)
[ -z "$OUTPUT" ] && continue
# Post with deduplication
MARKER="<!-- amp:${MSG_TYPE} -->"
BODY=$(printf '%s\n%s' "$MARKER" "$OUTPUT")
EXISTING_ID=$(gh api \
"repos/${REPO}/issues/${PR_NUMBER}/comments" \
--paginate --jq ".[] | select(.body | startswith(\"$MARKER\")) | .id" \
2>/dev/null | head -1)
if [ -n "$EXISTING_ID" ]; then
gh api "repos/${REPO}/issues/comments/${EXISTING_ID}" \
-X PATCH -f body="$BODY" --silent
else
gh api "repos/${REPO}/issues/${PR_NUMBER}/comments" \
-f body="$BODY" --silent
fi
done
```
### How collection works
The workflow collects AMP messages from two sources, in priority order:
1. **`.amp/` directory** — Agents or CI steps write JSON files here (e.g., `.amp/code-review.json`). Highest priority.
2. **PR body** — Agents like Claude Code can embed `` ```amp:type `` code blocks directly in the PR description.
File-based sources take precedence — if `.amp/code-review.json` exists, a code-review block in the PR body is ignored.
> [!NOTE]
> The full workflow in `.github/workflows/amp-review.yml` also collects from PR comments. The simplified workflow above omits that step for clarity.
### How rendering works
For each collected message:
1. The message type is derived from the filename (e.g., `code-review.json` maps to `code-review`)
2. Repository and SHA context are injected for file permalink generation
3. The matching Kida template renders the JSON to markdown
4. The output is posted as a PR comment with an HTML marker (`<!-- amp:code-review -->`) for deduplication
5. On subsequent pushes, existing comments are updated in place
## Step 3: Test locally
Before pushing, verify rendering locally with the example data:
```bash
# Install kida
pip install kida-templates
# Render a code review
kida render templates/code-review-report.md \
--data examples/amp/code-review.json \
--mode markdown
# Render a PR summary
kida render templates/pr-summary-report.md \
--data examples/amp/pr-summary.json \
--mode markdown
# Render to terminal (with ANSI colors)
kida render templates/code-review-report.md \
--data examples/amp/code-review.json \
--mode terminal
```
## Step 4: Trigger a review
1. Push a branch and open a PR
2. If your agent is configured (e.g., Claude Code in a CI step, or Copilot's auto-review), it produces AMP JSON
3. The `amp-review.yml` workflow collects the JSON and renders it
4. A formatted PR comment appears with severity badges, file permalinks, and diff suggestions
5. On subsequent pushes, the comment updates in place
## Composing multiple agents
When Claude and Copilot both review the same PR, you get separate AMP messages that render as separate PR comments (one per message type, deduplicated by the `<!-- amp:type -->` marker).
The current `kida render` CLI does not expose a `--compose` flag, so composition is handled as an upstream data-preparation step, not at render time.
- **One comment per message type** (default) — The workflow above already deduplicates by type using `<!-- amp:type -->` markers, so each agent's output gets its own auto-updating comment.
- **Threaded or aggregated views** — Merge multiple agent outputs into a single AMP JSON payload in a CI step before passing it to `kida render`. For example, combine Claude and Semgrep security findings into one `security-scan.json`, then render once.
See [[docs/usage/amp#composition|AMP Composition]] for the four composition models defined by the protocol.
## Adding new message types
To support a new AMP message type:
1. **Define the schema** — Create `schemas/amp/v1/my-type.schema.json`
2. **Write a template** — Create `templates/my-type-report.md` using Kida syntax
3. **Update the workflow** — Add the type to the `TEMPLATE_MAP` in `amp-review.yml`
4. **Add agent instructions** — Tell agents how to produce the new JSON format
See [[docs/usage/agent-templates#customizing-templates|Customizing Templates]] for template authoring patterns.
## Next steps
- [[docs/usage/amp|Agent Message Protocol]] — protocol spec, surfaces, and composition
- [[docs/usage/agent-templates|Agent Templates]] — schema details and customization for all built-in templates
- [[docs/usage/github-action|GitHub Action]] — CI report rendering beyond agent messages