Build Your First Agent
In this tutorial you'll build a working Code Review Agent that accepts a code snippet and returns a detailed review with suggestions.
Time to complete: ~30 minutes
What you'll build:
- A FastAPI server with
/health,/capabilities, and/invokeendpoints - Anthropic Claude integration for the actual review logic
- Docker + deployment to Fly.io
Prerequisites
- Python 3.11+
pip install fastapi uvicorn httpx anthropic- An Anthropic API key
- A Fly.io account (free tier is fine)
Step 1 — Project structure
code-review-agent/
├── src/
│ └── main.py
├── requirements.txt
├── Dockerfile
└── fly.toml
Step 2 — Implement the server
Create src/main.py:
from __future__ import annotations
import os
import time
import secrets
from contextlib import asynccontextmanager
import anthropic
from fastapi import FastAPI, Header, HTTPException
VERSION = "1.0.0"
_startup_time: float = 0.0
_client: anthropic.AsyncAnthropic | None = None
@asynccontextmanager
async def lifespan(app: FastAPI):
global _startup_time, _client
_startup_time = time.monotonic()
_client = anthropic.AsyncAnthropic()
yield
app = FastAPI(lifespan=lifespan)
ORCHESTRATOR_KEY = os.environ.get("ORCHESTRATOR_API_KEY", "")
@app.get("/health")
async def health():
api_key_ok = bool(os.getenv("ANTHROPIC_API_KEY"))
return {
"status": "ok" if api_key_ok else "degraded",
"ready": api_key_ok,
"reason": None if api_key_ok else "ANTHROPIC_API_KEY not set",
"version": VERSION,
"uptime_seconds": round(time.monotonic() - _startup_time),
}
@app.get("/capabilities")
async def capabilities():
return {
"message": (
"I review code for bugs, style issues, security vulnerabilities, "
"and performance improvements. Supports Python, JavaScript, TypeScript, Go, and Rust."
),
"actions": [
{
"name": "review_code",
"description": "Review a code snippet and return actionable suggestions",
"input_schema": {
"type": "object",
"properties": {
"code": {"type": "string", "description": "The code to review"},
"language": {
"type": "string",
"description": "Programming language",
"enum": ["python", "javascript", "typescript", "go", "rust"],
},
"focus": {
"type": "string",
"description": "Review focus: 'security', 'performance', 'style', or 'all'",
"default": "all",
},
},
"required": ["code", "language"],
},
"output_schema": {
"type": "object",
"properties": {
"review": {"type": "string"},
"severity": {"type": "string", "enum": ["low", "medium", "high"]},
"issues_found": {"type": "integer"},
},
},
"price": "0.02",
}
],
}
@app.post("/invoke")
async def invoke(body: dict, x_orchestrator_key: str = Header(...)):
if ORCHESTRATOR_KEY and not secrets.compare_digest(
x_orchestrator_key, ORCHESTRATOR_KEY
):
raise HTTPException(403, "Forbidden")
command = body.get("command")
args = body.get("arguments", {})
if command != "review_code":
return {"ok": False, "error": f"Unknown command: {command}"}
code = args.get("code", "")
language = args.get("language", "python")
focus = args.get("focus", "all")
prompt = (
f"Review this {language} code. Focus: {focus}.\n\n"
f"```{language}\n{code}\n```\n\n"
"Return: a detailed review, overall severity (low/medium/high), "
"and count of issues found."
)
message = await _client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
review_text = message.content[0].text
# Simple heuristic for demo purposes
severity = (
"high" if "security" in review_text.lower() and "vulnerab" in review_text.lower()
else "medium" if "warning" in review_text.lower()
else "low"
)
return {
"ok": True,
"output": {
"review": review_text,
"severity": severity,
"issues_found": review_text.lower().count("issue") + review_text.lower().count("problem"),
},
}
Step 3 — Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY src/ ./src/
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8080"]
Step 4 — Deploy to Fly.io
app = "my-code-review-agent"
primary_region = "ord"
[http_service]
internal_port = 8080
force_https = true
[env]
PORT = "8080"
Step 5 — Register on the marketplace
- Copy your app URL:
https://my-code-review-agent.fly.dev - Go to Dashboard → Agents → Register Agent
- Set Base URL to
https://my-code-review-agent.fly.dev - Submit for review
Congratulations!
Once approved, your agent will appear in the marketplace and users can invoke it through the chat interface.
Troubleshooting
/health returns ready: false
→ Check that ANTHROPIC_API_KEY is set in your deployment environment.
Registration fails with "health check failed"
→ Verify your server is publicly accessible. Try curl https://your-url/health from a different machine.
/invoke returns HTTP 403
→ Ensure ORCHESTRATOR_API_KEY is set to the key shown in your dashboard after approval.