# Aide-mémoire Agent SDK

Démarrages rapides du Claude Agent SDK en Python et TypeScript, options, hooks, subagents, serveurs MCP, modes de permission, événements de streaming et patterns headless.

import { Tabs, TabItem, Steps } from '@prosefly/astro-components';

Le **Claude Agent SDK** (`claude-agent-sdk`, renommé depuis « Claude Code SDK ») expose de façon programmatique le harnais d'agent de Claude Code : la boucle agentique, les outils, les hooks, les permissions, les subagents et les serveurs MCP. Vous l'hébergez — à opposer aux **Managed Agents**, où Anthropic héberge la boucle et le sandbox.

:::note[Quand recourir au SDK]
Choisissez l'Agent SDK quand vous devez contrôler le runtime, le réseau, la localité des données ou le harnais lui-même. Choisissez les Managed Agents pour une charge opérationnelle minimale. Choisissez une simple boucle Messages API quand vous n'avez pas besoin de la machinerie fichiers/outils de Claude Code.
:::

## Installation

```bash
pip install claude-agent-sdk          # Python
npm install @anthropic-ai/claude-agent-sdk   # TypeScript
```

## Démarrage rapide

<Tabs>
  <TabItem label="Python">
```python
import anyio
from claude_agent_sdk import query, ClaudeAgentOptions

async def main():
    options = ClaudeAgentOptions(
        model="claude-opus-5",
        system_prompt="You are a precise refactoring assistant.",
        allowed_tools=["Read", "Edit", "Bash"],
        permission_mode="acceptEdits",
        cwd="/repo",
    )
    async for message in query(prompt="Rename getUserName to getUsername repo-wide, then run tests.",
                               options=options):
        print(message)

anyio.run(main)
```
  </TabItem>
  <TabItem label="TypeScript">
```typescript
import { query } from '@anthropic-ai/claude-agent-sdk';

for await (const message of query({
  prompt: 'Rename getUserName to getUsername repo-wide, then run tests.',
  options: {
    model: 'claude-opus-5',
    systemPrompt: 'You are a precise refactoring assistant.',
    allowedTools: ['Read', 'Edit', 'Bash'],
    permissionMode: 'acceptEdits',
    cwd: '/repo',
  },
})) {
  console.log(message);
}
```
  </TabItem>
</Tabs>

## Options

