Loading...
    • Erstellen
    • Admin
    • Modelle & Preise
    • Client SDKs
    • API-Referenz
    Search...
    ⌘K
    Erste Schritte
    Einführung in ClaudeSchnellstart
    Entwickeln mit Claude
    FunktionsübersichtVerwendung der Messages APIStoppgründe behandeln
    Modellfähigkeiten
    Erweitertes DenkenAdaptives DenkenAufwandSchnellmodus (Beta: Forschungsvorschau)Strukturierte AusgabenZitateStreaming-NachrichtenStapelverarbeitungSuchergebnisseStreaming-AblehnungenMehrsprachige UnterstützungEmbeddings
    Tools
    ÜbersichtWie Tool-Nutzung funktioniertWeb-SuchtoolWeb-AbruftoolCode-AusführungstoolSpeichertoolBash-ToolComputer-Use-ToolTexteditor-Tool
    Tool-Infrastruktur
    Tool-SucheProgrammatisches Tool-AufrufenFeingranulares Tool-Streaming
    Kontextverwaltung
    KontextfensterKomprimierungKontextbearbeitungPrompt-CachingToken-Zählung
    Mit Dateien arbeiten
    Files APIPDF-UnterstützungBilder und Vision
    Skills
    ÜbersichtSchnellstartBest PracticesSkills für UnternehmenSkills in der API
    MCP
    Remote-MCP-ServerMCP-Connector
    Prompt-Engineering
    ÜbersichtBest Practices für PromptsConsole-Prompting-Tools
    Testen und Evaluieren
    Erfolg definieren und Evaluierungen erstellenDas Evaluierungstool in der Console verwendenLatenz reduzieren
    Leitplanken stärken
    Halluzinationen reduzierenAusgabekonsistenz erhöhenJailbreaks abmildernPrompt-Lecks reduzieren
    Ressourcen
    Glossar
    Versionshinweise
    Claude Platform
    Console
    Log in
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...

    Solutions

    • AI agents
    • Code modernization
    • Coding
    • Customer support
    • Education
    • Financial services
    • Government
    • Life sciences

    Partners

    • Amazon Bedrock
    • Google Cloud's Vertex AI

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Company

    • Anthropic
    • Careers
    • Economic Futures
    • Research
    • News
    • Responsible Scaling Policy
    • Security and compliance
    • Transparency

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Help and security

    • Availability
    • Status
    • Support
    • Discord

    Terms and policies

    • Privacy policy
    • Responsible disclosure policy
    • Terms of service: Commercial
    • Terms of service: Consumer
    • Usage policy
    Documentation

    Tutorial: Einen tool-nutzenden Agenten erstellen

    Eine geführte Anleitung von einem einzelnen Tool-Aufruf bis zu einer produktionsreifen agentischen Schleife.

    Was this page helpful?

    • Ring 1: Einzelnes Tool, einzelner Durchlauf
    • Ring 2: Die agentische Schleife
    • Ring 3: Mehrere Tools, parallele Aufrufe
    • Ring 4: Fehlerbehandlung
    • Ring 5: Die Tool Runner SDK-Abstraktion
    • Was Sie erstellt haben
    • Nächste Schritte

    Dieses Tutorial erstellt einen Kalenderverwaltungs-Agenten in fünf konzentrischen Ringen. Jeder Ring ist ein vollständiges, ausführbares Programm, das genau ein Konzept zum vorherigen Ring hinzufügt. Am Ende werden Sie die agentische Schleife von Hand geschrieben und dann durch die Tool Runner SDK-Abstraktion ersetzt haben.

    Das Beispiel-Tool ist create_calendar_event. Sein Schema verwendet verschachtelte Objekte, Arrays und optionale Felder, sodass Sie sehen werden, wie Claude realistische Eingabeformen verarbeitet, anstatt einer einzelnen flachen Zeichenkette.

    Jeder Ring läuft eigenständig. Kopieren Sie einen beliebigen Ring in eine neue Datei, und er wird ohne den Code aus früheren Ringen ausgeführt.

    Ring 1: Einzelnes Tool, einzelner Durchlauf

    Das kleinstmögliche tool-nutzende Programm: ein Tool, eine Benutzernachricht, ein Tool-Aufruf, ein Ergebnis. Der Code ist ausführlich kommentiert, damit Sie jede Zeile dem Tool-Use-Lebenszyklus zuordnen können.

    Die Anfrage sendet ein tools-Array zusammen mit der Benutzernachricht. Wenn Claude entscheidet, ein Tool aufzurufen, kommt die Antwort mit stop_reason: "tool_use" und einem tool_use-Inhaltsblock zurück, der den Tool-Namen, eine eindeutige id und den strukturierten input enthält. Ihr Code führt das Tool aus und sendet das Ergebnis dann in einem tool_result-Block zurück, dessen tool_use_id mit der id aus dem Aufruf übereinstimmt.

    #!/bin/bash
    # Ring 1: Single tool, single turn.
    # Source for <CodeSource> in build-a-tool-using-agent.mdx.
    
    # Define one tool as a JSON fragment. The input_schema is a JSON Schema
    # object describing the arguments Claude should pass when it calls this
    # tool. This schema includes nested objects (recurrence), arrays
    # (attendees), and optional fields, which is closer to real-world tools
    # than a flat string argument.
    TOOLS='[
      {
        "name": "create_calendar_event",
        "description": "Create a calendar event with attendees and optional recurrence.",
        "input_schema": {
          "type": "object",
          "properties": {
            "title": {"type": "string"},
            "start": {"type": "string", "format": "date-time"},
            "end": {"type": "string", "format": "date-time"},
            "attendees": {
              "type": "array",
              "items": {"type": "string", "format": "email"}
            },
            "recurrence": {
              "type": "object",
              "properties": {
                "frequency": {"enum": ["daily", "weekly", "monthly"]},
                "count": {"type": "integer", "minimum": 1}
              }
            }
          },
          "required": ["title", "start", "end"]
        }
      }
    ]'
    
    USER_MSG="Schedule a 30-minute sync with [email protected] and [email protected] next Monday at 10am."
    
    # Send the user's request along with the tool definition. Claude decides
    # whether to call the tool based on the request and the tool description.
    RESPONSE=$(curl -s https://api.anthropic.com/v1/messages \
      -H "x-api-key: $ANTHROPIC_API_KEY" \
      -H "anthropic-version: 2023-06-01" \
      -H "content-type: application/json" \
      -d "$(jq -n \
        --argjson tools "$TOOLS" \
        --arg msg "$USER_MSG" \
        '{
          model: "claude-opus-4-6",
          max_tokens: 1024,
          tools: $tools,
          tool_choice: {type: "auto", disable_parallel_tool_use: true},
          messages: [{role: "user", content: $msg}]
        }')")
    
    # When Claude calls a tool, the response has stop_reason "tool_use"
    # and the content array contains a tool_use block alongside any text.
    echo "stop_reason: $(echo "$RESPONSE" | jq -r '.stop_reason')"
    
    # Find the tool_use block. A response may contain text blocks before the
    # tool_use block, so filter by type rather than assuming position.
    TOOL_USE=$(echo "$RESPONSE" | jq '.content[] | select(.type == "tool_use")')
    TOOL_USE_ID=$(echo "$TOOL_USE" | jq -r '.id')
    echo "Tool: $(echo "$TOOL_USE" | jq -r '.name')"
    echo "Input: $(echo "$TOOL_USE" | jq -c '.input')"
    
    # Execute the tool. In a real system this would call your calendar API.
    # Here the result is hardcoded to keep the example self-contained.
    RESULT='{"event_id": "evt_123", "status": "created"}'
    
    # Send the result back. The tool_result block goes in a user message and
    # its tool_use_id must match the id from the tool_use block above. The
    # assistant's previous response is included so Claude has the full history.
    ASSISTANT_CONTENT=$(echo "$RESPONSE" | jq '.content')
    FOLLOWUP=$(curl -s https://api.anthropic.com/v1/messages \
      -H "x-api-key: $ANTHROPIC_API_KEY" \
      -H "anthropic-version: 2023-06-01" \
      -H "content-type: application/json" \
      -d "$(jq -n \
        --argjson tools "$TOOLS" \
        --arg msg "$USER_MSG" \
        --argjson assistant "$ASSISTANT_CONTENT" \
        --arg tool_use_id "$TOOL_USE_ID" \
        --arg result "$RESULT" \
        '{
          model: "claude-opus-4-6",
          max_tokens: 1024,
          tools: $tools,
          tool_choice: {type: "auto", disable_parallel_tool_use: true},
          messages: [
            {role: "user", content: $msg},
            {role: "assistant", content: $assistant},
            {role: "user", content: [
              {type: "tool_result", tool_use_id: $tool_use_id, content: $result}
            ]}
          ]
        }')")
    
    # With the tool result in hand, Claude produces a final natural-language
    # answer and stop_reason becomes "end_turn".
    echo "stop_reason: $(echo "$FOLLOWUP" | jq -r '.stop_reason')"
    echo "$FOLLOWUP" | jq -r '.content[] | select(.type == "text") | .text'

    Was zu erwarten ist

    Output
    stop_reason: tool_use
    Tool: create_calendar_event
    Input: {'title': 'Sync', 'start': '2026-03-30T10:00:00', 'end': '2026-03-30T10:30:00', 'attendees': ['[email protected]', '[email protected]']}
    stop_reason: end_turn
    I've scheduled your 30-minute sync with Alice and Bob for next Monday at 10am.

    Der erste stop_reason ist tool_use, weil Claude auf das Kalenderergebnis wartet. Nachdem Sie das Ergebnis gesendet haben, ist der zweite stop_reason end_turn und der Inhalt ist natürliche Sprache für den Benutzer.

    Ring 2: Die agentische Schleife

    Ring 1 ging davon aus, dass Claude das Tool genau einmal aufrufen würde. Echte Aufgaben erfordern oft mehrere Aufrufe: Claude könnte ein Ereignis erstellen, die Bestätigung lesen und dann ein weiteres erstellen. Die Lösung ist eine while-Schleife, die weiterhin Tools ausführt und Ergebnisse zurücksendet, bis stop_reason nicht mehr "tool_use" ist.

    Die andere Änderung ist der Gesprächsverlauf. Anstatt das messages-Array bei jeder Anfrage von Grund auf neu zu erstellen, führen Sie eine laufende Liste und hängen daran an. Jeder Durchlauf sieht den vollständigen vorherigen Kontext.

    Was zu erwarten ist

    Output
    I've set up your weekly team standup for the next 4 Mondays at 9am with Alice, Bob, and Carol invited.

    Die Schleife kann einmal oder mehrmals laufen, je nachdem, wie Claude die Aufgabe aufteilt. Ihr Code muss dies nicht mehr im Voraus wissen.

    Ring 3: Mehrere Tools, parallele Aufrufe

    Agenten haben selten nur eine Fähigkeit. Fügen Sie ein zweites Tool hinzu, list_calendar_events, damit Claude den bestehenden Zeitplan überprüfen kann, bevor es etwas Neues erstellt.

    Wenn Claude mehrere unabhängige Tool-Aufrufe durchführen muss, kann es mehrere tool_use-Blöcke in einer einzigen Antwort zurückgeben. Ihre Schleife muss alle davon verarbeiten und alle Ergebnisse zusammen in einer Benutzernachricht zurücksenden. Iterieren Sie über jeden tool_use-Block in response.content, nicht nur über den ersten.

    Was zu erwarten ist

    Output
    I checked your calendar for next Monday and found an existing meeting from 2pm to 3pm. I've scheduled the planning session for 10am to 11am to avoid the conflict.

    Weitere Informationen zur gleichzeitigen Ausführung und Reihenfolgegarantien finden Sie unter Parallele Tool-Nutzung.

    Ring 4: Fehlerbehandlung

    Tools schlagen fehl. Eine Kalender-API könnte ein Ereignis mit zu vielen Teilnehmern ablehnen, oder ein Datum könnte fehlerhaft formatiert sein. Wenn ein Tool einen Fehler auslöst, senden Sie die Fehlermeldung mit is_error: true zurück, anstatt abzustürzen. Claude liest den Fehler und kann es mit korrigierten Eingaben erneut versuchen, den Benutzer um Klärung bitten oder die Einschränkung erklären.

    Was zu erwarten ist

    Output
    I tried to schedule the all-hands but the calendar only allows 10 attendees per event. I can split this into two sessions, or you can let me know which 10 people to prioritize.

    Das is_error-Flag ist der einzige Unterschied zu einem erfolgreichen Ergebnis. Claude sieht das Flag und den Fehlertext und antwortet entsprechend. Siehe Tool-Aufrufe verarbeiten für die vollständige Fehlerbehandlungsreferenz.

    Ring 5: Die Tool Runner SDK-Abstraktion

    Die Ringe 2 bis 4 haben dieselbe Schleife von Hand geschrieben: API aufrufen, stop_reason prüfen, Tools ausführen, Ergebnisse anhängen, wiederholen. Der Tool Runner erledigt dies für Sie. Definieren Sie jedes Tool als Funktion, übergeben Sie die Liste an tool_runner und rufen Sie die endgültige Nachricht ab, sobald die Schleife abgeschlossen ist. Fehlerumhüllung, Ergebnisformatierung und Gesprächsverwaltung werden intern gehandhabt.

    Das Python SDK verwendet den @beta_tool-Dekorator, um das Schema aus Typhinweisen und dem Docstring abzuleiten. Das TypeScript SDK verwendet betaZodTool mit einem Zod-Schema.

    Tool Runner ist in den Python-, TypeScript- und Ruby-SDKs verfügbar. Die Shell- und CLI-Tabs zeigen einen Hinweis anstelle von Code; verwenden Sie die Ring-4-Schleife für shell-basierte Skripte.

    Was zu erwarten ist

    Output
    I checked your calendar for next Monday and found an existing meeting from 2pm to 3pm. I've scheduled the planning session for 10am to 11am to avoid the conflict.

    Die Ausgabe ist identisch mit Ring 3. Der Unterschied liegt im Code: ungefähr halb so viele Zeilen, keine manuelle Schleife, und das Schema befindet sich neben der Implementierung.

    Was Sie erstellt haben

    Sie begannen mit einem einzelnen fest codierten Tool-Aufruf und endeten mit einem produktionsreifen Agenten, der mehrere Tools, parallele Aufrufe und Fehler verarbeitet, und haben das alles dann in den Tool Runner zusammengefasst. Dabei haben Sie jedes Element des Tool-Use-Protokolls gesehen: tool_use-Blöcke, tool_result-Blöcke, tool_use_id-Abgleich, stop_reason-Prüfung und is_error-Signalisierung.

    Nächste Schritte

    Ihre Schemas verfeinern

    Schema-Spezifikation und Best Practices.

    Tool Runner im Detail

    Die vollständige SDK-Abstraktionsreferenz.

    Fehlerbehebung

    Häufige Tool-Use-Fehler beheben.

    #!/bin/bash
    # Ring 2: The agentic loop.
    # Source for <CodeSource> in build-a-tool-using-agent.mdx.
    
    TOOLS='[
      {
        "name": "create_calendar_event",
        "description": "Create a calendar event with attendees and optional recurrence.",
        "input_schema": {
          "type": "object",
          "properties": {
            "title": {"type": "string"},
            "start": {"type": "string", "format": "date-time"},
            "end": {"type": "string", "format": "date-time"},
            "attendees": {"type": "array", "items": {"type": "string", "format": "email"}},
            "recurrence": {
              "type": "object",
              "properties": {
                "frequency": {"enum": ["daily", "weekly", "monthly"]},
                "count": {"type": "integer", "minimum": 1}
              }
            }
          },
          "required": ["title", "start", "end"]
        }
      }
    ]'
    
    run_tool() {
      local name="$1"
      local input="$2"
      if [ "$name" = "create_calendar_event" ]; then
        local title=$(echo "$input" | jq -r '.title')
        jq -n --arg title "$title" '{event_id: "evt_123", status: "created", title: $title}'
      else
        echo "{\"error\": \"Unknown tool: $name\"}"
      fi
    }
    
    # Keep the full conversation history in a JSON array so each turn sees prior context.
    MESSAGES='[{"role": "user", "content": "Schedule a weekly team standup every Monday at 9am for the next 4 weeks. Invite the whole team: [email protected], [email protected], [email protected]."}]'
    
    call_api() {
      curl -s https://api.anthropic.com/v1/messages \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "content-type: application/json" \
        -d "$(jq -n --argjson tools "$TOOLS" --argjson messages "$MESSAGES" \
          '{model: "claude-opus-4-6", max_tokens: 1024, tools: $tools, tool_choice: {type: "auto", disable_parallel_tool_use: true}, messages: $messages}')"
    }
    
    RESPONSE=$(call_api)
    
    # Loop until Claude stops asking for tools. Each iteration runs the requested
    # tool, appends the result to history, and asks Claude to continue.
    while [ "$(echo "$RESPONSE" | jq -r '.stop_reason')" = "tool_use" ]; do
      TOOL_USE=$(echo "$RESPONSE" | jq '.content[] | select(.type == "tool_use")')
      TOOL_NAME=$(echo "$TOOL_USE" | jq -r '.name')
      TOOL_INPUT=$(echo "$TOOL_USE" | jq -c '.input')
      TOOL_USE_ID=$(echo "$TOOL_USE" | jq -r '.id')
      RESULT=$(run_tool "$TOOL_NAME" "$TOOL_INPUT")
    
      ASSISTANT_CONTENT=$(echo "$RESPONSE" | jq '.content')
      MESSAGES=$(echo "$MESSAGES" | jq \
        --argjson assistant "$ASSISTANT_CONTENT" \
        --arg tool_use_id "$TOOL_USE_ID" \
        --arg result "$RESULT" \
        '. + [
          {role: "assistant", content: $assistant},
          {role: "user", content: [{type: "tool_result", tool_use_id: $tool_use_id, content: $result}]}
        ]')
    
      RESPONSE=$(call_api)
    done
    
    echo "$RESPONSE" | jq -r '.content[] | select(.type == "text") | .text'
    #!/bin/bash
    # Ring 3: Multiple tools, parallel calls.
    # Source for <CodeSource> in build-a-tool-using-agent.mdx.
    
    TOOLS='[
      {
        "name": "create_calendar_event",
        "description": "Create a calendar event with attendees and optional recurrence.",
        "input_schema": {
          "type": "object",
          "properties": {
            "title": {"type": "string"},
            "start": {"type": "string", "format": "date-time"},
            "end": {"type": "string", "format": "date-time"},
            "attendees": {"type": "array", "items": {"type": "string", "format": "email"}},
            "recurrence": {
              "type": "object",
              "properties": {
                "frequency": {"enum": ["daily", "weekly", "monthly"]},
                "count": {"type": "integer", "minimum": 1}
              }
            }
          },
          "required": ["title", "start", "end"]
        }
      },
      {
        "name": "list_calendar_events",
        "description": "List all calendar events on a given date.",
        "input_schema": {
          "type": "object",
          "properties": {"date": {"type": "string", "format": "date"}},
          "required": ["date"]
        }
      }
    ]'
    
    run_tool() {
      case "$1" in
        create_calendar_event)
          jq -n --arg title "$(echo "$2" | jq -r '.title')" '{event_id: "evt_123", status: "created", title: $title}' ;;
        list_calendar_events)
          echo '{"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}' ;;
        *)
          echo "{\"error\": \"Unknown tool: $1\"}" ;;
      esac
    }
    
    MESSAGES='[{"role": "user", "content": "Check what I have next Monday, then schedule a planning session that avoids any conflicts."}]'
    
    call_api() {
      curl -s https://api.anthropic.com/v1/messages \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "content-type: application/json" \
        -d "$(jq -n --argjson tools "$TOOLS" --argjson messages "$MESSAGES" \
          '{model: "claude-opus-4-6", max_tokens: 1024, tools: $tools, messages: $messages}')"
    }
    
    RESPONSE=$(call_api)
    
    while [ "$(echo "$RESPONSE" | jq -r '.stop_reason')" = "tool_use" ]; do
      # A single response can contain multiple tool_use blocks. Process all of
      # them and return all results together in one user message.
      TOOL_RESULTS='[]'
      while read -r block; do
        NAME=$(echo "$block" | jq -r '.name')
        INPUT=$(echo "$block" | jq -c '.input')
        ID=$(echo "$block" | jq -r '.id')
        RESULT=$(run_tool "$NAME" "$INPUT")
        TOOL_RESULTS=$(echo "$TOOL_RESULTS" | jq --arg id "$ID" --arg result "$RESULT" \
          '. + [{type: "tool_result", tool_use_id: $id, content: $result}]')
      done < <(echo "$RESPONSE" | jq -c '.content[] | select(.type == "tool_use")')
    
      MESSAGES=$(echo "$MESSAGES" | jq \
        --argjson assistant "$(echo "$RESPONSE" | jq '.content')" \
        --argjson results "$TOOL_RESULTS" \
        '. + [{role: "assistant", content: $assistant}, {role: "user", content: $results}]')
    
      RESPONSE=$(call_api)
    done
    
    echo "$RESPONSE" | jq -r '.content[] | select(.type == "text") | .text'
    #!/bin/bash
    # Ring 4: Error handling.
    # Source for <CodeSource> in build-a-tool-using-agent.mdx.
    
    TOOLS='[
      {
        "name": "create_calendar_event",
        "description": "Create a calendar event with attendees and optional recurrence.",
        "input_schema": {
          "type": "object",
          "properties": {
            "title": {"type": "string"},
            "start": {"type": "string", "format": "date-time"},
            "end": {"type": "string", "format": "date-time"},
            "attendees": {"type": "array", "items": {"type": "string", "format": "email"}},
            "recurrence": {
              "type": "object",
              "properties": {
                "frequency": {"enum": ["daily", "weekly", "monthly"]},
                "count": {"type": "integer", "minimum": 1}
              }
            }
          },
          "required": ["title", "start", "end"]
        }
      },
      {
        "name": "list_calendar_events",
        "description": "List all calendar events on a given date.",
        "input_schema": {
          "type": "object",
          "properties": {"date": {"type": "string", "format": "date"}},
          "required": ["date"]
        }
      }
    ]'
    
    run_tool() {
      case "$1" in
        create_calendar_event)
          local count=$(echo "$2" | jq '.attendees | length // 0')
          if [ "$count" -gt 10 ]; then
            echo "ERROR: Too many attendees (max 10)"
            return 1
          fi
          jq -n --arg title "$(echo "$2" | jq -r '.title')" '{event_id: "evt_123", status: "created", title: $title}' ;;
        list_calendar_events)
          echo '{"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}' ;;
        *)
          echo "ERROR: Unknown tool: $1"
          return 1 ;;
      esac
    }
    
    EMAILS=$(seq 0 14 | sed 's/.*/user&@example.com/' | paste -sd, -)
    MESSAGES="[{\"role\": \"user\", \"content\": \"Schedule an all-hands with everyone: $EMAILS\"}]"
    
    call_api() {
      curl -s https://api.anthropic.com/v1/messages \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "content-type: application/json" \
        -d "$(jq -n --argjson tools "$TOOLS" --argjson messages "$MESSAGES" \
          '{model: "claude-opus-4-6", max_tokens: 1024, tools: $tools, messages: $messages}')"
    }
    
    RESPONSE=$(call_api)
    
    while [ "$(echo "$RESPONSE" | jq -r '.stop_reason')" = "tool_use" ]; do
      TOOL_RESULTS='[]'
      while read -r block; do
        NAME=$(echo "$block" | jq -r '.name')
        INPUT=$(echo "$block" | jq -c '.input')
        ID=$(echo "$block" | jq -r '.id')
        if OUTPUT=$(run_tool "$NAME" "$INPUT"); then
          TOOL_RESULTS=$(echo "$TOOL_RESULTS" | jq --arg id "$ID" --arg result "$OUTPUT" \
            '. + [{type: "tool_result", tool_use_id: $id, content: $result}]')
        else
          # Signal failure so Claude can retry or ask for clarification.
          TOOL_RESULTS=$(echo "$TOOL_RESULTS" | jq --arg id "$ID" --arg result "$OUTPUT" \
            '. + [{type: "tool_result", tool_use_id: $id, content: $result, is_error: true}]')
        fi
      done < <(echo "$RESPONSE" | jq -c '.content[] | select(.type == "tool_use")')
    
      MESSAGES=$(echo "$MESSAGES" | jq \
        --argjson assistant "$(echo "$RESPONSE" | jq '.content')" \
        --argjson results "$TOOL_RESULTS" \
        '. + [{role: "assistant", content: $assistant}, {role: "user", content: $results}]')
    
      RESPONSE=$(call_api)
    done
    
    echo "$RESPONSE" | jq -r '.content[] | select(.type == "text") | .text'
    #!/bin/bash
    # Ring 5: The Tool Runner SDK abstraction.
    # Source for <CodeSource> in build-a-tool-using-agent.mdx.
    
    # The Tool Runner SDK abstraction is available in the Python, TypeScript,
    # and Ruby SDKs. There is no equivalent for raw curl requests. Switch to
    # the Python or TypeScript tab to see Ring 5, or keep the Ring 4 loop as
    # your shell implementation.