# Agents: the agent SDK, managed agents, and scheduling
An agent is just Claude in a loop with tools, running until a task is actually done. That single idea is the whole lesson. A normal APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition → call gives Claude one turn to respond; an agent gives Claude the ability to call a tool, see the result, decide what to do next, call another tool, and keep going until it judges the work complete or you stop it.
You already know the pieces (tool use, MCP, the Messages APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition →). This lesson is about wiring them into something that runs autonomously, and about the production concern nobody mentions in tutorials: how to make an agent run on a schedule without you babysitting it.
ReachReachThe number of unique people exposed to your message in a given period. Unlike impressions, reach counts each person once, no matter how often they see it.View full definition → for a plain Messages APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition → call when the task is one-shot and self-contained: summarize this text, classify this ticket, draft this email. One input, one output, done.
ReachReachThe number of unique people exposed to your message in a given period. Unlike impressions, reach counts each person once, no matter how often they see it.View full definition → for an agent when the task requires discovery, iteration, or branching that you cannot script in advance. The tell is when you cannot write down the exact sequence of steps ahead of time because the steps depend on what Claude finds along the way.
Concrete examples where an agent earns its keep:
If you find yourself writing a giant if/else tree around model calls, you probably want an agent. If a single prompt with good context does the job, do not over-engineer it.
The Claude Agent SDK is the official toolkit for building agents on the same harness that powers Claude Code. It handles the loop for you: it manages the conversation, exposes tools to Claude, executes the tool calls Claude requests, feeds results back, and repeats until completion.
The key mental shift: with the raw Messages APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition → you write the loop yourself (call, check for tool_use, run the tool, append the result, call again). The Agent SDK owns that loop so you focus on what tools the agent has and what you want done.
Three things the SDK gives you out of the box:
It comes in both TypeScript and Python. Because it shares a foundation with Claude Code, an agent you build with it can read files, run commands, and edit code with the same competence you have seen in Claude Code, but pointed at *your* task instead of an interactive terminal session.
There are two ways to operate an agent, and the distinction matters for scheduling.
Self-hosted: you run the Agent SDK in your own process, on your own machine, container, or serverless function. You own the runtime, the secrets, and the schedule. Maximum control, more ops work.
Managed agents: Anthropic runs the agent loop on their infrastructure. You define the agent (its tools, instructions, and permissions) and Anthropic executes it, handling the orchestration, retries, and scaling. You hand off the operational burden.
The practical guidance: prototype self-hosted because the feedback loop is fast and you can print everything. Move to managed agents when you need reliability, isolation, and you would rather not maintain a server. For scheduling specifically, a managed agent paired with a cron-style trigger removes almost all the moving parts.
> A note on terms: an agent here is a configured Claude-plus-tools loop. A connector (from the Claude apps and the connector marketplace) is a packaged integration that exposes a service to Claude over MCP. A Skill is a reusable, packaged capability. Agents can *use* connectors and Skills as their tools.
Let's build the canonical case: an agent that runs every night, pulls yesterday's numbers, writes a short report, and posts it to Slack.
Why an agent and not a script? Because "write a short report" is exactly the fuzzy, judgment-heavy step that a script cannot do and a single APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition → call cannot reliably *gather the data for*. The agent queries the database (possibly several times depending on what it sees), interprets the numbers, decides what is worth highlighting, and formats the message.
Here is a clean Python sketch using the Agent SDK. The agent gets two tools (a database query tool and a Slack post tool) and one instruction. Note how little orchestration code there is: the loop lives inside query.
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, tool, create_sdk_mcp_server
@tool("run_sql", "Run a read-only SQL query against the analytics DB", {"sql": str})
async def run_sql(args):
rows = await analytics_db.fetch(args["sql"])
return {"content": [{"type": "text", "text": str(rows)}]}
@tool("post_to_slack", "Post a message to the #metrics channel", {"text": str})
async def post_to_slack(args):
await slack.post(channel="#metrics", text=args["text"])
return {"content": [{"type": "text", "text": "posted"}]}
tools = create_sdk_mcp_server(name="report-tools", tools=[run_sql, post_to_slack])
PROMPT = """Pull yesterday's signups, revenue, and active users from the
analytics DB. Compare each to the prior day. Write a 5-line report that
leads with anything unusual, then post it to Slack."""
async def main():
options = ClaudeAgentOptions(
mcp_servers={"report": tools},
allowed_tools=["mcp__report__run_sql", "mcp__report__post_to_slack"],
system_prompt="You are a careful analytics assistant. Query before you conclude.",
)
async for message in query(prompt=PROMPT, options=options):
print(message)
asyncio.run(main())A few things worth noticing:
allowed_tools is your guardrail. The SQLSQLSales Qualified Lead: a prospect the sales team has validated as ready for direct outreach and a proposal, having passed clear qualification criteria.View full definition → tool is read-only by construction; the agent literally cannot drop a table because you never gave it the ability to.run_sql calls it needs. If signups look weird, it can dig deeper before writing the report.Knowledge check
1. According to the lesson, what is the core definition of an agent?
2. Which signal does the lesson give as the clearest indication that you should reach for an agent rather than a single Messages API call?
3. The lesson notes the Claude Agent SDK runs on the same harness that powers which tool?
4. Select ALL correct answers about tasks that are good fits for an agent according to the lesson.
Sélectionnez toutes les réponses correctes.
5. Select ALL correct answers about how an agent differs from a normal single API call, per the lesson.
Sélectionnez toutes les réponses correctes.
An agent that you trigger by hand is a demo. An agent that runs every night at 2am while you sleep is a product. Scheduling is the difference.
There is no magic here, and that is good news: an agent run is just a process you can invoke. Pick the trigger mechanism that matches where the agent lives.
Option 1: OS cron (self-hosted). If the agent runs on a server you control, plain cron is fine. One line in your crontab:
0 2 * * * cd /opt/nightly-report && /usr/bin/python3 agent.py >> /var/log/report.log 2>&1That runs agent.py at 02:00 every day and appends output to a log. Simple, durable, and you can read the log when something looks off.
Option 2: A serverless scheduled function. A cloud scheduler (a scheduled Lambda, a Cloud Run job, a GitHub Actions schedule trigger) invokes the agent on a cron expression without you keeping a server alive. You pay only when it runs. This is the sweet spot for most nightly jobs.
Here is the GitHub Actions version, which is genuinely the easiest if your code already lives in a repo:
name: nightly-report
on:
schedule:
- cron: "0 2 * * *"
workflow_dispatch: {} # lets you trigger it manually too
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "3.12" }
- run: pip install claude-agent-sdk
- run: python agent.py
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}The workflow_dispatch line is a small but important habit: it gives you a manual "run now" button so you can test without waiting until 2am. Store your key as a repository secret, never in the file.
Option 3: Managed agents with a schedule. When you run the agent on Anthropic's managed infrastructure, you attach a schedule to the agent definition rather than wiring up your own cron. The agent loop, retries, and execution environment are handled for you; you provide the cadence and the trigger. Check the current Agent SDK docs for the exact configuration, since the managed surface is evolving quickly through 2025 and 2026.
A scheduled agent is autonomous, which raises three concerns a one-off call never had. Design for them from day one.
Idempotency and partial failure. If the agent posts to Slack but the run crashes afterward, will tomorrow's run double-post? For a report this is harmless, but for anything that writes data, make tool actions safe to retry or check whether the work was already done before doing it.
Observability. You are not watching. Log every tool call and the final outcome, and have the agent itself signal success or failure in a place you will see (a Slack thread, a status row). If the nightly report stops appearing, that absence should be loud.
Cost and runaway loops. An agent in a loop can, in pathological cases, call tools far more than you expect. Set a turn or budget limit so a confused agent stops instead of running for an hour. The Agent SDK lets you cap this; use it.
Least privilege. This is worth repeating because it is the cheapest insurance you have. The nightly agent gets a read-only database role and a single Slack channel. Nothing more. An unattended agent should never hold permissions it does not strictly need for its one job.
schedule for most cases, or a schedule attached directly to a managed agent.workflow_dispatch, a "run now" path) so you can test the agent without waiting for the schedule to fire.