| Option (Py / TS) | Objet |
| --- | --- |
| `model` | ID de modèle (`claude-opus-5`, `claude-sonnet-5`, …) |
| `system_prompt` / `systemPrompt` | Instructions de base ; peut s'ajouter au prompt intégré de Claude Code |
| `allowed_tools` / `allowedTools` | Allowlist d'outils (`Read`, `Edit`, `Bash`, `Grep`, …) |
| `disallowed_tools` / `disallowedTools` | Refus explicites |
| `permission_mode` / `permissionMode` | `default` \| `acceptEdits` \| `plan` \| `bypassPermissions` |
| `cwd` | Répertoire de travail |
| `mcp_servers` / `mcpServers` | Configs de serveurs MCP disponibles pour l'agent |
| `hooks` | Handlers de cycle de vie (voir ci-dessous) |
| `agents` | Définitions de subagents |
| `setting_sources` / `settingSources` | Charger ou non `CLAUDE.md`/les settings depuis le disque |
| `max_turns` / `maxTurns` | Plafond de sécurité sur les tours agentiques (pas l'arrêt principal) |
| `env` | Variables d'environnement pour l'exécution des outils |

:::caution[max_turns est un garde-fou, pas un arrêt]
`max_turns` est un filet de sécurité contre l'emballement. La véritable terminaison de la boucle reste `stop_reason: end_turn`. Utiliser un plafond de tours comme arrêt *principal* est l'anti-pattern 2.
:::

## Modes de permission

| Mode | Comportement | Usage |
| --- | --- | --- |
| `default` | Demander selon les règles de permission | Interactif, prudent |
| `acceptEdits` | Auto-accepter les éditions de fichiers, demander pour le shell | Boucles d'édition de confiance |
| `plan` | Lecture seule ; produire un plan, n'exécuter rien | Relecture avant d'agir |
| `bypassPermissions` | Aucune invite | **CI sandboxé uniquement** |

## Callback de permission programmatique

Contrôle fin au-delà des listes allow/deny — décidez par appel :

<Tabs>
  <TabItem label="Python">
```python
async def can_use_tool(tool_name, tool_input, context):
    if tool_name == "Bash" and "rm -rf" in tool_input.get("command", ""):
        return {"behavior": "deny", "message": "destructive command blocked"}
    if tool_name == "Edit" and tool_input.get("file_path", "").endswith(".env"):
        return {"behavior": "deny", "message": "secrets are read-only"}
    return {"behavior": "allow"}

options = ClaudeAgentOptions(can_use_tool=can_use_tool, allowed_tools=["Read", "Edit", "Bash"])
```
  </TabItem>
  <TabItem label="TypeScript">
```typescript
const options = {
  allowedTools: ['Read', 'Edit', 'Bash'],
  canUseTool: async (toolName: string, input: Record<string, unknown>) => {
    if (toolName === 'Bash' && String(input.command).includes('rm -rf'))
      return { behavior: 'deny', message: 'destructive command blocked' };
    return { behavior: 'allow' };
  },
};
```
  </TabItem>
</Tabs>

## Hooks

Mêmes événements de cycle de vie que Claude Code (`PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`, `SessionStart`, `SubagentStop`, `PreCompact`, `Notification`), enregistrés dans le code. Un hook `PreToolUse` renvoyant une décision de refus bloque l'outil — le mécanisme d'application déterministe.

```python
async def block_destructive(input_data, tool_use_id, context):
    cmd = input_data.get("tool_input", {}).get("command", "")
    if any(bad in cmd for bad in ("rm -rf", "git push --force", "drop table")):
        return {"decision": "block", "reason": "destructive command"}
    return {}

options = ClaudeAgentOptions(
    hooks={"PreToolUse": [{"matcher": "Bash", "hooks": [block_destructive]}]})
```

## Subagents

Définissez des agents délégués avec un contexte isolé, leurs propres outils et modèle :

```python
options = ClaudeAgentOptions(
    agents={
        "security-reviewer": {
            "description": "Reviews diffs for injection, secrets, authz. Use after auth edits.",
            "prompt": "Report findings only: file:line, severity, fix. Do not edit.",
            "tools": ["Read", "Grep", "Glob"],
            "model": "claude-sonnet-5",
        }
    })
```

Les subagents ne reçoivent **que** ce que le parent transmet ; ne présumez jamais d'un héritage. Utilisez un modèle moins cher pour le travail délégué mécanique.

## Serveurs MCP

```python
options = ClaudeAgentOptions(
    mcp_servers={
        "orders": {"command": "python", "args": ["orders_server.py"]},          # stdio
        "linear": {"type": "http", "url": "https://mcp.linear.app/mcp"},        # remote
    },
    allowed_tools=["mcp__orders__get_order", "mcp__linear__list_issues"])
```

Les outils MCP sont dans l'espace de noms `mcp__<server>__<tool>` ; mettez-les explicitement en allowlist.

## Événements de streaming

`query()` produit un flux de messages typés reflétant le flux SSE : un message system d'init, des messages assistant (blocs text/tool_use), des messages user (tool_result), et un message result final portant `stop_reason`, `usage` et le coût.

```python
async for message in query(prompt="…", options=options):
    if message.type == "assistant":
        for block in message.content:
            if block.type == "text":
                print(block.text, end="")
    elif message.type == "result":
        print(message.stop_reason, message.usage, message.total_cost_usd)
```

Branchez sur le **`stop_reason` du message result**, pas sur le parsing du texte assistant.

## Patterns headless

| Pattern | Comment |
| --- | --- |
| Job batch en un coup | `query()` une fois, lire le message `result`, sortir en non-zéro sur échec |
| Barrière CI | `permission_mode="plan"` ou une allowlist serrée ; parser `result` pour pass/fail |
| Fan-out sur des unités | Lancez N sessions SDK (une par fichier/module), chacune dans son propre worktree, agrégez |
| Service longue durée | Persistez le stockage du memory tool ; utilisez le context editing pour borner les tokens |
| Contrôle des coûts | Modèle moins cher pour les subagents ; plafonner `max_turns` ; journaliser `total_cost_usd` |

```python
# CI gate sketch
import sys
async def review():
    async for m in query(prompt="Review the diff for security issues; output PASS or FAIL.",
                         options=ClaudeAgentOptions(permission_mode="plan",
                                                    allowed_tools=["Read", "Grep", "Bash(git diff:*)"])):
        if m.type == "result":
            sys.exit(0 if "PASS" in (m.result or "") else 1)
```

## Agent SDK vs Managed Agents vs boucle Messages API

| | Boucle Messages API | Agent SDK | Managed Agents |
| --- | --- | --- | --- |
| Vous écrivez la boucle | ✓ | Harnais fourni | Non (hébergé) |
| Outils fichiers / machinerie Claude Code | Non | ✓ | ✓ |
| Hooks / subagents / modes de permission | À construire soi-même | ✓ | Configurés |
| Contrôle runtime/réseau/données | Complet | Complet | Limité |
| Charge d'exploitation | Faible (juste l'API) | Vous hébergez le harnais | Minimale |
| Choisir quand | Boucle d'outils simple | Besoin du harnais, auto-hébergé | Moins d'ops |

