First, a mental-model correction that saves confusion later: when Claude "uses a tool" on Amazon Bedrock, Claude does not execute anything. The model emits a structured request saying which tool it wants and with what input; your application runs the actual code and reports back. Bedrock does not offer Anthropic's server-side tools (web search, web fetch, code execution are unavailable there), so on this platform every tool is client-implemented — a function you write, own, and secure. Client-implemented tool use itself is fully supported for Claude on Bedrock, including Anthropic's bash, text editor, and memory tool types.
Step 1: declare the tools
A Converse request can carry a tool configuration alongside the conversation. For each tool you provide three things: a name the model will call it by, a description of what it does and when to use it, and an input schema defining the parameters it accepts (a JSON Schema object — for example, an order_id string for a "look up order status" tool). The description carries surprising weight: the model decides whether and how to use a tool almost entirely from it, so write descriptions the way you'd brief a new colleague, including what the tool does not do. Exact field names for the tool configuration are in the AWS Converse API reference.
Step 2: handle the toolUse block
When the model decides a tool is needed, the response ends not with a final answer but with an assistant message containing a toolUse content block, and a stop reason indicating the model is waiting on a tool. The block carries the tool's name, the input the model chose (matching your schema), and a unique identifier for this specific call. Your code should treat the input as untrusted data even though it matches the schema — validate ranges, check authorization (can this user's session see that order?), and apply the same input hygiene you would to any API parameter. This is where prompt injection becomes a practical engineering concern: text the model read earlier can influence what it asks your tools to do.
Step 3: return a toolResult and loop
Execute the function, then continue the conversation by appending two things to your message history: the assistant message containing the toolUse block, exactly as received, and a new user message containing a toolResult block that references the matching call identifier and carries your output (results can also signal an error status when the tool failed, letting the model recover gracefully rather than hallucinate). Send the whole history back through Converse. The model may now produce its final answer — or request another tool, in which case you repeat the cycle. Production loops always bound this: cap the number of iterations, set per-tool timeouts, and log every call for audit.
Bedrock-specific caveats
Three limitations are worth knowing before you design around them. First, Anthropic's tool search feature — useful when you have very many tools — works on Bedrock via the InvokeModel API only, not Converse; if you need it, see the API decision guide. Second, Bedrock's S3-based batch inference does not support tool calling, so tool loops are strictly an on-demand-inference pattern there. Third, remember the missing server-side tools: if your design assumed built-in web search or code execution, on Bedrock you must implement equivalents yourself or reconsider the platform — the web-tools workaround article covers options.
On permissions, the calling role needs bedrock:Converse (and bedrock:ConverseStream if you stream the loop) on the model and inference-profile ARNs involved. Each round trip of the loop resends the growing conversation — tool definitions, history, and results all count as input tokens — so prompt caching and history trimming pay off quickly in chatty tool workflows.
Where to go next
Ground yourself in the message mechanics with the Converse deep dive, add incremental output with ConverseStream, or start from the concepts in Tool Use 101.