Créer un agent IA en Python : Claude Agent SDK étape par étape
Créer un agent IA qui lit et corrige le code tout seul : on installe Claude Agent SDK, on écrit un premier agent, puis on ajoute un outil maison et MCP.
Cet article dans d’autres langues :العربيةDeutschEnglishEspañolItaliano日本語Polski简体中文
Dans cet article
- Qu’est-ce qu’un agent IA du point de vue d’un développeur ?
- Étape 1 : comment installer Claude Agent SDK ?
- Étape 2 : comment configurer la clé API ?
- Étape 3 : comment écrire son premier agent IA ?
- Étape 4 : comment choisir ce que l’agent peut faire seul ?
- Étape 5 : comment donner à l’agent un outil maison ?
- Étape 6 : comment connecter l’agent à un serveur MCP ?
- Et après le premier agent ?
- À quoi faut-il faire attention ?
- FAQ
Comment créer un agent IA sans écrire toute la boucle à partir de zéro ? Le chemin le plus court que je connaisse, c’est Claude Agent SDK, une bibliothèque pour Python et TypeScript qui nous donne les mêmes outils, la même boucle d’agent et la même gestion du contexte que ceux sur lesquels tourne Claude Code. On écrit une douzaine de lignes de code, et l’agent lit les fichiers, corrige les bugs et lance des commandes tout seul.
Dans ce tutoriel, on construit un premier agent en Python, on règle ses permissions, puis on lui ajoute un outil maison et un serveur MCP. Toutes les commandes et tous les extraits de code viennent du quickstart officiel d’Agent SDK et des pages de documentation que je cite en chemin.
Qu’est-ce qu’un agent IA du point de vue d’un développeur ?
D’après la documentation, un agent est une application qui accomplit une tâche en planifiant elle-même les étapes suivantes et en appelant des outils : elle lit des fichiers, lance des commandes ou modifie du code. J’ai présenté le sujet plus largement, sans code, dans Qu’est-ce qu’un agent IA.
Pour un développeur, l’essentiel, c’est la boucle : le modèle réfléchit, appelle un outil, regarde le résultat et décide de la suite. Agent SDK prend en charge cette boucle, l’exécution des outils, la gestion du contexte et les nouvelles tentatives, et nous, on se contente de lire le flux de messages.
La documentation distingue quatre façons de travailler avec Claude :
- Agent SDK, une bibliothèque qui exécute l’agent Claude Code dans notre application (dans un processus que l’on gère nous-mêmes),
- Claude Code CLI, le terminal pour le travail quotidien et interactif (voir mon tutoriel Claude Code),
- Client SDK, un accès direct à l’API Claude, où c’est nous qui écrivons la boucle d’outils (ou qui la confions au « tool runner », en bêta),
- Managed Agents, un agent hébergé par Anthropic et configuré via l’API.
Ce tutoriel porte sur la première option.
Étape 1 : comment installer Claude Agent SDK ?
Il nous faut Python 3.10+ (ou Node.js 18.14+ pour TypeScript) et un compte Claude Console. On commence par créer le dossier du projet :
mkdir my-agent
cd my-agent
Par défaut, l’agent a accès aux fichiers de ce dossier et de ses sous-dossiers. La documentation recommande le gestionnaire de paquets uv :
uv init
uv add claude-agent-sdk
Avec le pip classique (macOS et Linux), ça donne ceci :
python3 -m venv .venv
source .venv/bin/activate
pip install claude-agent-sdk
Détail intéressant : en général, inutile d’installer Claude Code à part, car les deux SDK embarquent un binaire natif de Claude Code. L’exception, ce sont les plateformes sans paquet précompilé, comme Windows ARM64 (on installe alors Claude Code à la main et le SDK le trouve dans le PATH).
Étape 2 : comment configurer la clé API ?
On crée la clé dans Claude Console et on la définit comme variable d’environnement dans le terminal où l’on lancera l’agent :
export ANTHROPIC_API_KEY=your-api-key
Le SDK ne charge pas les fichiers .env automatiquement : si la clé est dans un .env, il faut la charger soi-même (par exemple avec le paquet dotenv). À la place d’une clé Anthropic, on peut aussi utiliser Amazon Bedrock, Claude Platform on AWS, Google Cloud ou Microsoft Foundry en définissant la variable correspondante, par exemple CLAUDE_CODE_USE_BEDROCK=1.
Une réserve tirée de la documentation : sans accord préalable, Anthropic n’autorise pas les produits construits avec Agent SDK à proposer la connexion avec un compte claude.ai ni à utiliser les limites d’un abonnement. L’agent fonctionne avec une clé API et on paie les tokens (j’ai expliqué comment les suivre à propos du coût de l’API Claude).
Étape 3 : comment écrire son premier agent IA ?
L’exemple du quickstart est un agent qui trouve et corrige des bugs dans le code. On crée d’abord un fichier utils.py avec deux bugs volontaires :
def calculate_average(numbers):
total = 0
for num in numbers:
total += num
return total / len(numbers)
def get_user_name(user):
return user["name"].upper()
calculate_average([]) plantera sur une division par zéro, et get_user_name(None) sur une TypeError. Passons à l’agent lui-même, le fichier agent.py :
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ResultMessage
async def main():
# Agentic loop: streams messages as Claude works
async for message in query(
prompt="Review utils.py for bugs that would cause crashes. Fix any issues you find.",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Glob"], # Auto-approve these tools
permission_mode="acceptEdits", # Auto-approve file edits
),
):
# Print human-readable output
if isinstance(message, AssistantMessage):
for block in message.content:
if hasattr(block, "text"):
print(block.text) # Claude's reasoning
elif hasattr(block, "name"):
print(f"Tool: {block.name}") # Tool being called
elif isinstance(message, ResultMessage):
print(f"Done: {message.subtype}") # Final result
asyncio.run(main())
Le code se compose de trois parties :
querycrée la boucle de l’agent et renvoie un itérateur asynchrone, on lit donc les messages au fil de l’eau avecasync for,prompt, c’est la tâche. Claude décide lui-même quels outils utiliser,options, c’est la configuration : ici,allowed_toolsapprouve d’avanceRead,EditetGlob, etpermission_mode="acceptEdits"accepte automatiquement les modifications de fichiers.
On lance :
uv run agent.py
(ou python agent.py dans un venv activé). L’agent affiche son raisonnement et chaque outil appelé, puis Done: success à la fin. Ensuite, utils.py gère la liste vide et l’absence d’utilisateur. L’agent a lu le fichier, trouvé les cas limites et corrigé le code tout seul, sans que l’on écrive la moindre fonction de gestion des outils.
Si l’on voit Not logged in ou Invalid API key, la variable ANTHROPIC_API_KEY n’est presque certainement pas définie dans ce terminal.
Étape 4 : comment choisir ce que l’agent peut faire seul ?
Ce dont l’agent est capable dépend des outils. La documentation propose trois ensembles typiques :
Read,Glob,Grep: analyse seule, sans modifier de fichiers,Read,Edit,Glob: analyse et modification du code,Read,Edit,Bash,Glob,Grep: automatisation complète, avec des commandes dans le terminal.
Pour que l’agent puisse, par exemple, chercher sur le web ou lancer des tests, il suffit d’ajouter un outil et de changer le prompt :
options = ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Glob", "Bash"], permission_mode="acceptEdits"
)
Avec Bash activé, le quickstart propose la tâche « Write unit tests for utils.py, run them, and fix any failures ». On peut aussi donner un rôle à l’agent via system_prompt, par exemple celui d’un développeur Python expérimenté qui respecte la PEP 8.
Le deuxième levier, c’est le mode de permissions (permission_mode) :
"default"transmet chaque appel non approuvé à notre fonctioncanUseToolet, en son absence, le refuse,"acceptEdits"accepte les modifications de fichiers et les commandes simples commemkdiroumvdans le dossier de travail,"plan"demande à l’agent d’explorer d’abord le code et de préparer un plan,"dontAsk"ne demande jamais rien : seul ce qui a été approuvé d’avance s’exécute, le reste est refusé (pratique pour les agents sans humain au clavier),"auto"fait passer les actions par un classifieur qui examine chacune et la bloque ou l’autorise,"bypassPermissions"exécute tout sans demander et, d’après la documentation, ne convient qu’aux environnements isolés, comme la CI ou les conteneurs.
La règle d’or de la documentation : "acceptEdits" sur la machine de développement, "bypassPermissions" uniquement là où l’agent ne peut rien abîmer d’important. Pour comprendre pourquoi c’est important, voir mon guide sur la sécurité des agents IA.
Mieux vaut aussi poser des limites tout de suite : max_turns limite le nombre de tours, et max_budget_usd arrête la boucle une fois le coût estimé dépassé. Sans elles, l’agent tourne jusqu’à ce qu’il estime avoir fini, ce qui, sur des tâches ouvertes du type « améliore ce projet », peut prendre un moment ;)
Étape 5 : comment donner à l’agent un outil maison ?
Les outils intégrés ne sont qu’un début. On définit un outil maison avec le décorateur @tool (nom, description, schéma d’entrée et fonction), puis on l’emballe dans un serveur MCP qui tourne dans le même processus. L’exemple de la documentation récupère la température depuis l’API Open-Meteo, on ajoute donc d’abord la bibliothèque httpx :
uv add httpx
La définition de l’outil :
from typing import Annotated, Any
import httpx
from claude_agent_sdk import tool, create_sdk_mcp_server
# Define a tool: name, description, input schema, handler
@tool(
"get_temperature",
"Get the current temperature at a location",
{
"latitude": Annotated[float, "Latitude coordinate"],
"longitude": Annotated[float, "Longitude coordinate"],
},
)
async def get_temperature(args: dict[str, Any]) -> dict[str, Any]:
async with httpx.AsyncClient() as client:
response = await client.get(
"https://api.open-meteo.com/v1/forecast",
params={
"latitude": args["latitude"],
"longitude": args["longitude"],
"current": "temperature_2m",
"temperature_unit": "fahrenheit",
},
)
data = response.json()
# Return a content array - Claude sees this as the tool result
return {
"content": [
{
"type": "text",
"text": f"Temperature: {data['current']['temperature_2m']}°F",
}
]
}
# Wrap the tool in an in-process MCP server
weather_server = create_sdk_mcp_server(
name="weather",
version="1.0.0",
tools=[get_temperature],
)
Dans le même fichier, on passe le serveur à l’agent. Le nom complet de l’outil suit le format mcp__{serveur}__{outil}, et c’est lui qu’on met dans allowed_tools pour qu’il fonctionne sans demander d’autorisation :
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def main():
options = ClaudeAgentOptions(
mcp_servers={"weather": weather_server},
allowed_tools=["mcp__weather__get_temperature"],
)
async for message in query(
prompt="What's the temperature in San Francisco?",
options=options,
):
# ResultMessage is the final message after all tool calls complete
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
asyncio.run(main())
L’agent appelle notre outil et répond à partir du résultat. À la place de la météo, on peut brancher n’importe quoi : notre base de données, une API interne ou un script.
Étape 6 : comment connecter l’agent à un serveur MCP ?
Les serveurs MCP prêts à l’emploi (j’ai présenté le protocole lui-même dans MCP, c’est quoi) se déclarent dans l’option mcp_servers. L’exemple de la documentation connecte l’agent au serveur distant de documentation de Claude Code via HTTP :
options = ClaudeAgentOptions(
mcp_servers={
"claude-code-docs": {
"type": "http",
"url": "https://code.claude.com/docs/mcp",
}
},
allowed_tools=["mcp__claude-code-docs__*"],
)
L’astérisque dans allowed_tools approuve tous les outils de ce serveur. Avec le prompt « Use the docs MCP server to explain what hooks are in Claude Code », l’agent parcourt la documentation et renvoie une réponse.
Et après le premier agent ?
Agent SDK dispose aussi du reste des fonctions de Claude Code :
- les hooks, du code maison exécuté avant ou après l’appel d’un outil,
- les sous-agents pour des sous-tâches séparées,
- les sessions : l’identifiant de
ResultMessage.session_idpermet de reprendre une conversation avec tout son contexte ou de la dupliquer, - les skills, commandes et la mémoire chargés depuis le dossier
.claude/, comme dans Claude Code.
Des applications d’exemple toutes prêtes (dont un assistant de messagerie et un agent de recherche) se trouvent dans le dépôt claude-agent-sdk-demos.
À quoi faut-il faire attention ?
D’abord, aux coûts. Le contexte grossit à chaque tour, et un gros fichier ou une commande bavarde peuvent consommer des milliers de tokens d’un coup. Les parties fixes (prompt système, définitions des outils) sont mises en cache automatiquement, mais il vaut quand même mieux régler max_budget_usd dès le premier jour (le plafond peut être dépassé du coût d’une réponse, donc gardons de la marge).
Ensuite, aux permissions. Un agent avec Bash et bypassPermissions sur un portable ordinaire, c’est chercher les ennuis, donc gardons l’automatisation complète pour les conteneurs.
Enfin, à la marque. D’après la documentation, un produit construit avec le SDK peut s’appeler par exemple « Claude Agent » ou «
FAQ
Comment créer un agent IA sans écrire la boucle à partir de zéro ?
Le plus simple est d’utiliser une bibliothèque qui contient déjà cette boucle, comme Claude Agent SDK. On installe claude-agent-sdk, on définit ANTHROPIC_API_KEY et on appelle query() avec un prompt et une liste d’outils autorisés. Le reste, c’est-à-dire les appels d’outils et la gestion du contexte, c’est le SDK qui s’en charge.
Claude Agent SDK est-il gratuit ?
La bibliothèque elle-même ne coûte rien, mais l’agent utilise l’API Claude, donc on paie les tokens consommés. Sans l’accord d’Anthropic, on ne peut pas la brancher sur un abonnement claude.ai.
Quelle différence entre Agent SDK et l’API Claude ?
Avec le Client SDK classique (API Claude), on écrit la boucle d’outils soi-même ou on utilise le tool runner en bêta. Agent SDK embarque les outils intégrés de Claude Code (lecture et modification de fichiers, commandes, recherche web), les permissions, les sessions et les hooks.
Peut-on écrire l’agent en TypeScript ?
Oui. Le paquet s’appelle @anthropic-ai/claude-agent-sdk, il nécessite Node.js 18.14+, et dans les exemples de la documentation les options portent les mêmes noms qu’en Python, simplement en camelCase (allowedTools, permissionMode).