## Idées reçues courantes

| Idée reçue | Réalité | Pourquoi cela compte à l'examen |
| --- | --- | --- |
| « L'Agent SDK est hébergé par Anthropic » | Vous l'hébergez ; les Managed Agents sont hébergés | Distracteur de confusion d'hébergement |
| « `max_turns` est la façon dont la boucle s'arrête » | C'est un plafond de sécurité ; `stop_reason` arrête la boucle | Anti-pattern de plafond d'itérations |
| « `bypassPermissions` convient par commodité » | CI sandboxé uniquement | Distracteur d'agence excessive |
| « Les subagents voient tout le contexte du parent » | Seulement ce qui est transmis explicitement | Distracteur de perte de contexte silencieuse |
| « Appliquer les règles dans le prompt système » | Utilisez les hooks / callbacks de permission | Anti-pattern du prompt-comme-application |
| « Charger tous les outils MCP » | Allowlistez les outils avec espace de noms ; gardez la liste petite | Anti-pattern de trop d'outils |

## Analyse de scénario

Une équipe plateforme veut un agent CI auto-hébergé qui relit les PR pour des problèmes de sécurité, ne doit jamais éditer de fichiers, doit s'exécuter dans leur VPC (localité des données), et doit faire échouer le build sur un constat.

<Steps>
1. **Auto-hébergé + localité des données dans le VPC** → Agent SDK, pas Managed Agents (qui est hébergé). Une boucle Messages API impliquerait de reconstruire le harnais.
2. **Ne doit jamais éditer** → `permission_mode="plan"` (lecture seule) et une allowlist d'outils sans `Edit`/`Write`.
3. **Focus sécurité** → un subagent `security-reviewer` sur `claude-sonnet-5` (moins cher, suffisant), outils `Read`, `Grep`, `Bash(git diff:*)`.
4. **Application** → un refus `PreToolUse` pour toute écriture, déterministe, pas une phrase de prompt.
5. **Faire échouer le build** → parser le message `result` ; `sys.exit(1)` sur un constat.
6. **Sécurité coût/boucle** → plafonner `max_turns` ; journaliser `total_cost_usd` ; toujours brancher sur `stop_reason`.
</Steps>

Alternatives rejetées : Managed Agents (viole la localité des données), `bypassPermissions` (agence excessive), appliquer la lecture seule via le prompt système (prompt-comme-application), et utiliser un plafond de tours comme signal de complétion (anti-pattern de plafond d'itérations).

## Points clés à retenir

- L'Agent SDK est **auto-hébergé** ; les Managed Agents sont hébergés par Anthropic ; choisissez selon le contrôle runtime/données vs la charge d'ops.
- La terminaison de boucle est `stop_reason` ; `max_turns` n'est qu'un garde-fou contre l'emballement.
- Appliquez les règles avec les hooks / `can_use_tool`, jamais avec du texte de prompt.
- Les subagents ne reçoivent que le contexte transmis explicitement ; donnez-leur leur propre modèle (souvent moins cher) et une allowlist d'outils serrée.
- Les outils MCP sont `mcp__server__tool` ; allowlistez-les et gardez l'ensemble petit.
- `bypassPermissions` est réservé au CI sandboxé.
