Security Considerations
1. API Key Management
ā Bad:
os.environ["ANTHROPICAPIKEY"] = "sk-..." # Hardcoded!ā Good:
from dotenv import loaddotenv
import os
load
dotenv() # Reads from .env file (not in git)
apikey = os.getenv("ANTHROPICAPIKEY")
ā Better (Secrets Manager):
import boto3
client = boto3.client("secretsmanager")
secret = client.getsecretvalue(SecretId="anthropic-key")
apikey = secret["SecretString"]
2. Tool Restrictions
Always restrict tools to what's needed:
ā Bad: All tools available
options = ClaudeAgentOptions(permissionmode="bypassPermissions")
ā Good: Only what's needed
options = ClaudeAgentOptions(
allowedtools=["Read", "Grep"], # Explicit whitelist
disallowedtools=["Bash", "Write", "Edit"], # Explicit blacklist
permissionmode="default"
)
3. Input Validation
Validate user input before passing to agent:
from pydantic import BaseModel, Field
class TaskRequest(BaseModel):
prompt: str = Field(..., maxlength=1000) # Limit prompt size
maxbudget: float = Field(default=1.0, le=10.0) # Max $10
timeout: int = Field(default=300, le=3600) # Max 1 hour
Validate request
try:
request = TaskRequest(userinput)
except ValidationError as e:
return {"error": str(e)}
Safe to use
async for message in query(
prompt=request.prompt,
options={"maxbudgetusd": request.maxbudget}
):
pass
4. Rate Limiting
Prevent abuse with rate limiting:
from datetime import datetime, timedelta
from collections import defaultdict
usercalls = defaultdict(list)
maxcallsperhour = 10
def checkratelimit(userid: str) -> bool:
now = datetime.now()
hourago = now - timedelta(hours=1)
# Clean old calls
usercalls[userid] = [
t for t in usercalls[userid]
if t > hourago
]
if len(usercalls[userid]) >= maxcallsperhour:
return False
usercalls[userid].append(now)
return True
Use in handler
if not checkratelimit(userid):
return {"error": "Rate limit exceeded"}
---
Sandboxing
For untrusted prompts, use sandboxing:
Vercel Sandbox
from vercelsdk import Sandbox
Execute agent code in isolation
sandbox = Sandbox()
result = await sandbox.run("""
from claudeagentsdk import query
async for message in query(prompt="Read /etc/passwd"):
# This fails! No access to real filesystem
pass
""")
Container-Based Sandboxing
Restrictive container
FROM python:3.12-slim
No sudo, no package manager
RUN rm /bin/sudo /usr/bin/apt-get
Read-only root filesystem
Limited CPU/memory
No network access except API
COPY agent.py .
CMD ["python", "agent.py"]
Run with restrictions:
docker run \
--read-only \
--cap-drop=ALL \
--memory=512m \
--cpus=1 \
--network=none \
my-agent-image
---
Monitoring & Observability
Log All Operations
import logging
from datetime import datetime
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(name)
async def monitoredquery(prompt, userid):
starttime = datetime.now()
logger.info(f"Agent task started: user={userid}, promptlength={len(prompt)}")
messagecount = 0
finalresult = None
try:
async for message in query(prompt=prompt):
messagecount += 1
if isinstance(message, ResultMessage):
finalresult = message.result
elapsed = (datetime.now() - starttime).totalseconds()
logger.info(
f"Agent task completed: "
f"user={userid}, "
f"messages={messagecount}, "
f"duration={elapsed}s, "
f"cost=${message.cost['input'] + message.cost['output']:.6f}"
)
except Exception as e:
logger.error(f"Agent task failed: user={userid}, error={str(e)}")
raise
return finalresult
Metrics to Track
from prometheusclient import Counter, Histogram, Gauge
Counters
taskcount = Counter("agenttaskstotal", "Total tasks", ["status"])
toolcalls = Counter("toolcallstotal", "Total tool calls", ["tool"])
Histograms
taskduration = Histogram("agenttaskdurationseconds", "Task duration")
taskcost = Histogram("agenttaskcostusd", "Task cost in USD")
Gauges
activeagents = Gauge("activeagents", "Currently running agents")
Example: Prometheus Export
@app.get("/metrics")
async def metrics():
# Prometheus scrapes this endpoint
return generatelatest()
---
Real-World Case Studies
Case Study 1: Codebase Analyzer
Problem: Understand new unfamiliar codebase
Solution: Multi-agent system
agents = {
"structure": AgentDefinition(
description="Map file structure and dependencies",
tools=["Glob", "Grep", "Read"]
),
"patterns": AgentDefinition(
description="Identify design patterns and conventions",
tools=["Grep", "Read", "WebSearch"]
),
"entrypoints": AgentDefinition(
description="Find main entry points and flows",
tools=["Grep", "Read", "Bash"]
)
}
async for message in query(
prompt="Analyze this codebase and provide an overview for a new developer",
options={"agents": agents}
):
# Generates comprehensive analysis
pass
Result: 2-minute analysis vs 2 days manual exploration
Case Study 2: Continuous Testing
Problem: Run tests after every commit
Solution: Agent validates changes
async def validatepullrequest(prnumber):
async for message in query(
prompt=f"""Review PR #{pr
number}:
1. Check all tests pass
2. Verify code style
3. Check for regressions
4. Ensure documentation updated
""",
options={
"allowedtools": ["Bash", "Read", "Grep"],
"maxbudgetusd": 0.50 # Cost limit
}
):
if isinstance(message, ResultMessage):
# Auto-comment on PR with results
postprcomment(prnumber, message.result)
Result: Instant PR feedback, no human reviewer needed for mechanical checks
Case Study 3: Code Refactoring
Problem: Large refactoring project (1000+ files)
Solution: Team of agents
Create team for Stripe ā PayPal migration
teams = [
("Researcher", "Find all Stripe usages"),
("Designer", "Design PayPal integration"),
("Migrator", "Update code to use PayPal"),
("Tester", "Write tests for new code"),
("Validator", "Verify no regressions")
]
Each agent works in parallel
Results come back to lead for synthesis
Result: 3-week refactoring in 3 days
---
Common Pitfalls
| Pitfall | Solution | |
|---|---|---|
| ---------- | ||
| Hardcoded API keys | Use environment variables + secrets manager | |
| Too many tools | Whitelist only what's needed | |
| No cost limits | Set maxbudgetusd per request | |
| Ignoring timeouts | Set maxturns limit | |
| No monitoring | Log everything, export metrics | |
| Trusting user input | Validate + sanitize before agent | |
| Context bloat | Use subagents for isolation | |
| No error handling | Catch exceptions, return user-friendly errors |
---
Deployment Checklist
- [ ] API keys in secrets manager (not hardcoded)
---
Summary
Deploying agents to production requires:
---
Congratulations! š
You've completed all 8 modules and learned:
ā What agents are and how they work
ā Claude Code CLI agents and subagents
ā Building agents with the Agent SDK
ā Tools and tool use patterns
ā Advanced patterns (hooks, streaming, cost tracking)
ā Specialized agents and subagents
ā Multi-agent orchestration and coordination
ā Deploying agents to production
Next steps:**
Welcome to the agent-building community! š