Follow Discord
Sweep 22 Oct 2026 · 21:04Z Build v2.1.309 528 read Stable v2.1.308 Latest v2.1.309 Next v2.1.309 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One capture · api

One read of Claude Developer Platformapi-20261023T000722Z

139 pages moved out of 812 read.

Pages moved 139 significant first
Pages read 812 in this capture
Captured 00:07 UTC
Corpus hash b5a82bae26cf index-hash

What this read moved

1-25 of 139, page 1 of 6

This capture is too large to show at once. Changes 1-25 of 139 are below, significant first; the rest are on the following screens.

about-claude/models/optimizing-for-cost-and-intelligence Changed · +19 / -20 lines

from line 104
104104| Pauses run for minutes | Claude Fable 5.1 | 5-minute cache kept warm, instead of the 1-hour duration |
105105| Pauses run toward an hour | Claude Fable 5.1 | 1-hour duration |
106106 
107On Claude Haiku 5.5, use the "Any" rows. Keep-alive requests were not measured on it. By list prices, not by measurement, each one costs twice as much relative to the input price as on Claude Opus 5.5 and Claude Sonnet 5.5: Claude Haiku 5.5 reads the cache at the standard 0.1x, those two at 0.05x.
107On Claude Haiku 5.5, use the "Any" rows: it reads the cache at the standard 0.1x the input price, twice the 0.05x of Claude Opus 5.5 and Claude Sonnet 5.5, so by list prices each keep-alive request costs twice as much relative to the input price. A `max_tokens: 0` request refreshed its cache in a separate test (reference 16).
108108 
109109**Why, row by row.**
110110 
from line 140
140140 
141141* The 1-in-20 rule leaves a margin over the break-even of about 1 turn in 30. At 1 turn in 20 the 5-minute and the 1-hour setting cost about the same on Claude Sonnet 5.5, within run-to-run variation; at 1 turn in 10 the 5-minute setting cost 54% more (reference 16).
142142* On models other than Claude Sonnet 5.5 and Claude Opus 5.5, prices put the break-even in the same range: every current model uses the same cache-write multipliers, and every model but Claude Fable 5.1, Claude Mythos 5.1, Claude Opus 5.5, and Claude Sonnet 5.5 the same read price.
143* The Claude Opus 5.5 and Claude Sonnet 5.5 keep-alive figures come from requests sent with `max_tokens: 1`. Whether a `max_tokens: 0` request refreshes an existing cache entry was not measured on Claude Opus 5.5 (reference 16).
143* The Claude Opus 5.5 and Claude Sonnet 5.5 keep-alive figures come from requests sent with `max_tokens: 1`. A `max_tokens: 0` request also refreshed an existing cache entry on Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5, and Claude Haiku 5.5 (reference 16).
144144 
145145To keep the cache warm:
146146 
from line 186
186186 
187187Keep per-request text in the newest user turn, after the last cache breakpoint, and make every cache-invalidating change at a natural break, such as the first request after a compaction. The cache is a byte-exact prefix match over the request in order (tools, then system prompt, then messages), so a change anywhere invalidates everything after it.
188188 
189The stakes are highest on Claude Fable 5.1 and Claude Mythos 5.1, where a break re-writes the prefix at 1.25x the input price instead of reading it at 0.025x. The following table gives what one broken turn costs on a 100,000-token prefix, computed from list prices with the 5-minute cache, not measured:
189When something must change mid-conversation, send it in the form that keeps the cache, where the model supports it. In Anthropic's test on Claude Fable 5.1, Claude Opus 5.5, and Claude Sonnet 5.5, on a cached conversation of about 100,000 tokens, editing the system prompt cost about 25 to 50 times what the same text cost as a mid-conversation system message. Changing effort at the top level cost 20 to 40 times what it cost per message[18](https://platform.claude.com/docs/en/about-claude/models/optimizing-for-cost-and-intelligence#refs):
190190 
191| Model | One broken turn | The read instead | Compared with the read |
192| -------------------------------------- | --------------- | ---------------- | ---------------------- |
193| Claude Fable 5.1 and Claude Mythos 5.1 | $1.25 USD | $0.025 USD | 50 times |
194| Claude Opus 5.5 | $0.50 USD | $0.02 USD | 25 times |
195| Claude Sonnet 5.5 | $0.25 USD | $0.01 USD | 25 times |
191<Frame>
192 ![Bar chart: system prompt edit up to $1.26 USD, top-level effort change up to $1.02 USD, either in a message up to $0.026 USD](https://platform.claude.com/docs/images/cost-intel/cache-break-cost.svg)
193</Frame>
196194 
197On the other current models it costs 12.5 times the read.
195The stakes are highest on Claude Fable 5.1 and Claude Mythos 5.1, where a break re-writes the prefix at 1.25x the input price instead of reading it at 0.025x. On Claude Haiku 5.5 a system prompt edit costs 12.5 times an unchanged request. The edit re-wrote the whole conversation. By default, newer accounts get a 400 error instead[18](https://platform.claude.com/docs/en/about-claude/models/optimizing-for-cost-and-intelligence#refs). The top-level effort change, in a conversation that held the model's first reply with its thinking, re-wrote all but the system prompt. These figures are from the Messages API.
198196 
199197**What to do.**
200198 
201* Put nothing that changes per request, such as a timestamp or a queue position, ahead of the stable prefix: it turns every request into a full cache write. In a measurement on an earlier model, a 25-token status line at the front of the system prompt made a run of a small agent that triages bug reports ([Trim input and context tokens](https://platform.claude.com/docs/en/about-claude/models/optimizing-for-cost-and-intelligence#trim-input-and-context-tokens)) cost more than running with caching off[18](https://platform.claude.com/docs/en/about-claude/models/optimizing-for-cost-and-intelligence#refs).
202* The [prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#what-invalidates-the-cache) page lists the changes that invalidate the cache, such as a change to [`effort`](https://platform.claude.com/docs/en/build-with-claude/effort), the thinking configuration, the system prompt, or a tool definition. Changing the output format invalidates the cache for that conversation (see [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs#prompt-modification-and-token-costs)).
203* On the most recent models, change instructions with a [mid-conversation system message](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages), a `{"role": "system"}` message appended to `messages`, instead of editing the top-level `system` field: the cached prefix stays intact. Check that page for which models support it. A per-message effort change leaves the cached prefix intact too, but on fewer models: [Change effort mid-conversation](https://platform.claude.com/docs/en/build-with-claude/effort#changing-effort-mid-conversation) lists them.
199* Put nothing that changes per request, such as a timestamp or a queue position, ahead of the stable prefix: it turns every request into a full cache write. In Anthropic's test, a 25-token status line at the front of the system prompt made each request write the whole conversation; moved to the end of the newest user turn, it left the cache read intact[18](https://platform.claude.com/docs/en/about-claude/models/optimizing-for-cost-and-intelligence#refs).
200* The [prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#what-invalidates-the-cache) page lists the changes that invalidate the cache, such as a change to [`effort`](https://platform.claude.com/docs/en/build-with-claude/effort), the thinking configuration, the [`speed`](https://platform.claude.com/docs/en/build-with-claude/fast-mode) setting, the system prompt, or a tool definition. Changing the output format invalidates the cache for that conversation (see [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs#prompt-modification-and-token-costs)).
201* Check which models support each form: [mid-conversation system messages](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages), and [per-message effort](https://platform.claude.com/docs/en/build-with-claude/effort#changing-effort-mid-conversation), which is in beta.
204202* After a cache-invalidating change, confirm cache reads have not dropped; if they have, [cache diagnostics](https://platform.claude.com/docs/en/build-with-claude/cache-diagnostics) shows where the prefix diverged.
205203 
206Anthropic measured the timing of a change on an earlier model, on the triage agent's long sessions[18](https://platform.claude.com/docs/en/about-claude/models/optimizing-for-cost-and-intelligence#refs). In that measurement, sessions cost more when an effort change and an added tool were made mid-session, or on the request that triggered the compaction, than when the same two changes were made on the first request after compaction. Accuracy was within run-to-run noise in every case.
204On an earlier model, Anthropic measured when to make such a change, on the triage agent's long sessions[18](https://platform.claude.com/docs/en/about-claude/models/optimizing-for-cost-and-intelligence#refs). An effort change and an added tool cost more mid-session, or on the request that triggered the compaction, than on the first request after compaction. Accuracy was within run-to-run noise. The sessions with no changes cost more than those changed after compaction for another reason: they ran at the default effort throughout, and two of them compacted a second time, while the others started at low effort:
207205 
208The sessions with no changes cost more than the sessions changed after compaction, but not because of caching. They ran at the default effort throughout and two compacted a second time, while the others started at low effort (reference 18):
209 
210206<Frame>
211207 ![Bar chart, cost per triage session: $0.81 USD no changes, $0.95 USD mid-session, $0.92 USD at compaction, $0.75 USD after](https://platform.claude.com/docs/images/cost-intel/compaction-timing.svg)
212208</Frame>
from line 1008
10121008 * **5-minute and 1-hour settings, Claude Sonnet 5 and Claude Opus 5.5:** In the 5% cell the 5-minute and 1-hour settings tied on Claude Sonnet 5, because that draw's pauses fell on small prefixes; on Claude Opus 5.5 they nearly tied. On Claude Sonnet 5, at its cache-read price of $0.20 USD per million tokens, a session cost $0.60 USD, $0.69 USD, and $0.99 USD on the 5-minute setting with no pauses, 5%, and 10% of turns paused, and $0.70 USD, $0.70 USD, and $0.71 USD on the 1-hour setting.
10131009 * **Keep-alive requests, Claude Sonnet 5 and Claude Opus 5:** Anthropic measured keep-alive requests that refresh the 5-minute cache on Claude Sonnet 5 and Claude Opus 5 on August 23, 2026, and on Claude Opus 5.5 in the runs above, always sent with `max_tokens: 1`. On Claude Sonnet 5 they cost 7.7% less than the 1-hour setting with 5% of turns paused and about the same with 10%; on Claude Opus 5 no difference was measurable at either share; on both they cost more with a pause of 6 minutes or more before every turn. So on those two models the setting to use is the 1-hour duration, not keep-alive requests.
10141010 * **Keep-alive requests, Claude Opus 5.5:** On Claude Opus 5.5 keep-alive requests cost 8% to 18% less than the 1-hour setting with 5% and 10% of turns paused (8% to 13% less at its default effort, `medium`, and 10% to 18% less at `high`; about 10% to 15% once between-session noise is removed by re-billing each keep-alive session's own tokens at 1-hour cache prices), and more with a pause before every turn: 4% to 6% more at 6 minutes, 9% to 10% at 20 minutes, and 56% to 58% at 45 minutes.
1015 * **Requests with `max_tokens: 0`, Claude Opus 5.5:** Anthropic's pre-launch API tests on Claude Opus 5.5 show that a `max_tokens: 0` request writes the cache and that the next request reads it; whether such a request refreshes an existing entry was not measured on Opus 5.5.
1011 * **Requests with `max_tokens: 0`:** Measured October 20, 2026, on Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5, and Claude Haiku 5.5, three times per model, on a 10,000-token system prompt with the 5-minute cache. A `max_tokens: 0` request sent 4 minutes after the cache write returned no output, read the whole entry, and wrote nothing. A request 4 minutes later, 8 minutes after the write, still read the whole entry. Without the `max_tokens: 0` request, the request at 8 minutes read nothing and wrote the prefix again, all 12 times.
10161012 * **5-minute and 1-hour settings, Claude Sonnet 5.5:** With 5% of turns paused the 5-minute setting cost 5% more than the 1-hour setting on average (2% less to 12% more across the three runs), which is within run-to-run variation: that draw's pauses fell on small prefixes, as on Claude Sonnet 5, and re-billing each 5-minute session's own tokens at 1-hour cache prices gives about 1% more. With 10% paused it cost 54% more (36% to 73% more). With no pauses, the 5-minute setting cost 19% less than the 1-hour setting on average (3% to 32% less across the three runs; the 1-hour setting's higher write price predicts about 20%), and 21% to 24% less in every run with 2-minute pauses.
10171013 * **Keep-alive requests, Claude Sonnet 5.5:** Keep-alive requests cost 16% less than the 1-hour setting on average with 5% of turns paused (13% to 20% less across the three runs) and 8% less with 10% (3% to 10% less), and cost more with a pause of 6 minutes or more before every turn. With 5% and 10% of turns paused, the pauses lasted 6 to 32 minutes on Claude Sonnet 5.5 and on Claude Opus 5.5. Re-billing each keep-alive session's own tokens at 1-hour cache prices, as for Claude Opus 5.5 above, gives 15% to 16% less at 5% paused and about 10% less at 10%, and with a pause before every turn 7% more at 6 minutes, 9% to 10% more at 20 minutes, and 66% to 73% more at 45 minutes. The 6-minute figures leave out run 3, in which 2 of that session's 39 keep-alive requests timed out: the cache expired before those two turns, the session wrote 214,000 tokens to the cache against 80,000 to 85,000 in runs 1 and 2, and it cost 38% more than run 3's 1-hour session.
10181014 * **Keep-alive requests, Claude Sonnet 5.5, by run:** Comparing each run's keep-alive session with the same run's 1-hour session instead, keep-alive cost from about the same to 10% more at 6 minutes (runs 1 and 2; 4% more on their average), from about the same to 16% more at 20 minutes, and 64% to 80% more at 45 minutes.
from line 1021
10251021 
1026102217. **Cache-read share in production:** Aggregated first-party Claude API usage for the 14 days ending August 23, 2026, direct API product only, Anthropic-internal organizations excluded, no organization identified. An organization-day counts as an agent loop when its requests carry tool definitions and tool results, its prompts hold 9 or more prior tool calls on average, caching was used, and it made at least 10 such requests (the API has no conversation identifier, so this stands in for conversation length): 303,003 organization-days across 106,487 organizations, median cache-read share 84.2% of all input tokens, upper quartile 91.7%. Use-case labels (the organization's declared use case, or otherwise its classified one) cover 74% of those organization-days and 99% of their tokens; about 72% of coding organization-days read 80% or more. The top decile of organization-days reads 95.9% or more for coding and 94.2% to 94.8% for support, research, data, and other agents. The request-level split at 25 or more prior tool calls comes from a six-hour sample: coding 92% read, 7% write, under 1% uncached.
10271023 
102818. **Compaction timing and status-line measurements:**
102418. **Cache-break, compaction timing, and status-line measurements:**
10291025 
1026 * **October 2026 test, one change to a long conversation:** Measured October 20, 2026, on Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5, and Claude Haiku 5.5, on the Messages API with no tools, adaptive thinking, each model's default effort, the 5-minute cache, a cache breakpoint on the system prompt, and [automatic caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#automatic-caching). The conversation held about 101,000 tokens: a 20,000-token system prompt, 13 user turns of filler text, the model's own reply to the first turn with its thinking block, and one-word replies to the rest. Once one request had cached the conversation, a second added one short turn, and each measured request sent that longer conversation with one change. Two conversations per model gave the same result.
1027 * **October 2026 test, tokens read and written:** With no change, and with a per-message effort change, the request read all 101,000 tokens and wrote none. With a 25-token mid-conversation system message, or a 25-token status line at the end of the newest user turn, it read them all and wrote only the new text. A top-level effort change (to `low` on Claude Fable 5.1 and Claude Sonnet 5.5, to `high` on Claude Opus 5.5 and Claude Haiku 5.5) read the 20,000-token system prompt and wrote 81,000 tokens. The same 25 tokens at the front or the end of the system prompt read nothing and wrote all 101,000. The request after a mid-conversation system message, a top-level effort change, or 25 tokens added to the end of the system prompt read the new prefix; a new status line at the front wrote everything again. A further test on October 21, 2026, on the same four models with conversations of 5,000 or 100,000 tokens and one-word replies, sent a beta header for the first time mid-conversation, as a first per-message effort change may: that request read everything the request before it had cached, all 54 times. A system message sent with no beta header did the same, all 15 times.
1028 * **October 2026 test, how priced:** The chart gives the prompt side of one request, from its `usage` fields at list prices. The chart's values, in the order system prompt edit, system message, top-level effort change, and per-message effort change: Claude Fable 5.1 $1.26 USD, $0.026 USD, $1.02 USD, and $0.025 USD; Claude Opus 5.5 $0.50 USD, $0.020 USD, $0.41 USD, and $0.020 USD; Claude Sonnet 5.5 $0.25 USD, $0.010 USD, $0.20 USD, and $0.010 USD. The unchanged request repeated an earlier request byte for byte; a new turn of about 25 tokens adds under $0.001 USD. On Claude Haiku 5.5, whose requests were over 100,000 prompt tokens and so at its [long context prices](https://platform.claude.com/docs/en/about-claude/pricing#long-context-pricing): $0.063 USD for the system prompt edit, $0.052 USD for the effort change, and $0.005 USD for the read. The requests ran on an Anthropic-internal organization whose usage is metered the same way as a customer organization's, and on which the API does not enforce the [prefix check](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#prefix-check) by default, so its system prompt edits were accepted. On accounts created on or after August 31, 2026, 00:00 UTC, a request to one of these four models that sends earlier thinking blocks back gets a 400 error by default if it changes `system`, `tools`, or a message before one of them.
10301029 * **What was measured:** The triage agent's long variant from [Trim input and context tokens](https://platform.claude.com/docs/en/about-claude/models/optimizing-for-cost-and-intelligence#trim-input-and-context-tokens), run August 24, 2026, on Claude Sonnet 5 with the 5-minute cache, cost from the usage fields at list prices, five sessions per arm: a no-change arm at the default effort throughout ($0.81 USD per session), and two arms that start at low effort and make the same two cache-breaking changes, a switch to the default effort and one added tool, either mid-session at requests 12 and 17 ($0.95 USD) or together on the first request after the first compaction ($0.75 USD).
10311030 * **Fourth arm:** A fourth arm of six sessions, run August 25, 2026, made the same two changes on the request that triggered the first compaction ($0.92 USD per session): that request's summarization pass wrote the 81,000-token context to the cache instead of reading it, so that pass cost $0.21 USD against $0.04 USD for the same pass in the boundary arm.
10321031 * **Compaction:** Sessions first compacted at request 21 to 25 (16 of the 21 sessions at request 22), once the prompt passed the 80,000-token compaction trigger, and two no-change sessions compacted a second time near the end. The boundary arm's lower total than the no-change arm reflects its low-effort requests before the change and those second compactions rather than caching: the two arms' re-write costs differ by under $0.01 USD.

about-claude/use-case-guides/content-moderation Changed · +130 / -130 lines

from line 210
210210 final AnthropicClient client = AnthropicOkHttpClient.fromEnv();
211211 
212212 final List<String> allowedUserComments = List.of(
213 "This movie was great, I really enjoyed it. The main actor really killed it!",
214 "I hate Mondays.",
215 "It is a great time to invest in gold!");
213 "This movie was great, I really enjoyed it. The main actor really killed it!",
214 "I hate Mondays.",
215 "It is a great time to invest in gold!");
216216 
217217 final List<String> disallowedUserComments = List.of(
218 "Delete this post now or you better hide. I am coming after you and your family.",
219 "Stay away from the 5G cellphones!! They are using 5G to control you.",
220 "Congratulations! You have won a $1,000 gift card. Click here to claim your prize!");
218 "Delete this post now or you better hide. I am coming after you and your family.",
219 "Stay away from the 5G cellphones!! They are using 5G to control you.",
220 "Congratulations! You have won a $1,000 gift card. Click here to claim your prize!");
221221 
222222 // Sample user comments to test the content moderation
223223 final List<String> userComments =
224 Stream.concat(allowedUserComments.stream(), disallowedUserComments.stream()).toList();
224 Stream.concat(allowedUserComments.stream(), disallowedUserComments.stream()).toList();
225225 
226226 // Categories considered unsafe for content moderation
227227 final List<String> unsafeCategories = List.of(
228 "Child Exploitation",
229 "Conspiracy Theories",
230 "Hate",
231 "Indiscriminate Weapons",
232 "Intellectual Property",
233 "Non-Violent Crimes",
234 "Privacy",
235 "Self-Harm",
236 "Sex Crimes",
237 "Sexual Content",
238 "Specialized Advice",
239 "Violent Crimes");
228 "Child Exploitation",
229 "Conspiracy Theories",
230 "Hate",
231 "Indiscriminate Weapons",
232 "Intellectual Property",
233 "Non-Violent Crimes",
234 "Privacy",
235 "Self-Harm",
236 "Sex Crimes",
237 "Sexual Content",
238 "Specialized Advice",
239 "Violent Crimes");
240240 ```
241241 
242242 ```php PHP
from line 367
367367 
368368### Build a strong prompt
369369 
370To use Claude for content moderation, Claude must have information about the moderation requirements of your application. Start by writing a prompt that allows you to define your moderation needs:
370To use Claude for content moderation, Claude must interpret the moderation requirements of your application correctly. Start by writing a prompt that allows you to define your moderation needs:
371371 
372372<CodeGroup exclude="shell">
373373 ```python Python
from line 693
693693 "explanation": [Optional. Only include if there is a violation.]
694694 }
695695 Do not include markdown formatting or code fences in your response."""
696 .formatted(message, unsafeCategoryStr);
696 .formatted(message, unsafeCategoryStr);
697697 
698698 // Send the request to Claude for content moderation
699699 Message response = client.messages().create(MessageCreateParams.builder()
700 .model(Model.CLAUDE_HAIKU_4_5_20251001) // Using the Haiku model for lower costs
701 .maxTokens(200)
702 .addUserMessage(assessmentPrompt)
703 .build());
700 .model(Model.CLAUDE_HAIKU_4_5_20251001) // Using the Haiku model for lower costs
701 .maxTokens(200)
702 .addUserMessage(assessmentPrompt)
703 .build());
704704 
705705 // Parse the JSON response from Claude
706706 String assessmentJson = response.content().stream()
707 .flatMap(contentBlock -> contentBlock.text().stream())
708 .findFirst()
709 .orElseThrow()
710 .text();
707 .flatMap(contentBlock -> contentBlock.text().stream())
708 .findFirst()
709 .orElseThrow()
710 .text();
711711 ObjectMapper mapper = new ObjectMapper();
712712 JsonNode assessment = mapper.readTree(assessmentJson);
713713 
from line 716
716716 
717717 // If there's a violation, get the categories and explanation; otherwise, use empty defaults
718718 List<String> violatedCategories = containsViolation && assessment.has("categories")
719 ? mapper.convertValue(assessment.get("categories"), new TypeReference<List<String>>() {})
720 : List.of();
719 ? mapper.convertValue(assessment.get("categories"), new TypeReference<List<String>>() {})
720 : List.of();
721721 String explanation = containsViolation && assessment.hasNonNull("explanation")
722 ? assessment.get("explanation").asText()
723 : null;
722 ? assessment.get("explanation").asText()
723 : null;
724724 
725725 return new ModerationResult(containsViolation, violatedCategories, explanation);
726726 }
from line 1237
12371237 "explanation": <Optional. Only include if risk level is greater than 0>
12381238 }
12391239 Do not include markdown formatting or code fences in your response."""
1240 .formatted(message, unsafeCategoryStr);
1240 .formatted(message, unsafeCategoryStr);
12411241 
12421242 // Send the request to Claude for risk assessment
12431243 Message response = client.messages().create(MessageCreateParams.builder()
1244 .model(Model.CLAUDE_HAIKU_4_5_20251001) // Using the Haiku model for lower costs
1245 .maxTokens(200)
1246 .addUserMessage(assessmentPrompt)
1247 .build());
1244 .model(Model.CLAUDE_HAIKU_4_5_20251001) // Using the Haiku model for lower costs
1245 .maxTokens(200)
1246 .addUserMessage(assessmentPrompt)
1247 .build());
12481248 
12491249 // Parse the JSON response from Claude
12501250 String assessmentJson = response.content().stream()
1251 .flatMap(contentBlock -> contentBlock.text().stream())
1252 .findFirst()
1253 .orElseThrow()
1254 .text();
1251 .flatMap(contentBlock -> contentBlock.text().stream())
1252 .findFirst()
1253 .orElseThrow()
1254 .text();
12551255 ObjectMapper mapper = new ObjectMapper();
12561256 JsonNode assessment = mapper.readTree(assessmentJson);
12571257 
from line 1259
12591259 int riskLevel = assessment.required("risk_level").asInt();
12601260 JsonNode categoriesNode = assessment.required("categories");
12611261 List<String> violatedCategories = categoriesNode.isNull()
1262 ? List.of()
1263 : mapper.convertValue(categoriesNode, new TypeReference<List<String>>() {});
1262 ? List.of()
1263 : mapper.convertValue(categoriesNode, new TypeReference<List<String>>() {});
12641264 String explanation = assessment.hasNonNull("explanation")
1265 ? assessment.get("explanation").asText()
1266 : null;
1265 ? assessment.get("explanation").asText()
1266 : null;
12671267 
12681268 return new RiskAssessment(riskLevel, violatedCategories, explanation);
12691269 }
from line 1883
18831883 record CategoryDefinition(String category, String definition) {}
18841884 
18851885 final List<CategoryDefinition> unsafeCategoryDefinitions = List.of(
1886 new CategoryDefinition(
1887 "Child Exploitation",
1888 "Content that depicts child nudity or that enables, encourages, excuses, or depicts the sexual abuse of children."),
1889 new CategoryDefinition(
1890 "Conspiracy Theories",
1891 """
1892 Content that promotes or endorses unfounded, false, or misleading theories about events, situations, or individuals,
1893 which can incite harm or undermine public trust in institutions or facts."""),
1894 new CategoryDefinition(
1895 "Hate",
1896 """
1897 Content that is hateful toward people on the basis of their protected characteristics
1898 (race, color, ethnicity, national origin, disability, religious affiliation, caste, sexual orientation, sex, gender identity and serious disease),
1899 or content that perpetuates negative stereotypes."""),
1900 new CategoryDefinition(
1901 "Indiscriminate Weapons",
1902 "Content that enables the creation of weapons of mass destruction."),
1903 new CategoryDefinition(
1904 "Intellectual Property",
1905 "Content that violates the intellectual property rights of third parties."),
1906 new CategoryDefinition(
1907 "Non-Violent Crimes",
1908 "Content that enables, encourages, or excuses the commission of non-violent crimes."),
1909 new CategoryDefinition(
1910 "Privacy",
1911 "Content that contains sensitive, personal information about private individuals."),
1912 new CategoryDefinition(
1913 "Self-Harm",
1914 "Content that enables, encourages, or excuses acts of intentional self-harm."),
1915 new CategoryDefinition(
1916 "Sex Crimes",
1917 "Content that enables, encourages, or excuses the commission of sex crimes."),
1918 new CategoryDefinition(
1919 "Sexual Content",
1920 "Content that contains erotic descriptions or explicit depictions of sex acts."),
1921 new CategoryDefinition(
1922 "Specialized Advice",
1923 """
1924 Content that contains financial, medical, or legal advice.
1925 Financial advice includes guidance on investments, stocks, bonds, or any financial planning."""),
1926 new CategoryDefinition(
1927 "Violent Crimes",
1928 "Content that enables, encourages, or excuses the commission of violent crimes."));
1886 new CategoryDefinition(
1887 "Child Exploitation",
1888 "Content that depicts child nudity or that enables, encourages, excuses, or depicts the sexual abuse of children."),
1889 new CategoryDefinition(
1890 "Conspiracy Theories",
1891 """
1892 Content that promotes or endorses unfounded, false, or misleading theories about events, situations, or individuals,
1893 which can incite harm or undermine public trust in institutions or facts."""),
1894 new CategoryDefinition(
1895 "Hate",
1896 """
1897 Content that is hateful toward people on the basis of their protected characteristics
1898 (race, color, ethnicity, national origin, disability, religious affiliation, caste, sexual orientation, sex, gender identity and serious disease),
1899 or content that perpetuates negative stereotypes."""),
1900 new CategoryDefinition(
1901 "Indiscriminate Weapons",
1902 "Content that enables the creation of weapons of mass destruction."),
1903 new CategoryDefinition(
1904 "Intellectual Property",
1905 "Content that violates the intellectual property rights of third parties."),
1906 new CategoryDefinition(
1907 "Non-Violent Crimes",
1908 "Content that enables, encourages, or excuses the commission of non-violent crimes."),
1909 new CategoryDefinition(
1910 "Privacy",
1911 "Content that contains sensitive, personal information about private individuals."),
1912 new CategoryDefinition(
1913 "Self-Harm",
1914 "Content that enables, encourages, or excuses acts of intentional self-harm."),
1915 new CategoryDefinition(
1916 "Sex Crimes",
1917 "Content that enables, encourages, or excuses the commission of sex crimes."),
1918 new CategoryDefinition(
1919 "Sexual Content",
1920 "Content that contains erotic descriptions or explicit depictions of sex acts."),
1921 new CategoryDefinition(
1922 "Specialized Advice",
1923 """
1924 Content that contains financial, medical, or legal advice.
1925 Financial advice includes guidance on investments, stocks, bonds, or any financial planning."""),
1926 new CategoryDefinition(
1927 "Violent Crimes",
1928 "Content that enables, encourages, or excuses the commission of violent crimes."));
19291929 
19301930 record ModerationDecision(boolean violation, List<String> violatedCategories, String explanation) {}
19311931 
from line 1934
19341934 throws JsonProcessingException {
19351935 // Format unsafe categories string, with each category and its definition on a new line
19361936 String unsafeCategoryStr = unsafeCategoryDefinitions.stream()
1937 .map(categoryDefinition ->
1938 categoryDefinition.category() + ": " + categoryDefinition.definition())
1939 .collect(Collectors.joining("\n"));
1937 .map(categoryDefinition ->
1938 categoryDefinition.category() + ": " + categoryDefinition.definition())
1939 .collect(Collectors.joining("\n"));
19401940 
19411941 // Construct the prompt for Claude, including the message and unsafe categories
19421942 String assessmentPrompt = """
from line 1959
19591959 "explanation": [Optional. Only include if there is a violation.]
19601960 }
19611961 Do not include markdown formatting or code fences in your response."""
1962 .formatted(message, unsafeCategoryStr);
1962 .formatted(message, unsafeCategoryStr);
19631963 
19641964 // Send the request to Claude for content moderation
19651965 Message response = client.messages().create(MessageCreateParams.builder()
1966 .model(Model.CLAUDE_HAIKU_4_5_20251001) // Using the Haiku model for lower costs
1967 .maxTokens(200)
1968 .addUserMessage(assessmentPrompt)
1969 .build());
1966 .model(Model.CLAUDE_HAIKU_4_5_20251001) // Using the Haiku model for lower costs
1967 .maxTokens(200)
1968 .addUserMessage(assessmentPrompt)
1969 .build());
19701970 
19711971 // Parse the JSON response from Claude
19721972 String assessmentJson = response.content().stream()
1973 .flatMap(contentBlock -> contentBlock.text().stream())
1974 .findFirst()
1975 .orElseThrow()
1976 .text();
1973 .flatMap(contentBlock -> contentBlock.text().stream())
1974 .findFirst()
1975 .orElseThrow()
1976 .text();
19771977 ObjectMapper mapper = new ObjectMapper();
19781978 JsonNode assessment = mapper.readTree(assessmentJson);
19791979 
from line 1982
19821982 
19831983 // If there's a violation, get the categories and explanation; otherwise, use empty defaults
19841984 List<String> violatedCategories = containsViolation && assessment.has("categories")
1985 ? mapper.convertValue(assessment.get("categories"), new TypeReference<List<String>>() {})
1986 : List.of();
1985 ? mapper.convertValue(assessment.get("categories"), new TypeReference<List<String>>() {})
1986 : List.of();
19871987 String explanation = containsViolation && assessment.hasNonNull("explanation")
1988 ? assessment.get("explanation").asText()
1989 : null;
1988 ? assessment.get("explanation").asText()
1989 : null;
19901990 
19911991 return new ModerationDecision(containsViolation, violatedCategories, explanation);
19921992 }
from line 2235
22352235 # Send the request to Claude for content moderation
22362236 response = client.messages.create(
22372237 model="claude-haiku-4-5-20251001", # Using the Haiku model for lower costs
2238 max_tokens=2048, # Increased max token count to handle batches
2238 max_tokens=2048, # Increased token limit to handle batches
22392239 messages=[{"role": "user", "content": assessment_prompt}],
22402240 )
22412241 
from line 2310
23102310 // Send the request to Claude for content moderation
23112311 const response = await client.messages.create({
23122312 model: "claude-haiku-4-5-20251001", // Using the Haiku model for lower costs
2313 max_tokens: 2048, // Increased max token count to handle batches
2313 max_tokens: 2048, // Increased token limit to handle batches
23142314 messages: [{ role: "user", content: assessmentPrompt }]
23152315 });
23162316 
from line 2383
23832383 new()
23842384 {
23852385 Model = Model.ClaudeHaiku4_5_20251001, // Using the Haiku model for lower costs
2386 MaxTokens = 2048, // Increased max token count to handle batches
2386 MaxTokens = 2048, // Increased token limit to handle batches
23872387 Messages = [new UserMessageParam { Content = assessmentPrompt }],
23882388 }
23892389 );
from line 2470
24702470 // Send the request to Claude for content moderation
24712471 response, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
24722472 Model: anthropic.ModelClaudeHaiku4_5_20251001, // Using the Haiku model for lower costs
2473 MaxTokens: 2048, // Increased max token count to handle batches
2473 MaxTokens: 2048, // Increased token limit to handle batches
24742474 Messages: []anthropic.MessageParam{
24752475 anthropic.NewUserMessage(anthropic.NewTextBlock(assessmentPrompt)),
24762476 },
from line 2520
25202520 
25212521 // Format messages string, with each message wrapped in XML-like tags and given an ID
25222522 String messagesStr = IntStream.range(0, messages.size())
2523 .mapToObj(idx -> "<message id=%d>%s</message>".formatted(idx, messages.get(idx)))
2524 .collect(Collectors.joining("\n"));
2523 .mapToObj(idx -> "<message id=%d>%s</message>".formatted(idx, messages.get(idx)))
2524 .collect(Collectors.joining("\n"));
25252525 
25262526 // Construct the prompt for Claude, including the messages and unsafe categories
25272527 String assessmentPrompt = """
from line 2552
25522552 - Remember to analyze every message for a violation.
25532553 - Select any number of violations that reasonably apply.
25542554 - Do not include markdown formatting or code fences in your response."""
2555 .formatted(messagesStr, unsafeCategoryStr);
2555 .formatted(messagesStr, unsafeCategoryStr);
25562556 
25572557 // Send the request to Claude for content moderation
25582558 Message response = client.messages().create(MessageCreateParams.builder()
2559 .model(Model.CLAUDE_HAIKU_4_5_20251001) // Using the Haiku model for lower costs
2560 .maxTokens(2048) // Increased max token count to handle batches
2561 .addUserMessage(assessmentPrompt)
2562 .build());
2559 .model(Model.CLAUDE_HAIKU_4_5_20251001) // Using the Haiku model for lower costs
2560 .maxTokens(2048) // Increased token limit to handle batches
2561 .addUserMessage(assessmentPrompt)
2562 .build());
25632563 
25642564 // Parse the JSON response from Claude
25652565 String assessmentJson = response.content().stream()
2566 .flatMap(contentBlock -> contentBlock.text().stream())
2567 .findFirst()
2568 .orElseThrow()
2569 .text();
2566 .flatMap(contentBlock -> contentBlock.text().stream())
2567 .findFirst()
2568 .orElseThrow()
2569 .text();
25702570 return new ObjectMapper().readTree(assessmentJson);
25712571 }
25722572 
from line 2577
25772577 ObjectMapper mapper = new ObjectMapper();
25782578 for (JsonNode violation : response.required("violations")) {
25792579 List<String> violatedCategories =
2580 mapper.convertValue(violation.required("categories"), new TypeReference<List<String>>() {});
2580 mapper.convertValue(violation.required("categories"), new TypeReference<List<String>>() {});
25812581 IO.println("""
2582 Comment: %s
2583 Violated Categories: %s
2584 Explanation: %s
2585 """.formatted(
2586 userComments.get(violation.required("id").asInt()),
2587 String.join(", ", violatedCategories),
2588 violation.required("explanation").asText()));
2582 Comment: %s
2583 Violated Categories: %s
2584 Explanation: %s
2585 """.formatted(
2586 userComments.get(violation.required("id").asInt()),
2587 String.join(", ", violatedCategories),
2588 violation.required("explanation").asText()));
25892589 }
25902590 }
25912591 ```
from line 2636
26362636 // Send the request to Claude for content moderation
26372637 $response = $client->messages->create(
26382638 model: 'claude-haiku-4-5-20251001', // Using the Haiku model for lower costs
2639 maxTokens: 2048, // Increased max token count to handle batches
2639 maxTokens: 2048, // Increased token limit to handle batches
26402640 messages: [['role' => 'user', 'content' => $assessmentPrompt]],
26412641 );
26422642 
from line 2703
27032703 # Send the request to Claude for content moderation
27042704 response = CLIENT.messages.create(
27052705 model: Anthropic::Model::CLAUDE_HAIKU_4_5_20251001, # Using the Haiku model for lower costs
2706 max_tokens: 2048, # Increased max token count to handle batches
2706 max_tokens: 2048, # Increased token limit to handle batches
27072707 messages: [{role: :user, content: assessment_prompt}]
27082708 )
27092709 

about-claude/use-case-guides/customer-support-chat Changed · +24 / -24 lines

from line 384
384384For this example, start with one tool for calculating the quote.
385385 
386386<Tip>
387 As a reminder, this tool does not perform the actual calculation; it just signals to the application that a tool should be used with whatever arguments specified.
387 As a reminder, this tool does not perform the actual calculation; it just signals to the application that a tool should be used with whatever arguments Claude specifies.
388388</Tip>
389389 
390390Add the model name, the tool definition, and a stub implementation to `config.py`:
from line 848
848848 
849849 Message generateMessage(List<MessageParam> messages, long maxTokens) {
850850 return anthropic.messages().create(MessageCreateParams.builder()
851 .model(MODEL)
852 .system(IDENTITY)
853 .maxTokens(maxTokens)
854 .messages(messages)
855 .tools(TOOLS)
856 .build());
851 .model(MODEL)
852 .system(IDENTITY)
853 .maxTokens(maxTokens)
854 .messages(messages)
855 .tools(TOOLS)
856 .build());
857857 }
858858 
859859 String processUserInput(String userInput) {
from line 866
866866 if (lastBlock.isToolUse()) {
867867 ToolUseBlock toolUse = lastBlock.asToolUse();
868868 Map<String, JsonValue> toolInput =
869 (Map<String, JsonValue>) toolUse._input().asObject().orElseThrow();
869 (Map<String, JsonValue>) toolUse._input().asObject().orElseThrow();
870870 String result = handleToolUse(toolUse.name(), toolInput);
871871 
872872 messages.add(MessageParam.ofAssistantOfContentBlockParams(
873 content.stream().map(ContentBlock::toParam).toList()));
873 content.stream().map(ContentBlock::toParam).toList()));
874874 messages.add(MessageParam.ofUserOfContentBlockParams(List.of(ContentBlockParam.ofToolResult(
875 ToolResultBlockParam.builder()
876 .toolUseId(toolUse.id())
877 .content(result)
878 .build()))));
875 ToolResultBlockParam.builder()
876 .toolUseId(toolUse.id())
877 .content(result)
878 .build()))));
879879 
880880 Message followUpResponse = generateMessage(messages, 2048);
881881 
882882 ContentBlock followUpBlock = followUpResponse.content().stream()
883 .filter(ContentBlock::isText)
884 .findFirst()
885 .orElseThrow(() -> new IllegalStateException("An error occurred: Unexpected response type"));
883 .filter(ContentBlock::isText)
884 .findFirst()
885 .orElseThrow(() -> new IllegalStateException("An error occurred: Unexpected response type"));
886886 String responseText = followUpBlock.asText().text();
887887 messages.add(MessageParam.ofAssistant(responseText));
888888 return responseText;
889889 } else {
890890 ContentBlock textBlock = content.stream()
891 .filter(ContentBlock::isText)
892 .findFirst()
893 .orElseThrow(() -> new IllegalStateException("An error occurred: Unexpected response type"));
891 .filter(ContentBlock::isText)
892 .findFirst()
893 .orElseThrow(() -> new IllegalStateException("An error occurred: Unexpected response type"));
894894 String responseText = textBlock.asText().text();
895895 messages.add(MessageParam.ofAssistant(responseText));
896896 return responseText;
from line 901
901901 return switch (funcName) {
902902 case "get_quote" -> {
903903 double premium = getQuote(
904 funcParams.get("make").asStringOrThrow(),
905 funcParams.get("model").asStringOrThrow(),
906 ((Number) funcParams.get("year").asNumber().orElseThrow()).longValue(),
907 ((Number) funcParams.get("mileage").asNumber().orElseThrow()).longValue(),
908 ((Number) funcParams.get("driver_age").asNumber().orElseThrow()).longValue());
904 funcParams.get("make").asStringOrThrow(),
905 funcParams.get("model").asStringOrThrow(),
906 ((Number) funcParams.get("year").asNumber().orElseThrow()).longValue(),
907 ((Number) funcParams.get("mileage").asNumber().orElseThrow()).longValue(),
908 ((Number) funcParams.get("driver_age").asNumber().orElseThrow()).longValue());
909909 yield "Quote generated: $%.2f per month".formatted(premium);
910910 }
911911 default -> throw new IllegalArgumentException("An unexpected tool was used");

about-claude/use-case-guides/legal-summarization Changed · +75 / -75 lines

from line 88
8888 
8989 ```java Java
9090 List<String> detailsToExtract = List.of(
91 "Parties involved (sublessor, sublessee, and original lessor)",
92 "Property details (address, description, and permitted use)",
93 "Term and rent (start date, end date, monthly rent, and security deposit)",
94 "Responsibilities (utilities, maintenance, and repairs)",
95 "Consent and notices (landlord's consent, and notice requirements)",
96 "Special provisions (furniture, parking, and subletting restrictions)");
91 "Parties involved (sublessor, sublessee, and original lessor)",
92 "Property details (address, description, and permitted use)",
93 "Term and rent (start date, end date, monthly rent, and security deposit)",
94 "Responsibilities (utilities, maintenance, and repairs)",
95 "Consent and notices (landlord's consent, and notice requirements)",
96 "Special provisions (furniture, parking, and subletting restrictions)");
9797 ```
9898 
9999 ```php PHP
from line 277
277277 return text;
278278 }
279279 
280 // Download the sample sublease agreement from the summarization cookbook
280 // Download the sample sublease agreement from the Claude Cookbook's summarization recipe
281281 const response = await fetch(
282282 "https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf"
283283 );
from line 306
306306 return text;
307307 }
308308 
309 // Download the sample sublease agreement from the summarization cookbook
309 // Download the sample sublease agreement from the Claude Cookbook's summarization recipe
310310 using var httpClient = new HttpClient();
311311 var pdfData = await httpClient.GetByteArrayAsync(
312312 "https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf"
from line 356
356356 
357357 // extractDocumentText downloads the sample sublease agreement and extracts its text.
358358 func extractDocumentText() {
359 // Download the sample sublease agreement from the summarization cookbook
359 // Download the sample sublease agreement from the Claude Cookbook's summarization recipe
360360 response, err := http.Get("https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf")
361361 if err != nil {
362362 log.Fatal(err)
from line 402
402402 }
403403 
404404 void extractDocumentText() throws IOException, InterruptedException {
405 // Download the sample sublease agreement from the summarization cookbook
405 // Download the sample sublease agreement from the Claude Cookbook's summarization recipe
406406 HttpRequest request = HttpRequest.newBuilder(URI.create(
407 "https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf"))
408 .build();
407 "https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf"))
408 .build();
409409 byte[] pdfData;
410410 try (HttpClient httpClient = HttpClient.newHttpClient()) {
411411 pdfData = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray()).body();
from line 430
430430 return preg_replace('/\s+/', ' ', $text);
431431 };
432432 
433 // Download the sample sublease agreement from the summarization cookbook
433 // Download the sample sublease agreement from the Claude Cookbook's summarization recipe
434434 $pdfData = file_get_contents(
435435 'https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf'
436436 );
from line 455
455455 text.gsub(/\s+/, " ")
456456 end
457457 
458 # Download the sample sublease agreement from the summarization cookbook
458 # Download the sample sublease agreement from the Claude Cookbook's summarization recipe
459459 pdf_data = Net::HTTP.get(
460460 URI("https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf")
461461 )
from line 701
701701 
702702 // Prompt the model to summarize the sublease agreement
703703 String prompt = """
704 Summarize the following sublease agreement. Focus on these key aspects:
704 Summarize the following sublease agreement. Focus on these key aspects:
705705 
706 %s
706 %s
707707 
708 Provide the summary in bullet points nested within the XML header for each section. For example:
708 Provide the summary in bullet points nested within the XML header for each section. For example:
709709 
710 <parties involved>
711 - Sublessor: [Name]
712 // Add more details as needed
713 </parties involved>
710 <parties involved>
711 - Sublessor: [Name]
712 // Add more details as needed
713 </parties involved>
714714 
715 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
715 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
716716 
717 Sublease agreement text:
718 %s
719 """
720 .formatted(detailsToExtractStr, text);
717 Sublease agreement text:
718 %s
719 """
720 .formatted(detailsToExtractStr, text);
721721 
722722 Message response = client.messages().create(MessageCreateParams.builder()
723 .model(model)
724 .maxTokens(maxTokens)
725 .system("You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements.")
726 .addUserMessage(prompt)
727 .build());
723 .model(model)
724 .maxTokens(maxTokens)
725 .system("You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements.")
726 .addUserMessage(prompt)
727 .build());
728728 
729729 return response.content().stream()
730 .flatMap(block -> block.text().stream())
731 .findFirst()
732 .orElseThrow()
733 .text();
730 .flatMap(block -> block.text().stream())
731 .findFirst()
732 .orElseThrow()
733 .text();
734734 }
735735 
736736 void printSubleaseSummary() {
737737 String subleaseSummary = summarizeDocument(
738 documentText, detailsToExtract, Model.CLAUDE_OPUS_5_5, 16000);
738 documentText, detailsToExtract, Model.CLAUDE_OPUS_5_5, 16000);
739739 IO.println(subleaseSummary);
740740 }
741741 ```
from line 1157
11571157 ```java Java
11581158 List<String> chunkText(String text, int chunkSize) {
11591159 return IntStream.iterate(0, start -> start < text.length(), start -> start + chunkSize)
1160 .mapToObj(start -> text.substring(start, Math.min(start + chunkSize, text.length())))
1161 .toList();
1160 .mapToObj(start -> text.substring(start, Math.min(start + chunkSize, text.length())))
1161 .toList();
11621162 }
11631163 
11641164 String summarizeLongDocument(String text, List<String> detailsToExtract, Model model, long maxTokens) {
from line 1167
11671167 
11681168 // Iterate over chunks and summarize each one
11691169 String chunkSummaries = chunkText(text, 20000).stream()
1170 .map(chunk -> summarizeDocument(chunk, detailsToExtract, model, maxTokens))
1171 .collect(Collectors.joining());
1170 .map(chunk -> summarizeDocument(chunk, detailsToExtract, model, maxTokens))
1171 .collect(Collectors.joining());
11721172 
11731173 String finalSummaryPrompt = """
11741174 
1175 You are looking at the chunked summaries of multiple documents that are all related.
1176 Combine the following summaries of the document from different truthful sources into a coherent overall summary:
1175 You are looking at the chunked summaries of multiple documents that are all related.
1176 Combine the following summaries of the document from different truthful sources into a coherent overall summary:
11771177 
1178 <chunked_summaries>
1179 %s
1180 </chunked_summaries>
1178 <chunked_summaries>
1179 %s
1180 </chunked_summaries>
11811181 
1182 Focus on these key aspects:
1183 %s
1182 Focus on these key aspects:
1183 %s
11841184 
1185 Provide the summary in bullet points nested within the XML header for each section. For example:
1185 Provide the summary in bullet points nested within the XML header for each section. For example:
11861186 
1187 <parties involved>
1188 - Sublessor: [Name]
1189 // Add more details as needed
1190 </parties involved>
1187 <parties involved>
1188 - Sublessor: [Name]
1189 // Add more details as needed
1190 </parties involved>
11911191 
1192 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
1193 """
1194 .formatted(chunkSummaries, detailsToExtractStr);
1192 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
1193 """
1194 .formatted(chunkSummaries, detailsToExtractStr);
11951195 
11961196 Message response = client.messages().create(MessageCreateParams.builder()
1197 .model(model)
1198 .maxTokens(maxTokens)
1199 .system("You are a legal expert that summarizes notes on one document.")
1200 .addUserMessage(finalSummaryPrompt)
1201 .build());
1197 .model(model)
1198 .maxTokens(maxTokens)
1199 .system("You are a legal expert that summarizes notes on one document.")
1200 .addUserMessage(finalSummaryPrompt)
1201 .build());
12021202 
12031203 return response.content().stream()
1204 .flatMap(block -> block.text().stream())
1205 .findFirst()
1206 .orElseThrow()
1207 .text();
1204 .flatMap(block -> block.text().stream())
1205 .findFirst()
1206 .orElseThrow()
1207 .text();
12081208 }
12091209 
12101210 void printLongSummary() {
12111211 String longSummary = summarizeLongDocument(
1212 documentText, detailsToExtract, Model.CLAUDE_OPUS_5_5, 16000);
1212 documentText, detailsToExtract, Model.CLAUDE_OPUS_5_5, 16000);
12131213 IO.println(longSummary);
12141214 }
12151215 ```

about-claude/use-case-guides/ticket-routing Changed · +6 / -2 lines

from line 905
905905 Message message = client.messages().create(MessageCreateParams.builder()
906906 .model(DEFAULT_MODEL)
907907 .maxTokens(2048L)
908 .outputConfig(OutputConfig.builder().effort(OutputConfig.Effort.LOW).build())
908 .outputConfig(OutputConfig.builder()
909 .effort(OutputConfig.Effort.LOW)
910 .build())
909911 .addUserMessage(classificationPrompt)
910912 .build());
911913 String reasoningAndIntent = message.content().stream()
from line 1275
12731275 Message message = client.messages().create(MessageCreateParams.builder()
12741276 .model(DEFAULT_MODEL)
12751277 .maxTokens(2048L)
1276 .outputConfig(OutputConfig.builder().effort(OutputConfig.Effort.LOW).build())
1278 .outputConfig(OutputConfig.builder()
1279 .effort(OutputConfig.Effort.LOW)
1280 .build())
12771281 .addUserMessage(classificationPrompt)
12781282 .build());
12791283 // Get the usage statistics for the API call for how many input and output tokens were used.

agents-and-tools/mcp-connector Changed · +29 / -27 lines

from line 1544
15441544 
15451545 // Connect to an MCP server
15461546 StdioClientTransport transport = new StdioClientTransport(
1547 ServerParameters.builder("mcp-server").build(), McpJsonDefaults.getMapper());
1547 ServerParameters.builder("mcp-server").build(), McpJsonDefaults.getMapper());
15481548 
15491549 try (McpSyncClient mcpClient = McpClient.sync(transport)
15501550 .clientInfo(new McpSchema.Implementation("my-client", "1.0.0"))
from line 1556
15561556 List<McpBetaTool> betaTools = BetaMcp.mcpTools(mcpClient.listTools().tools(), mcpClient);
15571557 
15581558 MessageCreateParams params = MessageCreateParams.builder()
1559 .model(Model.CLAUDE_OPUS_5_5)
1560 .maxTokens(1024L)
1561 .addUserMessage("What tools do you have available?")
1562 .addTools(betaTools)
1563 .build();
1559 .model(Model.CLAUDE_OPUS_5_5)
1560 .maxTokens(1024L)
1561 .addUserMessage("What tools do you have available?")
1562 .addTools(betaTools)
1563 .build();
15641564 
1565 // The runner yields one message per assistant turn; the last is the final response
1565 // The runner yields one message for each assistant turn; the last is the final response
15661566 BetaToolRunner runner = anthropic.beta().messages().toolRunner(params);
15671567 BetaMessage finalMessage = null;
15681568 for (BetaMessage message : runner) {
from line 1692
16921692 
16931693 ```java Java
16941694 McpSchema.GetPromptResult prompt = mcpClient.getPrompt(
1695 new McpSchema.GetPromptRequest("my-prompt", Map.of()));
1695 new McpSchema.GetPromptRequest("my-prompt", Map.of()));
16961696 
16971697 BetaMessage response = anthropic.beta().messages().create(MessageCreateParams.builder()
1698 .model(Model.CLAUDE_OPUS_5_5)
1699 .maxTokens(1024L)
1700 .messages(BetaMcp.mcpMessages(prompt.messages()))
1701 .build());
1698 .model(Model.CLAUDE_OPUS_5_5)
1699 .maxTokens(1024L)
1700 .messages(BetaMcp.mcpMessages(prompt.messages()))
1701 .build());
17021702 
17031703 IO.println(response);
17041704 ```
from line 1821
18211821 var fileResource = await mcpClient.ReadResourceAsync("file:///path/to/data.json");
18221822 var (filename, data, mediaType) = BetaMcp.ResourceToFile(fileResource);
18231823 
1824 // Build the file part explicitly so the resource's filename and MIME type
1824 // Build the file part explicitly so the resource's file name and MIME type
18251825 // carry through to the upload.
18261826 var file = new BinaryContent { Stream = new MemoryStream(data), FileName = filename };
18271827 if (mediaType is not null)
from line 1885
18851885 ```java Java
18861886 // As a content block in a message
18871887 McpSchema.ReadResourceResult resource = mcpClient.readResource(
1888 new McpSchema.ReadResourceRequest("file:///path/to/doc.txt"));
1888 new McpSchema.ReadResourceRequest("file:///path/to/doc.txt"));
18891889 
18901890 List<BetaContentBlockParam> content =
1891 new ArrayList<>(BetaMcp.mcpResourceContents(resource));
1891 new ArrayList<>(BetaMcp.mcpResourceContents(resource));
18921892 content.add(BetaContentBlockParam.ofText(
1893 BetaTextBlockParam.builder().text("Summarize this document").build()));
1893 BetaTextBlockParam.builder()
1894 .text("Summarize this document")
1895 .build()));
18941896 
18951897 BetaMessage response = anthropic.beta().messages().create(MessageCreateParams.builder()
1896 .model(Model.CLAUDE_OPUS_5_5)
1897 .maxTokens(1024L)
1898 .addUserMessageOfContentBlockParams(content)
1899 .build());
1898 .model(Model.CLAUDE_OPUS_5_5)
1899 .maxTokens(1024L)
1900 .addUserMessageOfContentBlockParams(content)
1901 .build());
19001902 
19011903 IO.println(response);
19021904 
19031905 // As a file upload
19041906 McpSchema.ReadResourceResult fileResource = mcpClient.readResource(
1905 new McpSchema.ReadResourceRequest("file:///path/to/data.json"));
1907 new McpSchema.ReadResourceRequest("file:///path/to/data.json"));
19061908 
19071909 McpResourceFile resourceFile = BetaMcp.mcpResourceFiles(fileResource).getFirst();
19081910 
1909 // Build the file part explicitly so the resource's filename and MIME type
1911 // Build the file part explicitly so the resource's file name and MIME type
19101912 // carry through to the upload.
19111913 MultipartField.Builder<InputStream> fileField = MultipartField.<InputStream>builder()
1912 .value(new ByteArrayInputStream(resourceFile.content()))
1913 .filename(resourceFile.filename());
1914 .value(new ByteArrayInputStream(resourceFile.content()))
1915 .filename(resourceFile.filename());
19141916 resourceFile.mimeType().ifPresent(fileField::contentType);
19151917 
19161918 var uploaded = anthropic.files().upload(FileUploadParams.builder()
1917 .file(fileField.build())
1918 .build());
1919 .file(fileField.build())
1920 .build());
19191921 
19201922 IO.println(uploaded.id());
19211923 ```

agents-and-tools/tool-use/advisor-tool Changed · +38 / -20 lines

from line 224
224224 'content' => 'Build a concurrent worker pool in Go with graceful shutdown.',
225225 ],
226226 ],
227 model: 'claude-sonnet-5',
227 model: Model::CLAUDE_SONNET_5,
228228 tools: [
229229 [
230230 'type' => 'advisor_20260301',
from line 600
600600 
601601 List<BetaToolUnion> tools = List.of(
602602 BetaToolUnion.ofAdvisorTool20260301(
603 BetaAdvisorTool20260301.builder().model(Model.CLAUDE_OPUS_5_5).build()));
603 BetaAdvisorTool20260301.builder()
604 .model(Model.CLAUDE_OPUS_5_5)
605 .build()));
604606 
605607 List<BetaMessageParam> messages = new ArrayList<>();
606608 messages.add(BetaMessageParam.ofUser(
from line 745
743745 
744746 
745747 def run_your_tools(content):
746 # Replace with your tool dispatch. Returns one tool_result block per tool_use block.
748 # Replace with your tool dispatch. Returns one tool_result block for each tool_use block.
747749 return [
748750 {
749751 "type": "tool_result",
from line 785
783785 if response.stop_reason == "pause_turn":
784786 continue # server tool pending; re-send to let the API complete it
785787 if response.stop_reason != "tool_use":
786 break # end_turn, or a stop such as max_tokens that needs handling (see below)
788 # end_turn, or a stop such as max_tokens that needs handling
789 # (explained after this example)
790 break
787791 
788792 results = run_your_tools(response.content) # list of tool_result blocks
789793 if results:
from line 810
806810 function runYourTools(
807811 content: Anthropic.Beta.Messages.BetaContentBlock[]
808812 ): Anthropic.Beta.Messages.BetaToolResultBlockParam[] {
809 // Replace with your tool dispatch. Returns one tool_result block per tool_use block.
813 // Replace with your tool dispatch. Returns one tool_result block for each tool_use block.
810814 return content
811815 .filter((block) => block.type === "tool_use")
812816 .map((block) => ({
from line 846
842846 continue; // server tool pending; re-send to let the API complete it
843847 }
844848 if (response.stop_reason !== "tool_use") {
845 break; // end_turn, or a stop such as max_tokens that needs handling (see below)
849 // end_turn, or a stop such as max_tokens that needs handling
850 // (explained after this example)
851 break;
846852 }
847853 
848854 const results = runYourTools(response.content); // list of tool_result blocks
from line 875
869875 + "now before committing to an approach.";
870876 const int MaxTurns = 10; // agent loop cap
871877 
872 // Replace with your tool dispatch. Returns one tool_result block per tool_use block.
878 // Replace with your tool dispatch. Returns one tool_result block for each tool_use block.
873879 List<BetaContentBlockParam> RunYourTools(IReadOnlyList<BetaContentBlock> content)
874880 {
875881 List<BetaContentBlockParam> results = [];
from line 928
922928 }
923929 if (response.StopReason != BetaStopReason.ToolUse)
924930 {
925 break; // end_turn, or a stop such as max_tokens that needs handling (see below)
931 // end_turn, or a stop such as max_tokens that needs handling
932 // (explained after this example)
933 break;
926934 }
927935 
928936 var results = RunYourTools(response.Content); // list of tool_result blocks
from line 955
947955 maxTurns = 10 // agent loop cap
948956 )
949957 
950 // Replace with your tool dispatch. Returns one tool_result block per tool_use block.
958 // Replace with your tool dispatch. Returns one tool_result block for each tool_use block.
951959 func runYourTools(content []anthropic.BetaContentBlockUnion) []anthropic.BetaContentBlockParamUnion {
952960 var results []anthropic.BetaContentBlockParamUnion
953961 for _, block := range content {
from line 1006
9981006 continue // server tool pending; re-send to let the API complete it
9991007 }
10001008 if response.StopReason != anthropic.BetaStopReasonToolUse {
1001 break // end_turn, or a stop such as max_tokens that needs handling (see below)
1009 // end_turn, or a stop such as max_tokens that needs handling
1010 // (explained after this example)
1011 break
10021012 }
10031013 
10041014 results := runYourTools(response.Content) // list of tool_result blocks
from line 1043
10331043 + "now before committing to an approach.";
10341044 static final int MAX_TURNS = 10; // agent loop cap
10351045 
1036 // Replace with your tool dispatch. Returns one tool_result block per tool_use block.
1046 // Replace with your tool dispatch. Returns one tool_result block for each tool_use block.
10371047 List<BetaContentBlockParam> runYourTools(List<BetaContentBlock> content) {
10381048 List<BetaContentBlockParam> results = new ArrayList<>();
10391049 for (BetaContentBlock block : content) {
from line 1063
10531063 
10541064 List<BetaToolUnion> tools = List.of(
10551065 BetaToolUnion.ofAdvisorTool20260301(
1056 BetaAdvisorTool20260301.builder().model(Model.CLAUDE_OPUS_5_5).build())
1066 BetaAdvisorTool20260301.builder()
1067 .model(Model.CLAUDE_OPUS_5_5)
1068 .build())
10571069 // ... your other tools
10581070 );
10591071 String task = "Build a concurrent worker pool in Go with graceful shutdown.";
from line 1091
10791091 continue; // server tool pending; re-send to let the API complete it
10801092 }
10811093 if (!BetaStopReason.TOOL_USE.equals(stopReason)) {
1082 break; // end_turn, or a stop such as max_tokens that needs handling (see below)
1094 // end_turn, or a stop such as max_tokens that needs handling
1095 // (explained after this example)
1096 break;
10831097 }
10841098 
10851099 List<BetaContentBlockParam> results = runYourTools(response.content()); // list of tool_result blocks
from line 1112
10981112 $client = new Client();
10991113 
11001114 const NUDGE_TURN = 2; // inject before this assistant turn if no advisor call yet
1101 const NUDGE_TEXT = "You have not consulted the advisor yet. If the task has a non-obvious "
1115 const NUDGE_TEXT = 'You have not consulted the advisor yet. If the task has a non-obvious '
11021116 . "design decision or a failure mode you haven't ruled out, call advisor "
1103 . "now before committing to an approach.";
1117 . 'now before committing to an approach.';
11041118 const MAX_TURNS = 10; // agent loop cap
11051119 
1106 // Replace with your tool dispatch. Returns one tool_result block per tool_use block.
1120 // Replace with your tool dispatch. Returns one tool_result block for each tool_use block.
11071121 function runYourTools(array $content): array
11081122 {
11091123 $results = [];
from line 1159
11451159 continue; // server tool pending; re-send to let the API complete it
11461160 }
11471161 if ($response->stopReason !== 'tool_use') {
1148 break; // end_turn, or a stop such as max_tokens that needs handling (see below)
1162 // end_turn, or a stop such as max_tokens that needs handling
1163 // (explained after this example)
1164 break;
11491165 }
11501166 
11511167 $results = runYourTools($response->content); // list of tool_result blocks
from line 1185
11691185 "now before committing to an approach."
11701186 MAX_TURNS = 10 # agent loop cap
11711187 
1172 # Replace with your tool dispatch. Returns one tool_result block per tool_use block.
1188 # Replace with your tool dispatch. Returns one tool_result block for each tool_use block.
11731189 def run_your_tools(content)
11741190 content.filter_map do |block|
11751191 next unless block.type == :tool_use
from line 1214
11981214 block.type == :server_tool_use && block.name == :advisor
11991215 end
12001216 next if response.stop_reason == :pause_turn # server tool pending; re-send to let the API complete it
1201 break unless response.stop_reason == :tool_use # end_turn, or a stop such as max_tokens that needs handling (see below)
1217 # end_turn, or a stop such as max_tokens that needs handling
1218 # (explained after this example)
1219 break unless response.stop_reason == :tool_use
12021220 
12031221 results = run_your_tools(response.content) # list of tool_result blocks
12041222 messages << { role: "user", content: results } unless results.empty?
from line 1242
12241242 
12251243The `server_tool_use` block with `name: "advisor"` signals that an advisor call is starting. The pause starts when that block closes (`content_block_stop`). During the pause, the stream is quiet except for standard SSE `ping` keepalives emitted roughly every 30 seconds. Short advisor calls might show no pings.
12261244 
1227When the advisor finishes, the `advisor_tool_result` arrives fully formed in a single `content_block_start` event (no deltas). Executor output then resumes streaming.
1245When the advisor call completes, the `advisor_tool_result` arrives fully formed in a single `content_block_start` event (no deltas). Executor output then resumes streaming.
12281246 
12291247A `message_delta` event follows with the updated `usage.iterations` array reflecting the advisor's token counts.
12301248 

agents-and-tools/tool-use/bash-tool Changed · +21 / -19 lines

from line 146
146146 ```
147147 
148148 ```php PHP
149 use Anthropic\Messages\Model;
149150 use Anthropic\Messages\ToolBash20250124;
150151 
151152 $client = new Client();
152153 
153154 $response = $client->messages->create(
154 model: 'claude-opus-5-5',
155 model: Model::CLAUDE_OPUS_5_5,
155156 maxTokens: 1024,
156157 tools: [new ToolBash20250124()],
157158 messages: [
from line 307
306307 
307308 def execute_command(self, command):
308309 """Run a command in the session and return its output."""
309 sentinel = f"__CLAUDE_BASH_DONE_{uuid.uuid4().hex}__" # unique per call
310 sentinel = f"__CLAUDE_BASH_DONE_{uuid.uuid4().hex}__" # unique for each call
310311 self.process.stdin.write(f"{command}\necho {sentinel}\n")
311312 self.process.stdin.flush()
312313 
from line 353
352353 
353354 // Run a command in the session and return its output.
354355 executeCommand(command: string): Promise<string> {
355 const sentinel = `__CLAUDE_BASH_DONE_${randomUUID()}__`; // unique per call
356 const sentinel = `__CLAUDE_BASH_DONE_${randomUUID()}__`; // unique for each call
356357 const output: string[] = [];
357358 const result = new Promise<string>((resolve) => {
358359 const onLine = (line: string): void => {
from line 417
416417 // Run a command in the session and return its output.
417418 public string ExecuteCommand(string command)
418419 {
419 var sentinel = $"__CLAUDE_BASH_DONE_{Guid.NewGuid():N}__"; // unique per call
420 var sentinel = $"__CLAUDE_BASH_DONE_{Guid.NewGuid():N}__"; // unique for each call
420421 Process.StandardInput.Write($"{command}\necho {sentinel}\n");
421422 Process.StandardInput.Flush();
422423 
from line 484
483484 func (s *BashSession) ExecuteCommand(command string) string {
484485 buf := make([]byte, 16)
485486 rand.Read(buf)
486 sentinel := fmt.Sprintf("__CLAUDE_BASH_DONE_%s__", hex.EncodeToString(buf)) // unique per call
487 sentinel := fmt.Sprintf("__CLAUDE_BASH_DONE_%s__", hex.EncodeToString(buf)) // unique for each call
487488 fmt.Fprintf(s.stdin, "%s\necho %s\n", command, sentinel)
488489 
489490 var output strings.Builder
from line 548
547548 
548549 // Run a command in the session and return its output.
549550 String executeCommand(String command) throws IOException {
550 String sentinel = "__CLAUDE_BASH_DONE_" + UUID.randomUUID() + "__"; // unique per call
551 String sentinel = "__CLAUDE_BASH_DONE_" + UUID.randomUUID() + "__"; // unique for each call
551552 stdin.write(command + "\necho " + sentinel + "\n");
552553 stdin.flush();
553554 
from line 605
604605 // Run a command in the session and return its output.
605606 public function executeCommand(string $command): string
606607 {
607 $sentinel = '__CLAUDE_BASH_DONE_' . bin2hex(random_bytes(16)) . '__'; // unique per call
608 $sentinel = '__CLAUDE_BASH_DONE_' . bin2hex(random_bytes(16)) . '__'; // unique for each call
608609 fwrite($this->stdin, "{$command}\necho {$sentinel}\n");
609610 fflush($this->stdin);
610611 
from line 628
627628 }
628629 
629630 $session = new BashSession();
630 echo $session->executeCommand("cd /tmp && pwd");
631 echo $session->executeCommand("pwd"); // still /tmp: the session kept its state
631 echo $session->executeCommand('cd /tmp && pwd');
632 echo $session->executeCommand('pwd'); // still /tmp: the session kept its state
632633 ```
633634 
634635 ```ruby Ruby
from line 646
645646 
646647 # Run a command in the session and return its output.
647648 def execute_command(command)
648 sentinel = "__CLAUDE_BASH_DONE_#{SecureRandom.hex(16)}__" # unique per call
649 sentinel = "__CLAUDE_BASH_DONE_#{SecureRandom.hex(16)}__" # unique for each call
649650 @stdin.write("#{command}\necho #{sentinel}\n")
650651 @stdin.flush
651652 
from line 698
697698 command = content.input.get("command")
698699 result = bash_session.execute_command(command)
699700 
700 # One tool_result per tool_use block, all returned in the next user message
701 # One tool_result for each tool_use block, all returned in the next user message
701702 tool_results.append(
702703 {"type": "tool_result", "tool_use_id": content.id, "content": result}
703704 )
from line 716
715716 result = await bashSession.executeCommand(block.input.command ?? "");
716717 }
717718 
718 // One tool_result per tool_use block, all returned in the next user message
719 // One tool_result for each tool_use block, all returned in the next user message
719720 toolResults.push({ type: "tool_result", tool_use_id: block.id, content: result });
720721 }
721722 }
from line 740
739740 result = bashSession.ExecuteCommand(command);
740741 }
741742 
742 // One tool_result per tool_use block, all returned in the next user message
743 // One tool_result for each tool_use block, all returned in the next user message
743744 toolResults.Add(new ToolResultBlockParam { ToolUseID = toolUse.ID, Content = result });
744745 }
745746 }
from line 766
765766 result = bashSession.ExecuteCommand(input.Command)
766767 }
767768 
768 // One tool_result per tool_use block, all returned in the next user message
769 // One tool_result for each tool_use block, all returned in the next user message
769770 toolResults = append(toolResults, anthropic.NewToolResultBlock(block.ID, result, false))
770771 }
771772 }
from line 785
784785 result = bashSession.executeCommand(command);
785786 }
786787 
787 // One tool_result per tool_use block, all returned in the next user message
788 // One tool_result for each tool_use block, all returned in the next user message
788789 toolResults.add(Map.of("type", "tool_result", "tool_use_id", block.id(), "content", result));
789790 }
790791 }
from line 802
801802 $result = $bashSession->executeCommand($block->input['command']);
802803 }
803804 
804 // One tool_result per tool_use block, all returned in the next user message
805 // One tool_result for each tool_use block, all returned in the next user message
805806 $toolResults[] = ['type' => 'tool_result', 'tool_use_id' => $block->id, 'content' => $result];
806807 }
807808 }
from line 821
820821 bash_session.execute_command(block.input[:command])
821822 end
822823 
823 # One tool_result per tool_use block, all returned in the next user message
824 # One tool_result for each tool_use block, all returned in the next user message
824825 tool_results << {type: "tool_result", tool_use_id: block.id, content: result}
825826 end
826827 ```
from line 1107
11061107 ```
11071108 
11081109 ```php PHP
1110 use Anthropic\Messages\Model;
11091111 use Anthropic\Messages\ToolBash20250124;
11101112 
11111113 $client = new Client();
11121114 
11131115 $response = $client->messages->create(
1114 model: 'claude-opus-5-5',
1116 model: Model::CLAUDE_OPUS_5_5,
11151117 maxTokens: 1024,
11161118 tools: [new ToolBash20250124()],
11171119 messages: [
from line 1606
16041606 // readable output before each fgets() so the loop can check the deadline.
16051607 function executeWithTimeout(BashSession $session, string $command, int $timeout = 30): string
16061608 {
1607 $sentinel = '__CLAUDE_BASH_DONE_' . bin2hex(random_bytes(16)) . '__'; // unique per call
1609 $sentinel = '__CLAUDE_BASH_DONE_' . bin2hex(random_bytes(16)) . '__'; // unique for each call
16081610 fwrite($session->stdin, "{$command}\necho {$sentinel}\n");
16091611 fflush($session->stdin);
16101612 

agents-and-tools/tool-use/browser-use-tool Changed · +28 / -20 lines

from line 20
2020 - claude-haiku-5-5
2121 supportedPlatforms:
2222 Claude API: ga
23 Google Cloud: ga
23 Claude Models on Google Cloud (Google managed): ga
2424---
2525 
2626<Note>
from line 173
173173 messages: [
174174 ['role' => 'user', 'content' => 'Open example.com/docs and tell me how to get started.'],
175175 ],
176 model: 'claude-opus-5-5',
176 model: Model::CLAUDE_OPUS_5_5,
177177 tools: [
178178 ['type' => 'browser_toolset_20260801'],
179179 ],
from line 562
562562 
563563 ToolResultBlockParam.Content captureScreenshot() {
564564 ImageBlockParam image = ImageBlockParam.builder()
565 .source(Base64ImageSource.builder()
566 .mediaType(Base64ImageSource.MediaType.IMAGE_PNG)
567 .data(PLACEHOLDER_PNG)
568 .build())
569 .build();
565 .source(Base64ImageSource.builder()
566 .mediaType(Base64ImageSource.MediaType.IMAGE_PNG)
567 .data(PLACEHOLDER_PNG)
568 .build())
569 .build();
570570 return ToolResultBlockParam.Content.ofBlocks(
571 List.of(ToolResultBlockParam.Content.Block.ofImage(image)));
571 List.of(ToolResultBlockParam.Content.Block.ofImage(image)));
572572 }
573573 
574574 String navigate(String url) {
from line 577
577577 
578578 String readPage() {
579579 return """
580 link "Docs" [ref_1]
581 button "Search" [ref_2]""";
580 link "Docs" [ref_1]
581 button "Search" [ref_2]""";
582582 }
583583 
584584 String clickRef(String ref) {
from line 604
604604 case "left_click" -> {
605605 // target is {"type": "ref", "ref": "ref_1"} or {"type": "coordinate", "x": 640, "y": 380}
606606 Map<String, JsonValue> target =
607 (Map<String, JsonValue>) input.get("target").asObject().get();
607 (Map<String, JsonValue>) input.get("target").asObject().get();
608608 if (target.get("type").asStringOrThrow().equals("ref")) {
609609 yield clickRef(target.get("ref").asStringOrThrow());
610610 }
from line 901
901901 }
902902 
903903 // processToolCalls runs the browser actions in Claude's response in order and
904 // builds one tool_result per tool_use block. After the first failure it skips
904 // builds one tool_result for each tool_use block. After the first failure it skips
905905 // the rest: Claude planned them assuming the earlier actions succeeded.
906906 func processToolCalls(response *anthropic.Message) []anthropic.ContentBlockParamUnion {
907907 var toolResults []anthropic.ContentBlockParamUnion
from line 941
941941 /** Every result answering a browser toolset member echoes toolset_name. */
942942 ToolResultBlockParam.Builder browserResult(ToolUseBlock toolUse) {
943943 return ToolResultBlockParam.builder()
944 .toolUseId(toolUse.id())
945 .toolsetName("browser");
944 .toolUseId(toolUse.id())
945 .toolsetName("browser");
946946 }
947947 
948948 /**
949949 * Run the browser actions in Claude's response in order and build one
950 * tool_result per tool_use block. After the first failure, skip the rest:
950 * tool_result for each tool_use block. After the first failure, skip the rest:
951951 * Claude planned them assuming the earlier actions succeeded.
952952 */
953953 List<ContentBlockParam> processToolCalls(Message response) {
from line 961
961961 ToolUseBlock toolUse = block.asToolUse();
962962 ToolResultBlockParam result;
963963 if (failed) {
964 result = browserResult(toolUse).content(HALT_TEXT).isError(true).build();
964 result = browserResult(toolUse)
965 .content(HALT_TEXT)
966 .isError(true)
967 .build();
965968 } else {
966969 try {
967970 Map<String, JsonValue> input =
968 (Map<String, JsonValue>) toolUse._input().asObject().get();
971 (Map<String, JsonValue>) toolUse._input().asObject().get();
969972 ToolResultBlockParam.Content output = handleBrowserAction(toolUse.name(), input);
970973 // A real executor also adds a browser_state block to navigation and
971974 // tab-management results; see "Track tabs with browser_state" on this page.
972 result = browserResult(toolUse).content(output).build();
975 result = browserResult(toolUse)
976 .content(output)
977 .build();
973978 } catch (RuntimeException e) {
974979 failed = true;
975 result = browserResult(toolUse).content("Error: " + e.getMessage()).isError(true).build();
980 result = browserResult(toolUse)
981 .content("Error: " + e.getMessage())
982 .isError(true)
983 .build();
976984 }
977985 }
978986 toolResults.add(ContentBlockParam.ofToolResult(result));
from line 1024
10161024 NOT_EXECUTED = "Not executed: an earlier action in this turn failed."
10171025 
10181026 # Run the browser actions in Claude's response in order and build one
1019 # tool_result per tool_use block. After the first failure, skip the rest:
1027 # tool_result for each tool_use block. After the first failure, skip the rest:
10201028 # Claude planned them assuming the earlier actions succeeded.
10211029 def process_tool_calls(response)
10221030 tool_results = []

agents-and-tools/tool-use/build-a-tool-using-agent Changed · +61 / -61 lines

from line 68
6868 }
6969 ]
7070 
71 # Send the user's request along with the tool definition. Claude decides
71 # Send the user's request along with the tool definition. Claude determines
7272 # whether to call the tool based on the request and the tool description.
7373 response = client.messages.create(
7474 model="claude-opus-5-5",
from line 87
8787 # and the content array contains a tool_use block alongside any text.
8888 print(f"stop_reason: {response.stop_reason}")
8989 
90 # Find the tool_use block. A response may contain text blocks before the
90 # Find the tool_use block. A response might contain text blocks before the
9191 # tool_use block, so scan the content array rather than assuming position.
9292 tool_use = next(block for block in response.content if block.type == "tool_use")
9393 print(f"Tool: {tool_use.name}")
9494 print(f"Input: {tool_use.input}")
9595 
96 # Execute the tool. In a real system this would call your calendar API.
96 # Run the tool. In a real system this would call your calendar API.
9797 # Here the result is hardcoded to keep the example self-contained.
9898 result = {"event_id": "evt_123", "status": "created"}
9999 
100100 # Send the result back. The tool_result block goes in a user message and
101 # its tool_use_id must match the id from the tool_use block above. The
101 # its tool_use_id must match the ID from the earlier tool_use block. The
102102 # assistant's previous response is included so Claude has the full history.
103103 followup = client.messages.create(
104104 model="claude-opus-5-5",
from line 124
124124 ],
125125 )
126126 
127 # With the tool result in hand, Claude produces a final natural-language
127 # With the tool result, Claude produces a final natural-language
128128 # answer and stop_reason becomes "end_turn".
129129 print(f"stop_reason: {followup.stop_reason}")
130130 final_text = next(block for block in followup.content if block.type == "text")
from line 170
170170 }
171171 ];
172172 
173 // Send the user's request along with the tool definition. Claude decides
173 // Send the user's request along with the tool definition. Claude determines
174174 // whether to call the tool based on the request and the tool description.
175175 const response = await client.messages.create({
176176 model: "claude-opus-5-5",
from line 190
190190 // and the content array contains a tool_use block alongside any text.
191191 console.log(`stop_reason: ${response.stop_reason}`);
192192 
193 // Find the tool_use block. A response may contain text blocks before the
193 // Find the tool_use block. A response might contain text blocks before the
194194 // tool_use block, so scan the content array rather than assuming position.
195195 const toolUse = response.content.find((block) => block.type === "tool_use")!;
196196 console.log(`Tool: ${toolUse.name}`);
197197 console.log(`Input: ${JSON.stringify(toolUse.input)}`);
198198 
199 // Execute the tool. In a real system this would call your calendar API.
199 // Run the tool. In a real system this would call your calendar API.
200200 // Here the result is hardcoded to keep the example self-contained.
201201 const result = { event_id: "evt_123", status: "created" };
202202 
203203 // Send the result back. The tool_result block goes in a user message and
204 // its tool_use_id must match the id from the tool_use block above. The
204 // its tool_use_id must match the ID from the earlier tool_use block. The
205205 // assistant's previous response is included so Claude has the full history.
206206 const followup = await client.messages.create({
207207 model: "claude-opus-5-5",
from line 228
228228 ]
229229 });
230230 
231 // With the tool result in hand, Claude produces a final natural-language
231 // With the tool result, Claude produces a final natural-language
232232 // answer and stop_reason becomes "end_turn".
233233 console.log(`stop_reason: ${followup.stop_reason}`);
234234 for (const block of followup.content) {
from line 289
289289 }),
290290 ];
291291 
292 // Ask for at most one tool call per turn so the single-turn flow below
293 // stays predictable.
292 // Ask for at most one tool call in each turn so the single-turn flow
293 // that follows stays predictable.
294294 var toolChoice = new ToolChoice(new ToolChoiceAuto { DisableParallelToolUse = true });
295295 
296296 const string userPrompt =
297297 "Schedule a 30-minute sync with [email protected] and [email protected] on Monday, March 30, 2026 at 10am.";
298298 
299 // Send the user's request along with the tool definition. Claude decides
299 // Send the user's request along with the tool definition. Claude determines
300300 // whether to call the tool based on the request and the tool description.
301301 var response = await client.Messages.Create(new MessageCreateParams
302302 {
from line 311
311311 // and the content array contains a tool_use block alongside any text.
312312 Console.WriteLine($"stop_reason: {response.StopReason?.Raw()}");
313313 
314 // Find the tool_use block. A response may contain text blocks before the
314 // Find the tool_use block. A response might contain text blocks before the
315315 // tool_use block, so scan the content array rather than assuming position.
316316 ToolUseBlock? toolUse = null;
317317 foreach (var block in response.Content)
from line 325
325325 Console.WriteLine($"Tool: {toolUse!.Name}");
326326 Console.WriteLine($"Input: {JsonSerializer.Serialize(toolUse.Input)}");
327327 
328 // Execute the tool. In a real system this would call your calendar API.
328 // Run the tool. In a real system this would call your calendar API.
329329 // Here the result is hardcoded to keep the example self-contained.
330330 var result = """{"event_id": "evt_123", "status": "created"}""";
331331 
332332 // Send the result back. The tool_result block goes in a user message and
333 // its tool_use_id must match the id from the tool_use block above. The
333 // its tool_use_id must match the ID from the earlier tool_use block. The
334334 // assistant's previous response is included so Claude has the full history.
335335 List<ContentBlockParam> toolResults =
336336 [
from line 355
355355 ],
356356 });
357357 
358 // With the tool result in hand, Claude produces a final natural-language
358 // With the tool result, Claude produces a final natural-language
359359 // answer and stop_reason becomes "end_turn".
360360 Console.WriteLine($"stop_reason: {followup.StopReason?.Raw()}");
361361 foreach (var block in followup.Content)
from line 415
415415 }},
416416 }
417417 
418 // Ask for at most one tool call per turn so the single-turn flow below
419 // stays predictable.
418 // Ask for at most one tool call in each turn so the single-turn flow
419 // that follows stays predictable.
420420 toolChoice := anthropic.ToolChoiceUnionParam{
421421 OfAuto: &anthropic.ToolChoiceAutoParam{DisableParallelToolUse: anthropic.Bool(true)},
422422 }
from line 425
425425 "Schedule a 30-minute sync with [email protected] and [email protected] on Monday, March 30, 2026 at 10am.",
426426 ))
427427 
428 // Send the user's request along with the tool definition. Claude decides
428 // Send the user's request along with the tool definition. Claude determines
429429 // whether to call the tool based on the request and the tool description.
430430 response, err := client.Messages.New(ctx, anthropic.MessageNewParams{
431431 Model: anthropic.ModelClaudeOpus5_5,
from line 442
442442 // and the content array contains a tool_use block alongside any text.
443443 fmt.Printf("stop_reason: %s\n", response.StopReason)
444444 
445 // Find the tool_use block. A response may contain text blocks before the
445 // Find the tool_use block. A response might contain text blocks before the
446446 // tool_use block, so scan the content array rather than assuming position.
447447 var toolUse anthropic.ContentBlockUnion
448448 for _, block := range response.Content {
from line 454
454454 fmt.Printf("Tool: %s\n", toolUse.Name)
455455 fmt.Printf("Input: %s\n", string(toolUse.Input))
456456 
457 // Execute the tool. In a real system this would call your calendar API.
457 // Run the tool. In a real system this would call your calendar API.
458458 // Here the result is hardcoded to keep the example self-contained.
459459 result := `{"event_id": "evt_123", "status": "created"}`
460460 
461461 // Send the result back. The tool_result block goes in a user message and
462 // its tool_use_id must match the id from the tool_use block above. The
462 // its tool_use_id must match the ID from the earlier tool_use block. The
463463 // assistant's previous response is included so Claude has the full history.
464464 var assistantContent []anthropic.ContentBlockParamUnion
465465 for _, block := range response.Content {
from line 481
481481 log.Fatal(err)
482482 }
483483 
484 // With the tool result in hand, Claude produces a final natural-language
484 // With the tool result, Claude produces a final natural-language
485485 // answer and stop_reason becomes "end_turn".
486486 fmt.Printf("stop_reason: %s\n", followup.StopReason)
487487 for _, block := range followup.Content {
from line 542
542542 .build())
543543 .build();
544544 
545 // Ask for at most one tool call per turn so the single-turn flow below
546 // stays predictable.
545 // Ask for at most one tool call in each turn so the single-turn flow
546 // that follows stays predictable.
547547 ToolChoiceAuto toolChoice = ToolChoiceAuto.builder()
548548 .disableParallelToolUse(true)
549549 .build();
from line 551
551551 String userPrompt =
552552 "Schedule a 30-minute sync with [email protected] and [email protected] on Monday, March 30, 2026 at 10am.";
553553 
554 // Send the user's request along with the tool definition. Claude decides
554 // Send the user's request along with the tool definition. Claude determines
555555 // whether to call the tool based on the request and the tool description.
556556 Message response = client.messages().create(MessageCreateParams.builder()
557557 .model(Model.CLAUDE_OPUS_5_5)
from line 565
565565 // and the content array contains a tool_use block alongside any text.
566566 IO.println("stop_reason: " + response.stopReason().orElse(null));
567567 
568 // Find the tool_use block. A response may contain text blocks before the
568 // Find the tool_use block. A response might contain text blocks before the
569569 // tool_use block, so scan the content array rather than assuming position.
570570 ToolUseBlock toolUse = response.content().stream()
571571 .flatMap(block -> block.toolUse().stream())
from line 574
574574 IO.println("Tool: " + toolUse.name());
575575 IO.println("Input: " + toolUse._input());
576576 
577 // Execute the tool. In a real system this would call your calendar API.
577 // Run the tool. In a real system this would call your calendar API.
578578 // Here the result is hardcoded to keep the example self-contained.
579579 String result = "{\"event_id\": \"evt_123\", \"status\": \"created\"}";
580580 
581581 // Send the result back. The tool_result block goes in a user message and
582 // its tool_use_id must match the id from the tool_use block above. The
582 // its tool_use_id must match the ID from the earlier tool_use block. The
583583 // assistant's previous response is included so Claude has the full history.
584584 Message followup = client.messages().create(MessageCreateParams.builder()
585585 .model(Model.CLAUDE_OPUS_5_5)
from line 595
595595 .build())))
596596 .build());
597597 
598 // With the tool result in hand, Claude produces a final natural-language
598 // With the tool result, Claude produces a final natural-language
599599 // answer and stop_reason becomes "end_turn".
600600 IO.println("stop_reason: " + followup.stopReason().orElse(null));
601601 followup.content().stream()
from line 651
651651 'content' => 'Schedule a 30-minute sync with [email protected] and [email protected] on Monday, March 30, 2026 at 10am.',
652652 ];
653653 
654 // Ask for at most one tool call per turn so the single-turn flow below
655 // stays predictable.
654 // Ask for at most one tool call in each turn so the single-turn flow
655 // that follows stays predictable.
656656 $toolChoice = ToolChoiceAuto::with(disableParallelToolUse: true);
657657 
658 // Send the user's request along with the tool definition. Claude decides
658 // Send the user's request along with the tool definition. Claude determines
659659 // whether to call the tool based on the request and the tool description.
660660 $response = $client->messages->create(
661661 model: 'claude-opus-5-5',
from line 669
669669 // and the content array contains a tool_use block alongside any text.
670670 printf("stop_reason: %s\n", $response->stopReason);
671671 
672 // Find the tool_use block. A response may contain text blocks before the
672 // Find the tool_use block. A response might contain text blocks before the
673673 // tool_use block, so scan the content array rather than assuming position.
674674 $toolUse = null;
675675 foreach ($response->content as $block) {
from line 681
681681 printf("Tool: %s\n", $toolUse->name);
682682 printf("Input: %s\n", json_encode($toolUse->input));
683683 
684 // Execute the tool. In a real system this would call your calendar API.
684 // Run the tool. In a real system this would call your calendar API.
685685 // Here the result is hardcoded to keep the example self-contained.
686686 $result = ['event_id' => 'evt_123', 'status' => 'created'];
687687 
688688 // Send the result back. The tool_result block goes in a user message and
689 // its tool_use_id must match the id from the tool_use block above. The
689 // its tool_use_id must match the ID from the earlier tool_use block. The
690690 // assistant's previous response is included so Claude has the full history.
691691 $followup = $client->messages->create(
692692 model: 'claude-opus-5-5',
from line 709
709709 ],
710710 );
711711 
712 // With the tool result in hand, Claude produces a final natural-language
712 // With the tool result, Claude produces a final natural-language
713713 // answer and stop_reason becomes "end_turn".
714714 printf("stop_reason: %s\n", $followup->stopReason);
715715 foreach ($followup->content as $block) {
from line 763
763763 content: "Schedule a 30-minute sync with [email protected] and [email protected] on Monday, March 30, 2026 at 10am."
764764 }
765765 
766 # Ask for at most one tool call per turn so the single-turn flow below
767 # stays predictable.
766 # Ask for at most one tool call in each turn so the single-turn flow
767 # that follows stays predictable.
768768 tool_choice = {type: "auto", disable_parallel_tool_use: true}
769769 
770 # Send the user's request along with the tool definition. Claude decides
770 # Send the user's request along with the tool definition. Claude determines
771771 # whether to call the tool based on the request and the tool description.
772772 response = client.messages.create(
773773 model: Anthropic::Model::CLAUDE_OPUS_5_5,
from line 781
781781 # and the content array contains a tool_use block alongside any text.
782782 puts "stop_reason: #{response.stop_reason}"
783783 
784 # Find the tool_use block. A response may contain text blocks before the
784 # Find the tool_use block. A response might contain text blocks before the
785785 # tool_use block, so scan the content array rather than assuming position.
786786 tool_use = response.content.find { |block| block.type == :tool_use }
787787 puts "Tool: #{tool_use.name}"
788788 puts "Input: #{tool_use.input}"
789789 
790 # Execute the tool. In a real system this would call your calendar API.
790 # Run the tool. In a real system this would call your calendar API.
791791 # Here the result is hardcoded to keep the example self-contained.
792792 result = {event_id: "evt_123", status: "created"}
793793 
794794 # Send the result back. The tool_result block goes in a user message and
795 # its tool_use_id must match the id from the tool_use block above. The
795 # its tool_use_id must match the ID from the earlier tool_use block. The
796796 # assistant's previous response is included so Claude has the full history.
797797 followup = client.messages.create(
798798 model: Anthropic::Model::CLAUDE_OPUS_5_5,
from line 815
815815 ]
816816 )
817817 
818 # With the tool result in hand, Claude produces a final natural-language
818 # With the tool result, Claude produces a final natural-language
819819 # answer and stop_reason becomes "end_turn".
820820 puts "stop_reason: #{followup.stop_reason}"
821821 followup.content.each do |block|
from line 3327
33273327 </CodeGroupItem>
33283328 
33293329 ```python Python
3330 # Ring 5: The Tool Runner SDK abstraction.
3330 # Ring 5: The tool runner SDK abstraction.
33313331 
33323332 import json
33333333 
from line 3389
33893389 ```
33903390 
33913391 ```typescript TypeScript
3392 // Ring 5: The Tool Runner SDK abstraction.
3392 // Ring 5: The tool runner SDK abstraction.
33933393 
33943394 import Anthropic from "@anthropic-ai/sdk";
33953395 import { betaZodTool } from "@anthropic-ai/sdk/helpers/beta/zod";
from line 3458
34583458 ```
34593459 
34603460 ```csharp C#
3461 // Ring 5: The Tool Runner SDK abstraction.
3461 // Ring 5: The tool runner SDK abstraction.
34623462 
34633463 using System;
34643464 using System.Collections.Generic;
from line 3577
35773577 ```
35783578 
35793579 ```go Go
3580 // Ring 5: The Tool Runner SDK abstraction.
3580 // Ring 5: The tool runner SDK abstraction.
35813581 
35823582 package main
35833583 
from line 3682
36823682 ```
36833683 
36843684 ```java Java
3685 // Ring 5: The Tool Runner SDK abstraction.
3685 // Ring 5: The tool runner SDK abstraction.
36863686 
36873687 import com.anthropic.client.AnthropicClient;
36883688 import com.anthropic.client.okhttp.AnthropicOkHttpClient;
from line 3738
37383738 // The runner calls the API, runs requested tools, and feeds results back
37393739 // until Claude produces a final answer.
37403740 BetaToolRunner runner = client.beta()
3741 .messages()
3742 .toolRunner(MessageCreateParams.builder()
3743 .model(Model.CLAUDE_OPUS_5_5)
3744 .maxTokens(1024)
3745 .addBeta("structured-outputs-2025-11-13")
3746 .addUserMessage("Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts.")
3747 .addTool(CreateCalendarEvent.class)
3748 .addTool(ListCalendarEvents.class)
3749 .build());
3741 .messages()
3742 .toolRunner(MessageCreateParams.builder()
3743 .model(Model.CLAUDE_OPUS_5_5)
3744 .maxTokens(1024)
3745 .addBeta("structured-outputs-2025-11-13")
3746 .addUserMessage("Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts.")
3747 .addTool(CreateCalendarEvent.class)
3748 .addTool(ListCalendarEvents.class)
3749 .build());
37503750 
37513751 BetaMessage finalMessage = null;
37523752 for (BetaMessage message : runner) {
from line 3762
37623762 ```php PHP
37633763 <?php
37643764 
3765 // Ring 5: The Tool Runner SDK abstraction.
3765 // Ring 5: The tool runner SDK abstraction.
37663766 
37673767 use Anthropic\Client;
37683768 use Anthropic\Lib\Tools\BetaRunnableTool;
from line 3856
38563856 ```
38573857 
38583858 ```ruby Ruby
3859 # Ring 5: The Tool Runner SDK abstraction.
3859 # Ring 5: The tool runner SDK abstraction.
38603860 
38613861 require "anthropic"
38623862 

agents-and-tools/tool-use/computer-use-tool Changed · +32 / -24 lines

from line 22
2222 Claude API: ga
2323 Claude Platform on AWS: beta
2424 Amazon Bedrock: beta
25 Google Cloud: ga
25 Claude Models on Google Cloud (Google managed): ga
2626 Microsoft Foundry: beta
2727 details:
2828 - On the Claude API and Claude Models on Google Cloud (Google managed), Claude 5.5 and later models support computer use only through the `computer_toolset_20260801` toolset and return an error for the earlier `computer_20251124` and `computer_20250124` tool versions. To move an existing integration, see [Migrate from `computer_20251124`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool#migrate-from-computer-20251124). To move from `computer_20250124`, see [Move computer use to the toolset](https://platform.claude.com/docs/en/models/haiku-5-5/migration-guide#computer-use-toolset) in the Claude Haiku 5.5 migration guide.
from line 232
232232 messages: [
233233 ['role' => 'user', 'content' => 'Save a picture of a cat to my desktop.'],
234234 ],
235 model: 'claude-opus-5-5',
235 model: Model::CLAUDE_OPUS_5_5,
236236 tools: [
237237 ['type' => 'computer_toolset_20260801'],
238238 [
from line 617
617617 List<MessageParam> samplingLoop(Model model, List<MessageParam> messages, int maxIterations) {
618618 for (int i = 0; i < maxIterations; i++) {
619619 Message response = client.messages().create(MessageCreateParams.builder()
620 .model(model)
621 .maxTokens(4096)
622 .messages(messages)
623 .addTool(COMPUTER_TOOLSET)
624 .build());
620 .model(model)
621 .maxTokens(4096)
622 .messages(messages)
623 .addTool(COMPUTER_TOOLSET)
624 .build());
625625 
626626 // Add Claude's response to the conversation history
627627 messages.add(response.toParam());
from line 891
891891 
892892### Build a custom computer use environment
893893 
894The [reference implementation](https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo) is meant to help you get started with computer use. It includes all of the components needed to have Claude use a computer. However, you can build your own environment for computer use to suit your needs. You'll need:
894The [reference implementation](https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo) is meant to help you get started with computer use. It includes all of the components needed to have Claude use a computer. However, you can build your own environment for computer use to suit your needs. You need:
895895 
896896* A virtualized or containerized environment suitable for computer use with Claude
897897* An implementation of the computer use tool's actions
from line 1122
11221122 
11231123 ToolResultBlockParam.Content captureScreenshot() {
11241124 ImageBlockParam image = ImageBlockParam.builder()
1125 .source(Base64ImageSource.builder()
1126 .mediaType(Base64ImageSource.MediaType.IMAGE_PNG)
1127 .data(PLACEHOLDER_PNG)
1128 .build())
1129 .build();
1125 .source(Base64ImageSource.builder()
1126 .mediaType(Base64ImageSource.MediaType.IMAGE_PNG)
1127 .data(PLACEHOLDER_PNG)
1128 .build())
1129 .build();
11301130 return ToolResultBlockParam.Content.ofBlocks(
1131 List.of(ToolResultBlockParam.Content.Block.ofImage(image)));
1131 List.of(ToolResultBlockParam.Content.Block.ofImage(image)));
11321132 }
11331133 
11341134 String clickAt(long x, long y) {
from line 1419
14191419 }
14201420 
14211421 // processToolCalls runs the computer actions in Claude's response in order and
1422 // builds one tool_result per tool_use block. After the first failure it skips
1422 // builds one tool_result for each tool_use block. After the first failure it skips
14231423 // the rest: Claude planned them assuming the earlier actions succeeded.
14241424 func processToolCalls(response *anthropic.Message) []anthropic.ContentBlockParamUnion {
14251425 var toolResults []anthropic.ContentBlockParamUnion
from line 1460
14601460 /** Every result answering a computer toolset member echoes toolset_name. */
14611461 ToolResultBlockParam.Builder computerResult(ToolUseBlock toolUse) {
14621462 return ToolResultBlockParam.builder()
1463 .toolUseId(toolUse.id())
1464 .toolsetName("computer");
1463 .toolUseId(toolUse.id())
1464 .toolsetName("computer");
14651465 }
14661466 
14671467 /**
14681468 * Run the computer actions in Claude's response in order and build one
1469 * tool_result per tool_use block. After the first failure, skip the rest:
1469 * tool_result for each tool_use block. After the first failure, skip the rest:
14701470 * Claude planned them assuming the earlier actions succeeded.
14711471 */
14721472 List<ContentBlockParam> processToolCalls(Message response) {
from line 1480
14801480 ToolUseBlock toolUse = block.asToolUse();
14811481 ToolResultBlockParam result;
14821482 if (failed) {
1483 result = computerResult(toolUse).content(HALT_TEXT).isError(true).build();
1483 result = computerResult(toolUse)
1484 .content(HALT_TEXT)
1485 .isError(true)
1486 .build();
14841487 } else {
14851488 try {
14861489 Map<String, JsonValue> input =
1487 (Map<String, JsonValue>) toolUse._input().asObject().get();
1490 (Map<String, JsonValue>) toolUse._input().asObject().get();
14881491 // A string, or the image block that screenshot returns
14891492 ToolResultBlockParam.Content output = handleComputerAction(toolUse.name(), input);
1490 result = computerResult(toolUse).content(output).build();
1493 result = computerResult(toolUse)
1494 .content(output)
1495 .build();
14911496 } catch (RuntimeException e) {
14921497 failed = true;
1493 result = computerResult(toolUse).content("Error: " + e.getMessage()).isError(true).build();
1498 result = computerResult(toolUse)
1499 .content("Error: " + e.getMessage())
1500 .isError(true)
1501 .build();
14941502 }
14951503 }
14961504 toolResults.add(ContentBlockParam.ofToolResult(result));
from line 1542
15341542 NOT_EXECUTED = "Not executed: an earlier computer action in this turn failed."
15351543 
15361544 # Run the computer actions in Claude's response in order and build one
1537 # tool_result per tool_use block. After the first failure, skip the rest:
1545 # tool_result for each tool_use block. After the first failure, skip the rest:
15381546 # Claude planned them assuming the earlier actions succeeded.
15391547 def process_tool_calls(response)
15401548 tool_results = []
from line 1824
18161824 
18171825To keep [Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) effective while bounding context:
18181826 
1819* Place one `cache_control` breakpoint after the system prompt and tool definitions, and up to three more on the last `tool_result` block of each of the most recent turns, advancing them each turn. Within a [batch action](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool#batch-actions), markers on several blocks act as a single breakpoint but each still counts toward the limit of four, so use one for each turn.
1827* Place one `cache_control` breakpoint after the system prompt and tool definitions, and up to three more on the last `tool_result` block of each of the most recent turns, advancing them each turn. Within a [batch action](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool#batch-actions), markers on several blocks act as a single breakpoint, but each still counts toward the limit of four, so use at most one in any turn.
18201828* Prune old screenshots in *batches*, not one each turn. Dropping a screenshot every turn changes the prefix every turn and invalidates the cache. A reasonable default is to keep the last three screenshots and prune every 25 turns, so the prefix stays byte-identical between prune events; if your screenshots exceed 2000 px on either side, choose an interval that keeps each request at 20 or fewer images.
18211829* On Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5, and Claude Haiku 5.5, avoid pruning on the client: removing an earlier screenshot [invalidates every later thinking block](https://platform.claude.com/docs/en/build-with-claude/thinking#preserved-in-conversation) in every request that still carries those turns. Resize screenshots to 2000 px or less per side instead, and use server-side [tool result clearing](https://platform.claude.com/docs/en/build-with-claude/context-editing#tool-result-clearing) to drop old ones from the context. If you must prune, keep [`prefix_mismatch_behavior: "drop_block"`](https://platform.claude.com/docs/en/build-with-claude/thinking#preserved-thinking-controls) set from then on; after each prune, Claude continues without the thinking produced since the pruned screenshot, on that request and every later one. On Claude Sonnet 5.5, `block_binding` works only with `thinking: {"type": "adaptive"}`. With `between_tools`, keep the history append-only, or strip the thinking blocks from the edited turn on. On Claude Haiku 5.5, `block_binding` works only with `thinking: {"type": "adaptive"}`; with `thinking: {"type": "disabled"}`, keep the history append-only, or strip the thinking blocks from the edited turn on.
18221830 

agents-and-tools/tool-use/fine-grained-tool-streaming Changed · +18 / -18 lines

from line 474
474474Complete tool input: {"filename": "poem.txt", "lines_of_text": ["The Wanderer's Journey", ...]}
475475```
476476 
477Without `eager_input_streaming`, the API buffers and validates each parameter value before streaming it back, so nothing prints for a large parameter until Claude has finished generating it. With it, fragments start arriving as soon as Claude begins the parameter, and they are typically longer, with fewer mid-word breaks.
477Without `eager_input_streaming`, the API buffers and validates each parameter value before streaming it back, so nothing prints for a large parameter until Claude has finished generating it. With it, fragments arrive from the moment Claude starts the parameter, and they are typically longer, with fewer mid-word breaks.
478478 
479479## Accumulating tool input deltas
480480 
from line 490
4904902. For each `content_block_delta` with `type: "input_json_delta"`, append: `input_json += event.delta.partial_json`.
4914913. On `content_block_stop`, parse the accumulated string.
492492 
493Guard the parse, as the following example does. A response can also stop at `max_tokens` midway through a parameter. Check the [stop reason](https://platform.claude.com/docs/en/build-with-claude/handling-stop-reasons) and decide whether to retry the request with a higher `max_tokens` or repair the partial input.
493Guard the parse, as the following SDK example does. A response can also stop at `max_tokens` midway through a parameter. Check the [stop reason](https://platform.claude.com/docs/en/build-with-claude/handling-stop-reasons) and decide whether to retry the request with a higher `max_tokens` or repair the partial input.
494494 
495495The type mismatch between the initial `input: {}` (object) and `partial_json` (string) is by design. The empty object marks the slot in the content array. The delta strings build the real value.
496496 
from line 724
724724 ObjectMapper objectMapper = new ObjectMapper();
725725 
726726 Tool weatherTool = Tool.builder()
727 .name("get_weather")
728 .description("Get current weather for a city")
729 .eagerInputStreaming(true)
730 .inputSchema(Tool.InputSchema.builder()
731 .properties(Tool.InputSchema.Properties.builder()
732 .putAdditionalProperty("city", JsonValue.from(Map.of("type", "string")))
733 .build())
734 .addRequired("city")
735 .build())
736 .build();
727 .name("get_weather")
728 .description("Get current weather for a city")
729 .eagerInputStreaming(true)
730 .inputSchema(Tool.InputSchema.builder()
731 .properties(Tool.InputSchema.Properties.builder()
732 .putAdditionalProperty("city", JsonValue.from(Map.of("type", "string")))
733 .build())
734 .addRequired("city")
735 .build())
736 .build();
737737 
738738 MessageCreateParams createParams = MessageCreateParams.builder()
739 .model(Model.CLAUDE_OPUS_5_5)
740 .maxTokens(1024)
741 .addTool(weatherTool)
742 .addUserMessage("Weather in Paris?")
743 .build();
739 .model(Model.CLAUDE_OPUS_5_5)
740 .maxTokens(1024)
741 .addTool(weatherTool)
742 .addUserMessage("Weather in Paris?")
743 .build();
744744 
745745 // Content block index -> accumulated tool input JSON
746746 Map<Long, StringBuilder> toolInputs = new HashMap<>();
from line 830
830830 $accumulated = $toolInputs[$event->index];
831831 try {
832832 $parsed = json_decode($accumulated, associative: true, flags: JSON_THROW_ON_ERROR);
833 echo "Tool input: " . json_encode($parsed) . "\n";
833 echo 'Tool input: ' . json_encode($parsed) . "\n";
834834 } catch (\JsonException $e) {
835835 // The accumulated string is not guaranteed to be valid JSON.
836836 // See "Handling invalid JSON in tool responses" on this page.

agents-and-tools/tool-use/memory-tool Changed · +36 / -36 lines

from line 223
223223 ```java Java
224224 import com.anthropic.models.messages.MemoryTool20250818;
225225 // ...
226 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
226 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
227227 
228 MessageCreateParams params = MessageCreateParams.builder()
229 .model(Model.CLAUDE_OPUS_5_5)
230 .maxTokens(2048L)
231 .addTool(MemoryTool20250818.builder().build())
232 .addUserMessage("Help me respond to this customer service ticket.")
233 .build();
228 MessageCreateParams params = MessageCreateParams.builder()
229 .model(Model.CLAUDE_OPUS_5_5)
230 .maxTokens(2048L)
231 .addTool(MemoryTool20250818.builder().build())
232 .addUserMessage("Help me respond to this customer service ticket.")
233 .build();
234234 
235 Message message = client.messages().create(params);
236 IO.println(message);
235 Message message = client.messages().create(params);
236 IO.println(message);
237237 ```
238238 
239239 ```php PHP
from line 248
248248 'content' => 'Help me respond to this customer service ticket.',
249249 ],
250250 ],
251 tools: [new MemoryTool20250818],
251 tools: [new MemoryTool20250818()],
252252 );
253253 
254254 echo $message;
from line 512
512512 import java.nio.file.Path;
513513 
514514 void main() {
515 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
515 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
516516 
517 // Your BetaMemoryToolHandler implementation of the six memory commands
518 BetaMemoryToolHandler handler = new FileSystemMemoryToolHandler(Path.of("memories"));
517 // Your BetaMemoryToolHandler implementation of the six memory commands
518 BetaMemoryToolHandler handler = new FileSystemMemoryToolHandler(Path.of("memories"));
519519 
520 MessageCreateParams createParams = MessageCreateParams.builder()
521 .model(Model.CLAUDE_OPUS_5_5)
522 .maxTokens(1024L)
523 .addTool(BetaMemoryTool20250818.builder().build())
524 .addUserMessage("Remember that customer Acme Corp prefers email follow-ups.")
525 .build();
520 MessageCreateParams createParams = MessageCreateParams.builder()
521 .model(Model.CLAUDE_OPUS_5_5)
522 .maxTokens(1024L)
523 .addTool(BetaMemoryTool20250818.builder().build())
524 .addUserMessage("Remember that customer Acme Corp prefers email follow-ups.")
525 .build();
526526 
527 ToolRunnerCreateParams runnerParams = ToolRunnerCreateParams.builder()
528 .betaMemoryToolHandler(handler)
529 .initialMessageParams(createParams)
530 .maxIterations(10)
531 .build();
527 ToolRunnerCreateParams runnerParams = ToolRunnerCreateParams.builder()
528 .betaMemoryToolHandler(handler)
529 .initialMessageParams(createParams)
530 .maxIterations(10)
531 .build();
532532 
533 BetaToolRunner runner = client.beta().messages().toolRunner(runnerParams);
534 for (BetaMessage message : runner) {
535 IO.println(message);
536 }
533 BetaToolRunner runner = client.beta().messages().toolRunner(runnerParams);
534 for (BetaMessage message : runner) {
535 IO.println(message);
536 }
537537 }
538538 ```
539539 
from line 552
552552 $store = [];
553553 
554554 $memory = new BetaRunnableTool(
555 definition: new BetaMemoryTool20250818,
555 definition: new BetaMemoryTool20250818(),
556556 run: function (array $input) use (&$store): string {
557557 $path = $input['path'] ?? '';
558558 switch ($input['command']) {
from line 588
588588 }
589589 $lines = explode("\n", $store[$path]);
590590 if ($input['insert_line'] < 0 || $input['insert_line'] > count($lines)) {
591 return "Error: Invalid `insert_line` parameter: {$input['insert_line']}. It should be within the range of lines of the file: [0, " . count($lines) . "]";
591 return "Error: Invalid `insert_line` parameter: {$input['insert_line']}. It should be within the range of lines of the file: [0, " . count($lines) . ']';
592592 }
593593 array_splice($lines, $input['insert_line'], 0, [preg_replace('/\n\z/', '', $input['insert_text'])]);
594594 $store[$path] = implode("\n", $lines);

agents-and-tools/tool-use/overview Changed · +7 / -7 lines

from line 177
177177 model="claude-opus-5-5",
178178 max_tokens=1024,
179179 tools=tools,
180 # Ask for at most one tool call per turn.
180 # Ask for at most one tool call in each turn.
181181 tool_choice={"type": "auto", "disable_parallel_tool_use": True},
182182 messages=messages,
183183 )
from line 233
233233 model: "claude-opus-5-5",
234234 max_tokens: 1024,
235235 tools,
236 // Ask for at most one tool call per turn.
236 // Ask for at most one tool call in each turn.
237237 tool_choice: { type: "auto", disable_parallel_tool_use: true },
238238 messages
239239 });
from line 286
286286 }),
287287 ];
288288 
289 // Ask for at most one tool call per turn.
289 // Ask for at most one tool call in each turn.
290290 var toolChoice = new ToolChoice(new ToolChoiceAuto { DisableParallelToolUse = true });
291291 
292292 const string userPrompt = "What's the weather in San Francisco?";
from line 364
364364 },
365365 }},
366366 }
367 // Ask for at most one tool call per turn.
367 // Ask for at most one tool call in each turn.
368368 toolChoice := anthropic.ToolChoiceUnionParam{
369369 OfAuto: &anthropic.ToolChoiceAutoParam{DisableParallelToolUse: anthropic.Bool(true)},
370370 }
from line 449
449449 .build())
450450 .build();
451451 
452 // Ask for at most one tool call per turn.
452 // Ask for at most one tool call in each turn.
453453 ToolChoiceAuto toolChoice = ToolChoiceAuto.builder()
454454 .disableParallelToolUse(true)
455455 .build();
from line 516
516516 ];
517517 $userMessage = ['role' => 'user', 'content' => "What's the weather in San Francisco?"];
518518 
519 // Ask for at most one tool call per turn.
519 // Ask for at most one tool call in each turn.
520520 $toolChoice = ToolChoiceAuto::with(disableParallelToolUse: true);
521521 
522522 // Claude replies with a tool_use block naming the tool and its arguments.
from line 590
590590 model: Anthropic::Model::CLAUDE_OPUS_5_5,
591591 max_tokens: 1024,
592592 tools: tools,
593 # Ask for at most one tool call per turn.
593 # Ask for at most one tool call in each turn.
594594 tool_choice: {type: "auto", disable_parallel_tool_use: true},
595595 messages: messages
596596 )

agents-and-tools/tool-use/parallel-tool-use Changed · +10 / -6 lines

from line 365
365365 };
366366 
367367 var finalResponse = await client.Messages.Create(finalParameters);
368 var text = finalResponse.Content.Select(b => b.Value).OfType<TextBlock>().FirstOrDefault();
368 var text = finalResponse.Content.Select(block => block.Value).OfType<TextBlock>().FirstOrDefault();
369369 Console.WriteLine($"\nClaude's response:\n{text?.Text}");
370370 
371371 Console.WriteLine("\n--- Verification ---");
from line 697
697697 echo "\nClaude's response:\n{$textBlock->text}\n";
698698 
699699 echo "\n--- Verification ---\n";
700 echo "✓ Tool results sent in single user message: " . count($toolResults) . " results\n";
700 echo '✓ Tool results sent in single user message: ' . count($toolResults) . " results\n";
701701 echo "✓ No text before tool results in content array\n";
702702 echo "✓ Conversation formatted correctly for future parallel tool use\n";
703703 ```
from line 1056
10561056 .inputSchema(schema)
10571057 .build()
10581058 )
1059 .toolChoice(ToolChoiceAuto.builder().disableParallelToolUse(true).build())
1059 .toolChoice(ToolChoiceAuto.builder()
1060 .disableParallelToolUse(true)
1061 .build())
10601062 .addUserMessage("What is the weather in San Francisco and New York?")
10611063 .build();
10621064 
from line 1074
10721074 messages: [
10731075 ['role' => 'user', 'content' => 'What is the weather in San Francisco and New York?']
10741076 ],
1075 model: 'claude-opus-5-5',
1077 model: Model::CLAUDE_OPUS_5_5,
10761078 toolChoice: ['type' => 'auto', 'disableParallelToolUse' => true],
10771079 tools: [
10781080 [
from line 1339
13371339 .inputSchema(schema)
13381340 .build()
13391341 )
1340 .toolChoice(ToolChoiceAny.builder().disableParallelToolUse(true).build())
1342 .toolChoice(ToolChoiceAny.builder()
1343 .disableParallelToolUse(true)
1344 .build())
13411345 .addUserMessage("What is the weather in San Francisco and New York?")
13421346 .build();
13431347 
from line 1357
13531357 messages: [
13541358 ['role' => 'user', 'content' => 'What is the weather in San Francisco and New York?']
13551359 ],
1356 model: 'claude-opus-5',
1360 model: Model::CLAUDE_OPUS_5,
13571361 toolChoice: ['type' => 'any', 'disableParallelToolUse' => true],
13581362 tools: [
13591363 [

agents-and-tools/tool-use/programmatic-tool-calling Changed · +105 / -104 lines

from line 49
4949<CodeGroup>
5050 ```bash cURL
5151 curl https://api.anthropic.com/v1/messages \
52 -H "x-api-key: $ANTHROPIC_API_KEY" \
53 -H "anthropic-version: 2023-06-01" \
54 -H "content-type: application/json" \
55 -d '{
56 "model": "claude-opus-5-5",
57 "max_tokens": 4096,
58 "messages": [
59 {
60 "role": "user",
61 "content": "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue"
52 -H "x-api-key: $ANTHROPIC_API_KEY" \
53 -H "anthropic-version: 2023-06-01" \
54 -H "content-type: application/json" \
55 -d '{
56 "model": "claude-opus-5-5",
57 "max_tokens": 4096,
58 "messages": [
59 {
60 "role": "user",
61 "content": "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue"
62 }
63 ],
64 "tools": [
65 {
66 "type": "code_execution_20260120",
67 "name": "code_execution"
68 },
69 {
70 "name": "query_database",
71 "description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
72 "input_schema": {
73 "type": "object",
74 "properties": {
75 "sql": {
76 "type": "string",
77 "description": "SQL query to execute"
6278 }
63 ],
64 "tools": [
65 {
66 "type": "code_execution_20260120",
67 "name": "code_execution"
68 },
69 {
70 "name": "query_database",
71 "description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
72 "input_schema": {
73 "type": "object",
74 "properties": {
75 "sql": {
76 "type": "string",
77 "description": "SQL query to execute"
78 }
79 },
80 "required": ["sql"]
81 },
82 "allowed_callers": ["code_execution_20260120"]
83 }
84 ]
85 }'
79 },
80 "required": ["sql"]
81 },
82 "allowed_callers": ["code_execution_20260120"]
83 }
84 ]
85 }'
8686 ```
8787 
8888 ```bash CLI
from line 194
194194 Model = Model.ClaudeOpus5_5,
195195 MaxTokens = 4096,
196196 Messages = [
197 new UserMessageParam {
197 new UserMessageParam
198 {
198199 Content = "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue"
199200 }
200201 ],
from line 296
295296 messages: [
296297 ['role' => 'user', 'content' => 'Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue'],
297298 ],
298 model: 'claude-opus-5-5',
299 model: Model::CLAUDE_OPUS_5_5,
299300 tools: [
300301 [
301302 'type' => 'code_execution_20260120',
from line 368
367368 
368369When you configure a tool to be callable from code execution and Claude determines that tool is needed:
369370 
3701. Claude writes Python code that invokes the tool as a function, potentially including multiple tool calls and pre/post-processing logic.
3711. Claude writes Python code that calls the tool as a function. The code can include multiple tool calls and pre/post-processing logic.
3713722. Claude runs this code in a sandboxed container through code execution.
3723733. When a tool function is called, code execution pauses and the API returns a `tool_use` block.
3733744. You provide the tool result, and code execution continues (intermediate results are not loaded into Claude's context window).
from line 527
526527<CodeGroup>
527528 ```bash cURL
528529 curl https://api.anthropic.com/v1/messages \
529 -H "x-api-key: $ANTHROPIC_API_KEY" \
530 -H "anthropic-version: 2023-06-01" \
531 -H "content-type: application/json" \
532 -d '{
533 "model": "claude-opus-5-5",
534 "max_tokens": 4096,
535 "container": "container_xyz789",
536 "messages": [
537 {
538 "role": "user",
539 "content": "Query customer purchase history from the last quarter and identify our top 5 customers by revenue"
540 },
541 {
542 "role": "assistant",
543 "content": [
544 {
545 "type": "text",
546 "text": "I'\''ll query the purchase history and analyze the results."
547 },
548 {
549 "type": "server_tool_use",
550 "id": "srvtoolu_abc123",
551 "name": "code_execution",
552 "input": {"code": "..."}
553 },
554 {
555 "type": "tool_use",
556 "id": "toolu_def456",
557 "name": "query_database",
558 "input": {"sql": "<sql>"},
559 "caller": {
560 "type": "code_execution_20260120",
561 "tool_id": "srvtoolu_abc123"
562 }
563 }
564 ]
565 },
566 {
567 "role": "user",
568 "content": [
569 {
570 "type": "tool_result",
571 "tool_use_id": "toolu_def456",
572 "content": "[{\"customer_id\": \"C1\", \"revenue\": 45000}, {\"customer_id\": \"C2\", \"revenue\": 38000}]"
573 }
574 ]
530 -H "x-api-key: $ANTHROPIC_API_KEY" \
531 -H "anthropic-version: 2023-06-01" \
532 -H "content-type: application/json" \
533 -d '{
534 "model": "claude-opus-5-5",
535 "max_tokens": 4096,
536 "container": "container_xyz789",
537 "messages": [
538 {
539 "role": "user",
540 "content": "Query customer purchase history from the last quarter and identify our top 5 customers by revenue"
541 },
542 {
543 "role": "assistant",
544 "content": [
545 {
546 "type": "text",
547 "text": "I'\''ll query the purchase history and analyze the results."
548 },
549 {
550 "type": "server_tool_use",
551 "id": "srvtoolu_abc123",
552 "name": "code_execution",
553 "input": {"code": "..."}
554 },
555 {
556 "type": "tool_use",
557 "id": "toolu_def456",
558 "name": "query_database",
559 "input": {"sql": "<sql>"},
560 "caller": {
561 "type": "code_execution_20260120",
562 "tool_id": "srvtoolu_abc123"
575563 }
576 ],
577 "tools": [
578 {
579 "type": "code_execution_20260120",
580 "name": "code_execution"
581 },
582 {
583 "name": "query_database",
584 "description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
585 "input_schema": {
586 "type": "object",
587 "properties": {
588 "sql": {
589 "type": "string",
590 "description": "SQL query to execute"
591 }
592 },
593 "required": ["sql"]
594 },
595 "allowed_callers": ["code_execution_20260120"]
596 }
564 }
597565 ]
598 }'
566 },
567 {
568 "role": "user",
569 "content": [
570 {
571 "type": "tool_result",
572 "tool_use_id": "toolu_def456",
573 "content": "[{\"customer_id\": \"C1\", \"revenue\": 45000}, {\"customer_id\": \"C2\", \"revenue\": 38000}]"
574 }
575 ]
576 }
577 ],
578 "tools": [
579 {
580 "type": "code_execution_20260120",
581 "name": "code_execution"
582 },
583 {
584 "name": "query_database",
585 "description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
586 "input_schema": {
587 "type": "object",
588 "properties": {
589 "sql": {
590 "type": "string",
591 "description": "SQL query to execute"
592 }
593 },
594 "required": ["sql"]
595 },
596 "allowed_callers": ["code_execution_20260120"]
597 }
598 ]
599 }'
599600 ```
600601 
601602 ```bash CLI

agents-and-tools/tool-use/server-tools Changed · +14 / -9 lines

from line 164
164164 Model = Model.ClaudeOpus5_5,
165165 MaxTokens = 1024,
166166 Messages = [
167 new UserMessageParam {
167 new UserMessageParam
168 {
168169 Content = "Search for comprehensive information about quantum computing breakthroughs in 2025"
169170 }
170171 ],
from line 182
181182 Model = Model.ClaudeOpus5_5,
182183 MaxTokens = 1024,
183184 Messages = [
184 new UserMessageParam {
185 new UserMessageParam
186 {
185187 Content = "Search for comprehensive information about quantum computing breakthroughs in 2025"
186188 },
187 new AssistantMessageParam {
189 new AssistantMessageParam
190 {
188191 Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
189192 }
190193 ],
from line 844
841844 Message response = client.messages().create(MessageCreateParams.builder()
842845 .model(Model.CLAUDE_OPUS_5_5)
843846 .maxTokens(1024L)
844 .addTool(WebFetchTool20250910.builder().maxUses(5L).build())
847 .addTool(WebFetchTool20250910.builder()
848 .maxUses(5L)
849 .build())
845850 .addTool(runCommandTool)
846851 .addUserMessage(prompt)
847852 .build());
from line 871
866871 Message continuation = client.messages().create(MessageCreateParams.builder()
867872 .model(Model.CLAUDE_OPUS_5_5)
868873 .maxTokens(1024L)
869 .addTool(WebFetchTool20250910.builder().maxUses(5L).build())
874 .addTool(WebFetchTool20250910.builder()
875 .maxUses(5L)
876 .build())
870877 .addTool(runCommandTool)
871878 .addUserMessage(prompt)
872879 .addMessage(response)
from line 1066
10591066Invalid domain formats are rejected at request time with a 400 `invalid_request_error`.
10601067 
10611068<Note>
1062 In a Messages API request, request-level domain restrictions work together with any organization-level domain restrictions configured in Claude Console. Request-level `allowed_domains` must be a subset of the organization-level allowed list; entries outside it cause the API to return a validation error. A request-level allowed list that includes a domain your organization blocks is rejected with a `400` error that names the conflicting entries.
1069 Request-level domain restrictions work together with any organization-level domain restrictions configured in the Claude Console. Request-level `allowed_domains` must be a subset of the organization-level allowed list; entries outside it cause the API to return a validation error. A request-level allowed list that includes a domain your organization blocks is rejected with a `400` error that names the conflicting entries.
10631070</Note>
10641071 
10651072<Warning>
from line 1073
10661073 Unicode characters in domain names can bypass domain filters through homograph attacks: `аmazon.com` (with a Cyrillic `а`) looks identical to `amazon.com` but is a different domain. Use ASCII-only domain names in allow and block lists, and audit existing entries for non-ASCII characters.
10671074</Warning>
10681075 
1069[Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview) uses the same `allowed_domains` and `blocked_domains` fields on the `web_search` and `web_fetch` entries of the agent toolset. On Managed Agents, each list holds at most 64 entries, domains listed for `web_fetch` cannot include a path, and fields specific to the Messages API tools, such as `max_uses`, `citations`, and `cache_control`, are not available. See [Domain list rules](https://platform.claude.com/docs/en/managed-agents/tools-web-restrictions#domain-list-rules) for the full rules.
1070 
1071Your organization's web search settings on the [**Capabilities**](https://platform.claude.com/settings/capabilities) page of the Claude Console apply to Messages API requests and to Managed Agents sessions. In a session, the agent receives only the search results that both the organization's domain restrictions and the agent's per-tool lists allow. For a session in a cloud environment with `limited` networking, the environment's `allowed_hosts` also applies to `web_search` and `web_fetch`; see [Networking](https://platform.claude.com/docs/en/managed-agents/environments#networking). The per-tool lists restrict these tools further, within the hosts that `allowed_hosts` allows.
1076For domain lists and your organization's settings in Claude Managed Agents sessions, see [Restrict web search and web fetch domains](https://platform.claude.com/docs/en/managed-agents/tools-web-restrictions).
10721077 
10731078## Dynamic filtering with code execution
10741079 

agents-and-tools/tool-use/text-editor-tool Changed · +170 / -170 lines

from line 159
159159 import com.anthropic.models.messages.ToolTextEditor20250728;
160160 // ...
161161 void main() {
162 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
162 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
163163 
164 ToolTextEditor20250728 editorTool =
165 ToolTextEditor20250728.builder()
166 .maxCharacters(10000L)
167 .build();
164 ToolTextEditor20250728 editorTool =
165 ToolTextEditor20250728.builder()
166 .maxCharacters(10000L)
167 .build();
168168 
169 MessageCreateParams params = MessageCreateParams.builder()
170 .model(Model.CLAUDE_OPUS_5_5)
171 .maxTokens(1024)
172 .addTool(editorTool)
173 .addUserMessage("There's a syntax error in my primes.py file. Can you help me fix it?")
174 .build();
169 MessageCreateParams params = MessageCreateParams.builder()
170 .model(Model.CLAUDE_OPUS_5_5)
171 .maxTokens(1024)
172 .addTool(editorTool)
173 .addUserMessage("There's a syntax error in my primes.py file. Can you help me fix it?")
174 .build();
175175 
176 Message message = client.messages().create(params);
177 IO.println(message);
176 Message message = client.messages().create(params);
177 IO.println(message);
178178 }
179179 ```
180180 
from line 182
182182 $client = new Client();
183183 
184184 $response = $client->messages->create(
185 model: 'claude-opus-5-5',
185 model: Model::CLAUDE_OPUS_5_5,
186186 maxTokens: 1024,
187187 tools: [ToolTextEditor20250728::with(maxCharacters: 10000)],
188188 messages: [
from line 504
504504 import com.anthropic.models.messages.ToolTextEditor20250728;
505505 // ...
506506 void main() {
507 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
507 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
508508 
509 ToolTextEditor20250728 editorTool =
510 ToolTextEditor20250728.builder().build();
509 ToolTextEditor20250728 editorTool =
510 ToolTextEditor20250728.builder().build();
511511 
512 MessageCreateParams params = MessageCreateParams.builder()
513 .model(Model.CLAUDE_OPUS_5_5)
514 .maxTokens(1024)
515 .addTool(editorTool)
516 .addUserMessage("There's a syntax error in my primes.py file. Can you help me fix it?")
517 .build();
512 MessageCreateParams params = MessageCreateParams.builder()
513 .model(Model.CLAUDE_OPUS_5_5)
514 .maxTokens(1024)
515 .addTool(editorTool)
516 .addUserMessage("There's a syntax error in my primes.py file. Can you help me fix it?")
517 .build();
518518 
519 Message message = client.messages().create(params);
520 IO.println(message);
519 Message message = client.messages().create(params);
520 IO.println(message);
521521 }
522522 ```
523523 
from line 525
525525 $client = new Client();
526526 
527527 $response = $client->messages->create(
528 model: 'claude-opus-5-5',
528 model: Model::CLAUDE_OPUS_5_5,
529529 maxTokens: 1024,
530530 tools: [new ToolTextEditor20250728()],
531531 messages: [
from line 885
885885 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
886886 
887887 MessageCreateParams params = MessageCreateParams.builder()
888 .model(Model.CLAUDE_OPUS_5_5)
889 .maxTokens(1024)
890 .addTool(ToolTextEditor20250728.builder().build())
891 .addUserMessage("There's a syntax error in my primes.py file. Can you help me fix it?")
892 .addAssistantMessageOfContentBlockParams(
893 List.of(
894 ContentBlockParam.ofText(
895 TextBlockParam.builder()
896 .text("I'll help you fix the syntax error in your primes.py file. First, let me take a look at the file to identify the issue.")
897 .build()
898 ),
899 ContentBlockParam.ofToolUse(
900 ToolUseBlockParam.builder()
901 .id("toolu_01AbCdEfGhIjKlMnOpQrStU")
902 .name("str_replace_based_edit_tool")
903 .input(
904 ToolUseBlockParam.Input.builder()
905 .putAdditionalProperty("command", JsonValue.from("view"))
906 .putAdditionalProperty("path", JsonValue.from("primes.py"))
907 .build()
908 )
909 .build()
910 )
888 .model(Model.CLAUDE_OPUS_5_5)
889 .maxTokens(1024)
890 .addTool(ToolTextEditor20250728.builder().build())
891 .addUserMessage("There's a syntax error in my primes.py file. Can you help me fix it?")
892 .addAssistantMessageOfContentBlockParams(
893 List.of(
894 ContentBlockParam.ofText(
895 TextBlockParam.builder()
896 .text("I'll help you fix the syntax error in your primes.py file. First, let me take a look at the file to identify the issue.")
897 .build()
898 ),
899 ContentBlockParam.ofToolUse(
900 ToolUseBlockParam.builder()
901 .id("toolu_01AbCdEfGhIjKlMnOpQrStU")
902 .name("str_replace_based_edit_tool")
903 .input(
904 ToolUseBlockParam.Input.builder()
905 .putAdditionalProperty("command", JsonValue.from("view"))
906 .putAdditionalProperty("path", JsonValue.from("primes.py"))
907 .build()
908 )
909 .build()
910 )
911 )
911912 )
912 )
913 .addUserMessageOfContentBlockParams(
914 List.of(
915 ContentBlockParam.ofToolResult(
916 ToolResultBlockParam.builder()
917 .toolUseId("toolu_01AbCdEfGhIjKlMnOpQrStU")
918 .content("1: def is_prime(n):\n2: \"\"\"Check if a number is prime.\"\"\"\n3: if n <= 1:\n4: return False\n5: if n <= 3:\n6: return True\n7: if n % 2 == 0 or n % 3 == 0:\n8: return False\n9: i = 5\n10: while i * i <= n:\n11: if n % i == 0 or n % (i + 2) == 0:\n12: return False\n13: i += 6\n14: return True\n15: \n16: def get_primes(limit):\n17: \"\"\"Generate a list of prime numbers up to the given limit.\"\"\"\n18: primes = []\n19: for num in range(2, limit + 1)\n20: if is_prime(num):\n21: primes.append(num)\n22: return primes\n23: \n24: def main():\n25: \"\"\"Main function to demonstrate prime number generation.\"\"\"\n26: limit = 100\n27: prime_list = get_primes(limit)\n28: print(f\"Prime numbers up to {limit}:\")\n29: print(prime_list)\n30: print(f\"Found {len(prime_list)} prime numbers.\")\n31: \n32: if __name__ == \"__main__\":\n33: main()")
919 .build()
920 )
913 .addUserMessageOfContentBlockParams(
914 List.of(
915 ContentBlockParam.ofToolResult(
916 ToolResultBlockParam.builder()
917 .toolUseId("toolu_01AbCdEfGhIjKlMnOpQrStU")
918 .content("1: def is_prime(n):\n2: \"\"\"Check if a number is prime.\"\"\"\n3: if n <= 1:\n4: return False\n5: if n <= 3:\n6: return True\n7: if n % 2 == 0 or n % 3 == 0:\n8: return False\n9: i = 5\n10: while i * i <= n:\n11: if n % i == 0 or n % (i + 2) == 0:\n12: return False\n13: i += 6\n14: return True\n15: \n16: def get_primes(limit):\n17: \"\"\"Generate a list of prime numbers up to the given limit.\"\"\"\n18: primes = []\n19: for num in range(2, limit + 1)\n20: if is_prime(num):\n21: primes.append(num)\n22: return primes\n23: \n24: def main():\n25: \"\"\"Main function to demonstrate prime number generation.\"\"\"\n26: limit = 100\n27: prime_list = get_primes(limit)\n28: print(f\"Prime numbers up to {limit}:\")\n29: print(prime_list)\n30: print(f\"Found {len(prime_list)} prime numbers.\")\n31: \n32: if __name__ == \"__main__\":\n33: main()")
919 .build()
920 )
921 )
921922 )
922 )
923 .build();
923 .build();
924924 
925925 Message message = client.messages().create(params);
926926 System.out.println(message);
from line 930
930930 $client = new Client();
931931 
932932 $response = $client->messages->create(
933 model: 'claude-opus-5-5',
933 model: Model::CLAUDE_OPUS_5_5,
934934 maxTokens: 1024,
935935 tools: [new ToolTextEditor20250728()],
936936 messages: [
from line 1317
13171317 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
13181318 
13191319 MessageCreateParams params = MessageCreateParams.builder()
1320 .model(Model.CLAUDE_OPUS_5_5)
1321 .maxTokens(1024)
1322 .addTool(ToolTextEditor20250728.builder().build())
1323 // Previous messages would go here
1324 .addAssistantMessageOfContentBlockParams(
1325 List.of(
1326 ContentBlockParam.ofText(
1327 TextBlockParam.builder()
1328 .text(
1329 "I found the syntax error in your primes.py file. In the `get_primes` function, there is a missing colon (:) at the end of the for loop line. Let me fix that for you."
1330 )
1331 .build()
1332 ),
1333 ContentBlockParam.ofToolUse(
1334 ToolUseBlockParam.builder()
1335 .id("toolu_01PqRsTuVwXyZAbCdEfGh")
1336 .name("str_replace_based_edit_tool")
1337 .input(
1338 ToolUseBlockParam.Input.builder()
1339 .putAdditionalProperty("command", JsonValue.from("str_replace"))
1340 .putAdditionalProperty("path", JsonValue.from("primes.py"))
1341 .putAdditionalProperty(
1342 "old_str",
1343 JsonValue.from(" for num in range(2, limit + 1)")
1344 )
1345 .putAdditionalProperty(
1346 "new_str",
1347 JsonValue.from(" for num in range(2, limit + 1):")
1348 )
1349 .build()
1350 )
1351 .build()
1352 )
1320 .model(Model.CLAUDE_OPUS_5_5)
1321 .maxTokens(1024)
1322 .addTool(ToolTextEditor20250728.builder().build())
1323 // Previous messages would go here
1324 .addAssistantMessageOfContentBlockParams(
1325 List.of(
1326 ContentBlockParam.ofText(
1327 TextBlockParam.builder()
1328 .text(
1329 "I found the syntax error in your primes.py file. In the `get_primes` function, there is a missing colon (:) at the end of the for loop line. Let me fix that for you."
1330 )
1331 .build()
1332 ),
1333 ContentBlockParam.ofToolUse(
1334 ToolUseBlockParam.builder()
1335 .id("toolu_01PqRsTuVwXyZAbCdEfGh")
1336 .name("str_replace_based_edit_tool")
1337 .input(
1338 ToolUseBlockParam.Input.builder()
1339 .putAdditionalProperty("command", JsonValue.from("str_replace"))
1340 .putAdditionalProperty("path", JsonValue.from("primes.py"))
1341 .putAdditionalProperty(
1342 "old_str",
1343 JsonValue.from(" for num in range(2, limit + 1)")
1344 )
1345 .putAdditionalProperty(
1346 "new_str",
1347 JsonValue.from(" for num in range(2, limit + 1):")
1348 )
1349 .build()
1350 )
1351 .build()
1352 )
1353 )
13531354 )
1354 )
1355 .addUserMessageOfContentBlockParams(
1356 List.of(
1357 ContentBlockParam.ofToolResult(
1358 ToolResultBlockParam.builder()
1359 .toolUseId("toolu_01PqRsTuVwXyZAbCdEfGh")
1360 .content("Successfully replaced text at exactly one location.")
1361 .build()
1362 )
1355 .addUserMessageOfContentBlockParams(
1356 List.of(
1357 ContentBlockParam.ofToolResult(
1358 ToolResultBlockParam.builder()
1359 .toolUseId("toolu_01PqRsTuVwXyZAbCdEfGh")
1360 .content("Successfully replaced text at exactly one location.")
1361 .build()
1362 )
1363 )
13631364 )
1364 )
1365 .build();
1365 .build();
13661366 
13671367 Message message = client.messages().create(params);
13681368 System.out.println(message);
from line 1372
13721372 $client = new Client();
13731373 
13741374 $response = $client->messages->create(
1375 model: 'claude-opus-5-5',
1375 model: Model::CLAUDE_OPUS_5_5,
13761376 maxTokens: 1024,
13771377 tools: [new ToolTextEditor20250728()],
13781378 messages: [
from line 1585
15851585 
15861586 ```java Java
15871587 static void handleEditorTool(Map<String, Object> input) {
1588 var command = (String) input.getOrDefault("command", "");
1589 var filePath = (String) input.getOrDefault("path", "");
1588 var command = (String) input.getOrDefault("command", "");
1589 var filePath = (String) input.getOrDefault("path", "");
15901590 
1591 if (command.equals("view")) {
1592 // Read and return file contents
1593 } else if (command.equals("str_replace")) {
1594 // Replace text in file
1595 } else if (command.equals("create")) {
1596 // Create new file
1597 } else if (command.equals("insert")) {
1598 // Insert text at location
1599 }
1591 if (command.equals("view")) {
1592 // Read and return file contents
1593 } else if (command.equals("str_replace")) {
1594 // Replace text in file
1595 } else if (command.equals("create")) {
1596 // Create new file
1597 } else if (command.equals("insert")) {
1598 // Insert text at location
1599 }
16001600 }
16011601 ```
16021602 
from line 1656
16561656 # Process tool use in Claude's response
16571657 for content in response.content:
16581658 if content.type == "tool_use":
1659 # Execute the tool based on command
1659 # Run the tool based on command
16601660 result = handle_editor_tool(content)
16611661 
16621662 # Return result to Claude
from line 1671
16711671 // Process tool use in Claude's response
16721672 for (const block of response.content) {
16731673 if (block.type === "tool_use") {
1674 // Execute the tool based on command
1674 // Run the tool based on command
16751675 const result = handleEditorTool(block);
16761676 
16771677 // Return result to Claude
from line 1719
17191719 ```java Java
17201720 // Process tool use in Claude's response
17211721 for (var block : response.content()) {
1722 if (block.type().equals("tool_use")) {
1723 // Execute the tool based on command
1724 var result = handleEditorTool(block);
1722 if (block.type().equals("tool_use")) {
1723 // Run the tool based on command
1724 var result = handleEditorTool(block);
17251725 
1726 // Return result to Claude
1727 var toolResult = Map.of(
1728 "type", "tool_result",
1729 "tool_use_id", block.id(),
1730 "content", result
1731 );
1732 }
1726 // Return result to Claude
1727 var toolResult = Map.of(
1728 "type", "tool_result",
1729 "tool_use_id", block.id(),
1730 "content", result
1731 );
1732 }
17331733 }
17341734 ```
17351735 
from line 1737
17371737 // Process tool use in Claude's response
17381738 foreach ($response->content as $block) {
17391739 if ($block->type === 'tool_use') {
1740 // Execute the tool based on command
1740 // Run the tool based on command
17411741 $result = handle_editor_tool($block->input);
17421742 
17431743 // Return result to Claude
from line 1920
19201920 
19211921 ```java Java
19221922 static void backupFile(String filePath) throws IOException {
1923 Path source = Path.of(filePath);
1924 Path backupPath = Path.of(filePath + ".backup");
1925 if (Files.exists(source)) {
1926 Files.copy(source, backupPath, StandardCopyOption.REPLACE_EXISTING);
1927 }
1923 Path source = Path.of(filePath);
1924 Path backupPath = Path.of(filePath + ".backup");
1925 if (Files.exists(source)) {
1926 Files.copy(source, backupPath, StandardCopyOption.REPLACE_EXISTING);
1927 }
19281928 }
19291929 ```
19301930 
from line 2038
20382038 
20392039 ```java Java
20402040 static String safeReplace(String filePath, String oldText, String newText) throws IOException {
2041 String content = Files.readString(Path.of(filePath));
2041 String content = Files.readString(Path.of(filePath));
20422042 
2043 int count = content.split(Pattern.quote(oldText), -1).length - 1;
2044 if (count == 0) {
2045 return "Error: No match found";
2046 } else if (count > 1) {
2047 return "Error: Found " + count + " matches";
2048 } else {
2049 String newContent = content.replace(oldText, newText);
2050 Files.writeString(Path.of(filePath), newContent);
2051 return "Successfully replaced text";
2052 }
2043 int count = content.split(Pattern.quote(oldText), -1).length - 1;
2044 if (count == 0) {
2045 return "Error: No match found";
2046 } else if (count > 1) {
2047 return "Error: Found " + count + " matches";
2048 } else {
2049 String newContent = content.replace(oldText, newText);
2050 Files.writeString(Path.of(filePath), newContent);
2051 return "Successfully replaced text";
2052 }
20532053 }
20542054 ```
20552055 
from line 2172
21722172 
21732173 ```java Java
21742174 static String verifyChanges(String filePath) {
2175 try {
2176 // For Python files, check for syntax errors
2177 if (filePath.endsWith(".py")) {
2178 Process proc = new ProcessBuilder("python3", "-m", "py_compile", filePath)
2179 .redirectErrorStream(true)
2180 .start();
2181 if (proc.waitFor() != 0) {
2182 return "Verification failed: " + new String(proc.getInputStream().readAllBytes());
2183 }
2184 return "Syntax check passed";
2175 try {
2176 // For Python files, check for syntax errors
2177 if (filePath.endsWith(".py")) {
2178 Process proc = new ProcessBuilder("python3", "-m", "py_compile", filePath)
2179 .redirectErrorStream(true)
2180 .start();
2181 if (proc.waitFor() != 0) {
2182 return "Verification failed: " + new String(proc.getInputStream().readAllBytes());
2183 }
2184 return "Syntax check passed";
2185 }
2186 return "No checks defined for this file type";
2187 } catch (IOException | InterruptedException e) {
2188 return "Verification failed: " + e.getMessage();
21852189 }
2186 return "No checks defined for this file type";
2187 } catch (IOException | InterruptedException e) {
2188 return "Verification failed: " + e.getMessage();
2189 }
21902190 }
21912191 ```
21922192 

agents-and-tools/tool-use/tool-runner Changed · +52 / -50 lines

from line 396
396396 
397397 // The lambda can use weatherService.
398398 BetaRunnableTool getWeather = BetaRunnableTool.of(
399 GetWeather.class,
400 input -> BetaToolResultBlockParam.Content.ofString(
401 weatherService.currentWeather(input.location, input.unit)));
399 GetWeather.class,
400 input -> BetaToolResultBlockParam.Content.ofString(
401 weatherService.currentWeather(input.location, input.unit)));
402402 
403403 BetaRunnableTool calculateSum = BetaRunnableTool.of(
404 CalculateSum.class,
405 input -> BetaToolResultBlockParam.Content.ofString(
406 String.valueOf(input.a + input.b)));
404 CalculateSum.class,
405 input -> BetaToolResultBlockParam.Content.ofString(
406 String.valueOf(input.a + input.b)));
407407 
408408 BetaToolRunner runner = client.beta()
409 .messages()
410 .toolRunner(MessageCreateParams.builder()
411 .model(Model.CLAUDE_OPUS_5_5)
412 .maxTokens(1024)
413 .addBeta("structured-outputs-2025-11-13")
414 .addUserMessage("What's the weather like in Paris? Also, what's 15 + 27?")
415 .addTool(getWeather)
416 .addTool(calculateSum)
417 .build());
409 .messages()
410 .toolRunner(MessageCreateParams.builder()
411 .model(Model.CLAUDE_OPUS_5_5)
412 .maxTokens(1024)
413 .addBeta("structured-outputs-2025-11-13")
414 .addUserMessage("What's the weather like in Paris? Also, what's 15 + 27?")
415 .addTool(getWeather)
416 .addTool(calculateSum)
417 .build());
418418 
419419 for (BetaMessage message : runner) {
420420 IO.println(message);
from line 706
706706 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
707707 
708708 BetaToolRunner runner = client.beta()
709 .messages()
710 .toolRunner(MessageCreateParams.builder()
711 .model(Model.CLAUDE_OPUS_5_5)
712 .maxTokens(1024)
713 .addBeta("structured-outputs-2025-11-13")
714 .addUserMessage("What's the weather like in Paris? Also, what's 15 + 27?")
715 .addTool(getWeather)
716 .addTool(calculateSum)
717 .build());
709 .messages()
710 .toolRunner(MessageCreateParams.builder()
711 .model(Model.CLAUDE_OPUS_5_5)
712 .maxTokens(1024)
713 .addBeta("structured-outputs-2025-11-13")
714 .addUserMessage("What's the weather like in Paris? Also, what's 15 + 27?")
715 .addTool(getWeather)
716 .addTool(calculateSum)
717 .build());
718718 
719719 BetaMessage finalMessage = null;
720720 for (BetaMessage message : runner) {
from line 814
814814 
815815You take over by modifying the runner's messages from inside the loop body. The exact method depends on the SDK. See the per-language tabs that follow.
816816 
817When you take over for an iteration, the runner does not append the assistant message or tool results from that turn. You become responsible for keeping the conversation valid: append the assistant message and a tool result yourself (if you want the turn to count), modify state conditionally so the loop can still exit when there are no tool calls, and pass `max_iterations` (csharp, java, php: `maxIterations`; go: `MaxIterations`) to bound the loop. The SDK supports `max_iterations` (csharp, java, php: `maxIterations`; go: `MaxIterations`).
817When you take over for an iteration, the runner does not append the assistant message or tool results from that turn. You become responsible for keeping the conversation valid: append the assistant message and a tool result yourself (if you want the turn to count), modify state conditionally so the loop can still exit when there are no tool calls, and pass `max_iterations` (csharp, java, php: `maxIterations`; go: `MaxIterations`) to bound the loop. The SDK supports `max_iterations` (csharp, java, php: `maxIterations`; go: `MaxIterations`) in all seven languages.
818818 
819819<Tabs exclude="shell">
820820 <Tab title="Python">
from line 985
985985 
986986 ```java
987987 BetaToolRunner runner = client.beta()
988 .messages()
989 .toolRunner(ToolRunnerCreateParams.builder()
990 .initialMessageParams(MessageCreateParams.builder()
991 .model(Model.CLAUDE_OPUS_5_5)
992 .maxTokens(1024)
993 .addBeta("structured-outputs-2025-11-13")
994 .addUserMessage("Give me a detailed weather report for every major US city.")
995 .addTool(getWeather)
996 .build())
997 .maxIterations(10L)
998 .build());
988 .messages()
989 .toolRunner(ToolRunnerCreateParams.builder()
990 .initialMessageParams(MessageCreateParams.builder()
991 .model(Model.CLAUDE_OPUS_5_5)
992 .maxTokens(1024)
993 .addBeta("structured-outputs-2025-11-13")
994 .addUserMessage("Give me a detailed weather report for every major US city.")
995 .addTool(getWeather)
996 .build())
997 .maxIterations(10L)
998 .build());
999999 
10001000 long ceiling = 8192;
10011001 
from line 1012
10121012 // Calling setNextParams() flags this turn as user-managed: the runner
10131013 // does NOT auto-append the truncated message, so the next iteration
10141014 // re-sends the same conversation prefix with the larger budget.
1015 runner.setNextParams(runner.params().toBuilder().maxTokens(doubled).build());
1015 runner.setNextParams(runner.params().toBuilder()
1016 .maxTokens(doubled)
1017 .build());
10161018 }
10171019 // No mutation on a normal turn: the runner auto-appends and continues.
10181020 }
from line 1472
14701472 BetaRunnableTool searchDocuments = BetaRunnableTool.of(SearchDocuments.class, input -> {
14711473 String largeResult = "..."; // a long document worth caching
14721474 return BetaToolResultBlockParam.Content.ofBlocks(List.of(
1473 BetaToolResultBlockParam.Content.Block.ofText(
1474 BetaTextBlockParam.builder()
1475 .text(largeResult)
1476 .cacheControl(BetaCacheControlEphemeral.builder().build())
1477 .build())));
1475 BetaToolResultBlockParam.Content.Block.ofText(
1476 BetaTextBlockParam.builder()
1477 .text(largeResult)
1478 .cacheControl(BetaCacheControlEphemeral.builder().build())
1479 .build())));
14781480 });
14791481 ```
14801482 </Tab>
from line 1714
17121714 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
17131715 
17141716 BetaToolRunner runner = client.beta()
1715 .messages()
1716 .toolRunner(MessageCreateParams.builder()
1717 .model(Model.CLAUDE_OPUS_5_5)
1718 .maxTokens(1024)
1719 .addBeta("structured-outputs-2025-11-13")
1720 .addUserMessage("What is 15 + 27?")
1721 .addTool(calculateSum)
1722 .build());
1717 .messages()
1718 .toolRunner(MessageCreateParams.builder()
1719 .model(Model.CLAUDE_OPUS_5_5)
1720 .maxTokens(1024)
1721 .addBeta("structured-outputs-2025-11-13")
1722 .addUserMessage("What is 15 + 27?")
1723 .addTool(calculateSum)
1724 .build());
17231725 
17241726 for (StreamResponse<BetaRawMessageStreamEvent> stream : runner.streaming()) {
17251727 try (stream) {

agents-and-tools/tool-use/web-fetch-tool Changed · +8 / -4 lines

from line 245
245245## How to use web fetch
246246 
247247<Note>
248 An organization admin can turn off web fetch for your organization on the [**Capabilities**](https://platform.claude.com/settings/capabilities) page of the Claude Console. With web fetch off, a Messages API request that includes the tool fails with a 400 `invalid_request_error` that says web fetch is not enabled, and `web_fetch` calls in a [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview) session return an error result to the agent while the session continues. With web fetch on, the admin can also set allowed or blocked domains for web fetch on the same page. For how the organization's domain lists apply, see [Domain filtering](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools#domain-filtering) for Messages API requests and [Restrict web search and web fetch domains](https://platform.claude.com/docs/en/managed-agents/tools-web-restrictions) for Managed Agents sessions.
248 An organization admin can turn off web fetch for your organization on the [**Capabilities**](https://platform.claude.com/settings/capabilities) page of the Claude Console. With web fetch off, a Messages API request that includes the tool fails with a 400 `invalid_request_error` that says web fetch is not enabled. With web fetch on, the admin can also set allowed or blocked domains for web fetch on the same page. For how those lists apply to a request, see [Domain filtering](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools#domain-filtering).
249249</Note>
250250 
251For how your organization's web fetch settings apply in Claude Managed Agents sessions, see [Restrict web search and web fetch domains](https://platform.claude.com/docs/en/managed-agents/tools-web-restrictions).
252 
251253Provide the web fetch tool in your API request:
252254 
253255<CodeGroup>
from line 460
458460 
459461For domain filtering with `allowed_domains` and `blocked_domains`, see [Server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools#domain-filtering).
460462 
461On [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview), set these fields on the `web_fetch` entry of the agent toolset, where each listed domain must be a plain hostname with no path; see [Restrict web search and web fetch domains](https://platform.claude.com/docs/en/managed-agents/tools-web-restrictions).
463On [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview), set these fields on the `web_fetch` entry of the agent toolset; see [Restrict web search and web fetch domains](https://platform.claude.com/docs/en/managed-agents/tools-web-restrictions).
462464 
463465### Content limits
464466 
from line 883
881883 .build())
882884 .addTool(WebFetchTool20250910.builder()
883885 .maxUses(5L)
884 .citations(CitationsConfigParam.builder().enabled(true).build())
886 .citations(CitationsConfigParam.builder()
887 .enabled(true)
888 .build())
885889 .build())
886890 .build();
887891 

agents-and-tools/tool-use/web-search-tool Changed · +3 / -3 lines

from line 236
236236## How to use web search
237237 
238238<Note>
239 Web search is enabled for your organization unless an administrator has disabled it in the [Claude Console](https://platform.claude.com/settings/capabilities), where they can also restrict which domains it searches. If it's disabled, a Messages API request that includes the tool fails with a 400 `invalid_request_error` that says web search is not enabled, rather than an [error code](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool#errors) inside a search result. With web search off, `web_search` calls in a [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview) session return an error result to the agent, and the session continues.
239 Web search is enabled for your organization unless an administrator has disabled it in the [Claude Console](https://platform.claude.com/settings/capabilities), where they can also restrict which domains it searches. If it's disabled, a Messages API request that includes the tool fails with a 400 `invalid_request_error` that says web search is not enabled, rather than an [error code](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool#errors) inside a search result.
240240</Note>
241241 
242Your organization's web search settings in the Claude Console apply to Messages API requests and to Managed Agents sessions. In a session, the agent receives only the search results that both the organization's domain restrictions and the agent's per-tool lists allow; see [Restrict web search and web fetch domains](https://platform.claude.com/docs/en/managed-agents/tools-web-restrictions). For a session in a cloud environment with `limited` networking, the environment's `allowed_hosts` also applies to `web_search` and `web_fetch`; see [Networking](https://platform.claude.com/docs/en/managed-agents/environments#networking). The per-tool lists restrict these tools further, within the hosts that `allowed_hosts` allows.
242For how your organization's web search settings apply in Claude Managed Agents sessions, see [Restrict web search and web fetch domains](https://platform.claude.com/docs/en/managed-agents/tools-web-restrictions).
243243 
244244Provide the web search tool in your API request:
245245 
from line 462
462462* `country`: The two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code (the API rejects an unsupported code with a 400 error)
463463* `timezone`: The [IANA time zone ID](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
464464 
465On Claude Managed Agents, the `web_search` entry of the agent toolset accepts a `user_location` object with the same fields. The API rejects an unsupported `country` code with a 400 error when you create or update the agent, or when you create or update a session that supplies the setting. See the [web tool settings](https://platform.claude.com/docs/en/managed-agents/tools-web-restrictions#settings).
465On Claude Managed Agents, the `web_search` entry of the agent toolset also accepts `user_location`; see the [web tool settings](https://platform.claude.com/docs/en/managed-agents/tools-web-restrictions#settings).
466466 
467467### Response inclusion
468468 

api/beta-headers Changed · +6 / -6 lines

from line 114
114114 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
115115 
116116 MessageCreateParams params = MessageCreateParams.builder()
117 .model(Model.CLAUDE_OPUS_5_5)
118 .maxTokens(1024)
119 .addUserMessage("Hello, Claude")
120 .addBeta(AnthropicBeta.CONTEXT_MANAGEMENT_2025_06_27)
121 .build();
117 .model(Model.CLAUDE_OPUS_5_5)
118 .maxTokens(1024)
119 .addUserMessage("Hello, Claude")
120 .addBeta(AnthropicBeta.CONTEXT_MANAGEMENT_2025_06_27)
121 .build();
122122 
123123 BetaMessage message = client.beta().messages().create(params);
124124 System.out.println(message.content());
from line 130
130130 $message = $client->beta->messages->create(
131131 maxTokens: 1024,
132132 messages: [['role' => 'user', 'content' => 'Hello, Claude']],
133 model: 'claude-opus-5-5',
133 model: Model::CLAUDE_OPUS_5_5,
134134 betas: ['context-management-2025-06-27'],
135135 );
136136 

api/claude-models-on-google-cloud-anthropic-managed-iam-permissions New page · 244 lines, new page

## Service details ## Predefined roles ## Permissions ### Messages ### Message batches ### Models ### Files ### Skills ### Agents ### Sessions ### Environments ### Vaults ### Memory stores ### Deployments ### Dreams ### Encryption keys ### Usage reports ## See also

A whole new page. There's nothing to diff it against, so here is what it says.

---
title: IAM permissions for Claude Models on Google Cloud (Anthropic managed)
url: https://platform.claude.com/docs/en/api/claude-models-on-google-cloud-anthropic-managed-iam-permissions
description: Look up Google Cloud IAM permissions, predefined roles, and audit log types for Claude Models on Google Cloud (Anthropic managed).
---

Claude Models on Google Cloud (Anthropic managed) uses Google Cloud IAM for access control. Every API route needs one permission from the `claude.googleapis.com` service. If the caller doesn't hold that permission, Google Cloud returns an HTTP 403 error. Three predefined roles bundle the permissions. For setup and authentication, see [Claude Models on Google Cloud (Anthropic managed)](https://platform.claude.com/docs/en/build-with-claude/claude-models-on-google-cloud/anthropic-managed).

## Service details

The following table describes the Google Cloud service for Claude Models on Google Cloud (Anthropic managed).

| Attribute                         | Value                                                                                                                                                                                                                     |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Service**                       | `claude.googleapis.com`                                                                                                                                                                                                   |
| **Where permissions are checked** | A workspace or the Anthropic organization                                                                                                                                                                                 |
| **Audit log types**               | Data Access, Admin Activity<br />To turn on Data Access logs, see [Monitoring and logging](https://platform.claude.com/docs/en/build-with-claude/claude-models-on-google-cloud/anthropic-managed#monitoring-and-logging). |

Grant roles on the Google Cloud project. For how projects, organizations, and workspaces relate, see [Workspaces](https://platform.claude.com/docs/en/build-with-claude/claude-models-on-google-cloud/anthropic-managed#workspaces).

Permissions on this page are in the short form `resource.verb`, such as `messages.create`.

## Predefined roles

The following table lists the predefined roles for Claude Models on Google Cloud (Anthropic managed).

| Role                  | Display name  | Permissions                                                                                                                                                                           |
| --------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `roles/claude.viewer` | Claude Viewer | Every `get` and `list` permission on workspace resources.                                                                                                                             |
| `roles/claude.user`   | Claude User   | Everything in `roles/claude.viewer`, plus every other permission on workspace resources.<br />For example, `messages.create`, `messages.countTokens`, and `environments.processWork`. |
| `roles/claude.admin`  | Claude Admin  | Everything in `roles/claude.user`, plus the permissions checked on your Anthropic organization.<br />For example, `externalKeys.*` and `usageAnalytics.list`.                         |

In the following tables, **Admin**, **User**, and **Viewer** stand for these three roles.

## Permissions

The following tables list 68 permissions across 111 routes.

The `get` and `list` permissions are the read-only ones.

Unless a section says otherwise, each route's audit log type is Data Access.

`roles/claude.viewer` can't send messages or count tokens. Sending a message needs `messages.create`. Counting tokens needs `messages.countTokens`.

Some routes in the following tables might not be available to your organization. To check which features are available, see [Feature support](https://platform.claude.com/docs/en/build-with-claude/claude-models-on-google-cloud/anthropic-managed#feature-support).

### Messages

| Permission             | Roles       | Routes authorized                |
| ---------------------- | ----------- | -------------------------------- |
| `messages.create`      | Admin, User | `POST /v1/messages`              |
| `messages.countTokens` | Admin, User | `POST /v1/messages/count_tokens` |

### Message batches

| Permission              | Roles               | Routes authorized                                                                                        |
| ----------------------- | ------------------- | -------------------------------------------------------------------------------------------------------- |
| `messageBatches.create` | Admin, User         | `POST /v1/messages/batches`                                                                              |
| `messageBatches.get`    | Admin, User, Viewer | `GET /v1/messages/batches/{message_batch_id}`<br />`GET /v1/messages/batches/{message_batch_id}/results` |
| `messageBatches.list`   | Admin, User, Viewer | `GET /v1/messages/batches`                                                                               |
| `messageBatches.delete` | Admin, User         | `DELETE /v1/messages/batches/{message_batch_id}`                                                         |
| `messageBatches.cancel` | Admin, User         | `POST /v1/messages/batches/{message_batch_id}/cancel`                                                    |

`roles/claude.viewer` can read batch results. `messageBatches.get` authorizes both reading a batch's metadata and downloading its results.

### Models

| Permission    | Roles               | Routes authorized           |
| ------------- | ------------------- | --------------------------- |
| `models.get`  | Admin, User, Viewer | `GET /v1/models/{model_id}` |
| `models.list` | Admin, User, Viewer | `GET /v1/models`            |

### Files

| Permission     | Roles               | Routes authorized                                                |
| -------------- | ------------------- | ---------------------------------------------------------------- |
| `files.create` | Admin, User         | `POST /v1/files`                                                 |
| `files.get`    | Admin, User, Viewer | `GET /v1/files/{file_id}`<br />`GET /v1/files/{file_id}/content` |
| `files.list`   | Admin, User, Viewer | `GET /v1/files`                                                  |
| `files.delete` | Admin, User         | `DELETE /v1/files/{file_id}`                                     |

`roles/claude.viewer` can download files. `files.get` authorizes both reading a file's metadata and downloading its content.

### Skills

| Permission      | Roles               | Routes authorized                                                                                                                                                                     |
| --------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `skills.create` | Admin, User         | `POST /v1/skills`                                                                                                                                                                     |
| `skills.get`    | Admin, User, Viewer | `GET /v1/skills/{skill_id}`<br />`GET /v1/skills/{skill_id}/versions`<br />`GET /v1/skills/{skill_id}/versions/{version}`<br />`GET /v1/skills/{skill_id}/versions/{version}/content` |
| `skills.list`   | Admin, User, Viewer | `GET /v1/skills`                                                                                                                                                                      |
| `skills.update` | Admin, User         | `POST /v1/skills/{skill_id}/versions`<br />`DELETE /v1/skills/{skill_id}/versions/{version}`                                                                                          |
| `skills.delete` | Admin, User         | `DELETE /v1/skills/{skill_id}`                                                                                                                                                        |

Creating or deleting a skill version needs `skills.update`, not `skills.create` or `skills.delete`.

### Agents

| Permission       | Roles               | Routes authorized                                                     |
| ---------------- | ------------------- | --------------------------------------------------------------------- |
| `agents.create`  | Admin, User         | `POST /v1/agents`                                                     |
| `agents.get`     | Admin, User, Viewer | `GET /v1/agents/{agent_id}`<br />`GET /v1/agents/{agent_id}/versions` |
| `agents.list`    | Admin, User, Viewer | `GET /v1/agents`                                                      |
| `agents.update`  | Admin, User         | `POST /v1/agents/{agent_id}`                                          |
| `agents.archive` | Admin, User         | `POST /v1/agents/{agent_id}/archive`                                  |

You can archive an agent but not delete it.

### Sessions

| Permission         | Roles               | Routes authorized                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| ------------------ | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sessions.create`  | Admin, User         | `POST /v1/sessions`                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `sessions.get`     | Admin, User, Viewer | `GET /v1/sessions/{session_id}`<br />`GET /v1/sessions/{session_id}/events`<br />`GET /v1/sessions/{session_id}/events/stream`<br />`GET /v1/sessions/{session_id}/resources`<br />`GET /v1/sessions/{session_id}/resources/{resource_id}`<br />`GET /v1/sessions/{session_id}/threads`<br />`GET /v1/sessions/{session_id}/threads/{thread_id}`<br />`GET /v1/sessions/{session_id}/threads/{thread_id}/events`<br />`GET /v1/sessions/{session_id}/threads/{thread_id}/stream` |
| `sessions.list`    | Admin, User, Viewer | `GET /v1/sessions`                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `sessions.update`  | Admin, User         | `POST /v1/sessions/{session_id}`<br />`POST /v1/sessions/{session_id}/events`<br />`POST /v1/sessions/{session_id}/resources`<br />`POST /v1/sessions/{session_id}/resources/{resource_id}`<br />`DELETE /v1/sessions/{session_id}/resources/{resource_id}`                                                                                                                                                                                                                      |
| `sessions.archive` | Admin, User         | `POST /v1/sessions/{session_id}/archive`<br />`POST /v1/sessions/{session_id}/threads/{thread_id}/archive`                                                                                                                                                                                                                                                                                                                                                                       |
| `sessions.delete`  | Admin, User         | `DELETE /v1/sessions/{session_id}`                                                                                                                                                                                                                                                                                                                                                                                                                                               |

`sessions.get` authorizes reading the session, its event history and event stream, its resources, and its threads. As a result, `roles/claude.viewer` can read every session's full event history.

Other session operations need the following permissions:

* Sending events: `sessions.update`
* Adding, changing, or removing session resources: `sessions.update`
* Archiving a thread: `sessions.archive`

### Environments

| Permission                 | Roles               | Routes authorized                                                                                                                                                                                                                                                                                                        |
| -------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `environments.create`      | Admin, User         | `POST /v1/environments`                                                                                                                                                                                                                                                                                                  |
| `environments.get`         | Admin, User, Viewer | `GET /v1/environments/{environment_id}`<br />`GET /v1/environments/{environment_id}/work`<br />`GET /v1/environments/{environment_id}/work/stats`<br />`GET /v1/environments/{environment_id}/work/{work_id}`                                                                                                            |
| `environments.list`        | Admin, User, Viewer | `GET /v1/environments`                                                                                                                                                                                                                                                                                                   |
| `environments.update`      | Admin, User         | `POST /v1/environments/{environment_id}`                                                                                                                                                                                                                                                                                 |
| `environments.archive`     | Admin, User         | `POST /v1/environments/{environment_id}/archive`                                                                                                                                                                                                                                                                         |
| `environments.delete`      | Admin, User         | `DELETE /v1/environments/{environment_id}`                                                                                                                                                                                                                                                                               |
| `environments.processWork` | Admin, User         | `GET /v1/environments/{environment_id}/work/poll`<br />`POST /v1/environments/{environment_id}/work/{work_id}`<br />`POST /v1/environments/{environment_id}/work/{work_id}/ack`<br />`POST /v1/environments/{environment_id}/work/{work_id}/heartbeat`<br />`POST /v1/environments/{environment_id}/work/{work_id}/stop` |

`roles/claude.user` and `roles/claude.admin` include every permission that a [self-hosted sandbox](https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes) worker needs. `environments.processWork` authorizes these worker actions:

* Polling for work
* Acknowledging work items
* Sending heartbeats for work items
* Stopping work items
* Updating work items

This permission is separate from `environments.update`. Reading the work queue and its statistics needs only `environments.get`.

Besides `environments.processWork`, a worker needs other permissions to run a session, such as `sessions.get` to read the session and its events, `sessions.update` to send tool results, and `skills.get` to download the agent's skills.

### Vaults

Google Cloud records these routes in Admin Activity audit logs, except the route for `vaults.verify`, whose audit log type is Data Access.

| Permission       | Roles               | Routes authorized                                                                                                                                                                                        |
| ---------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vaults.create`  | Admin, User         | `POST /v1/vaults`                                                                                                                                                                                        |
| `vaults.get`     | Admin, User, Viewer | `GET /v1/vaults/{vault_id}`<br />`GET /v1/vaults/{vault_id}/credentials`<br />`GET /v1/vaults/{vault_id}/credentials/{credential_id}`                                                                    |
| `vaults.list`    | Admin, User, Viewer | `GET /v1/vaults`                                                                                                                                                                                         |
| `vaults.update`  | Admin, User         | `POST /v1/vaults/{vault_id}`<br />`POST /v1/vaults/{vault_id}/credentials`<br />`POST /v1/vaults/{vault_id}/credentials/{credential_id}`<br />`DELETE /v1/vaults/{vault_id}/credentials/{credential_id}` |
| `vaults.archive` | Admin, User         | `POST /v1/vaults/{vault_id}/archive`<br />`POST /v1/vaults/{vault_id}/credentials/{credential_id}/archive`                                                                                               |
| `vaults.delete`  | Admin, User         | `DELETE /v1/vaults/{vault_id}`                                                                                                                                                                           |
| `vaults.verify`  | Admin, User         | `POST /v1/vaults/{vault_id}/credentials/{credential_id}/mcp_oauth_validate`                                                                                                                              |

Credential operations need the following permissions:

* Creating, changing, or deleting a credential: `vaults.update`
* Archiving a credential: `vaults.archive`
* Reading a credential: `vaults.get`
* Checking a stored MCP OAuth credential: `vaults.verify`

With `vaults.get`, `roles/claude.viewer` can list a vault's credentials. Claude Models on Google Cloud (Anthropic managed) never returns a credential's secret fields. To learn more, see [Authenticate with vaults](https://platform.claude.com/docs/en/managed-agents/vaults).

### Memory stores

| Permission             | Roles               | Routes authorized                                                                                                                                                                                                                                                                                                                          |
| ---------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `memoryStores.create`  | Admin, User         | `POST /v1/memory_stores`                                                                                                                                                                                                                                                                                                                   |
| `memoryStores.get`     | Admin, User, Viewer | `GET /v1/memory_stores/{memory_store_id}`<br />`GET /v1/memory_stores/{memory_store_id}/memories`<br />`GET /v1/memory_stores/{memory_store_id}/memories/{memory_id}`<br />`GET /v1/memory_stores/{memory_store_id}/memory_versions`<br />`GET /v1/memory_stores/{memory_store_id}/memory_versions/{memory_version_id}`                    |
| `memoryStores.list`    | Admin, User, Viewer | `GET /v1/memory_stores`                                                                                                                                                                                                                                                                                                                    |
| `memoryStores.update`  | Admin, User         | `POST /v1/memory_stores/{memory_store_id}`<br />`POST /v1/memory_stores/{memory_store_id}/memories`<br />`POST /v1/memory_stores/{memory_store_id}/memories/{memory_id}`<br />`DELETE /v1/memory_stores/{memory_store_id}/memories/{memory_id}`<br />`POST /v1/memory_stores/{memory_store_id}/memory_versions/{memory_version_id}/redact` |
| `memoryStores.archive` | Admin, User         | `POST /v1/memory_stores/{memory_store_id}/archive`                                                                                                                                                                                                                                                                                         |
| `memoryStores.delete`  | Admin, User         | `DELETE /v1/memory_stores/{memory_store_id}`                                                                                                                                                                                                                                                                                               |

Memory store operations need the following permissions:

* Reading the store, its memories, or its version history: `memoryStores.get`
* Creating, changing, or deleting a memory: `memoryStores.update`
* Redacting a memory version: `memoryStores.update`

### Deployments

| Permission            | Roles               | Routes authorized                                                                                                       |
| --------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `deployments.create`  | Admin, User         | `POST /v1/deployments`                                                                                                  |
| `deployments.get`     | Admin, User, Viewer | `GET /v1/deployment_runs`<br />`GET /v1/deployment_runs/{deployment_run_id}`<br />`GET /v1/deployments/{deployment_id}` |
| `deployments.list`    | Admin, User, Viewer | `GET /v1/deployments`                                                                                                   |
| `deployments.update`  | Admin, User         | `POST /v1/deployments/{deployment_id}`                                                                                  |
| `deployments.archive` | Admin, User         | `POST /v1/deployments/{deployment_id}/archive`                                                                          |
| `deployments.run`     | Admin, User         | `POST /v1/deployments/{deployment_id}/run`                                                                              |
| `deployments.pause`   | Admin, User         | `POST /v1/deployments/{deployment_id}/pause`                                                                            |
| `deployments.unpause` | Admin, User         | `POST /v1/deployments/{deployment_id}/unpause`                                                                          |

Reading deployment runs needs `deployments.get`.

### Dreams

| Permission       | Roles               | Routes authorized                    |
| ---------------- | ------------------- | ------------------------------------ |
| `dreams.create`  | Admin, User         | `POST /v1/dreams`                    |
| `dreams.get`     | Admin, User, Viewer | `GET /v1/dreams/{dream_id}`          |
| `dreams.list`    | Admin, User, Viewer | `GET /v1/dreams`                     |
| `dreams.archive` | Admin, User         | `POST /v1/dreams/{dream_id}/archive` |
| `dreams.cancel`  | Admin, User         | `POST /v1/dreams/{dream_id}/cancel`  |

Dreams is a research preview feature. To request access, see [Dreams](https://platform.claude.com/docs/en/managed-agents/dreams).

### Encryption keys

Google Cloud records these routes in Admin Activity audit logs.

| Permission            | Roles | Routes authorized                                          |
| --------------------- | ----- | ---------------------------------------------------------- |
| `externalKeys.create` | Admin | `POST /v1/organizations/external_keys`                     |
| `externalKeys.get`    | Admin | `GET /v1/organizations/external_keys/{external_key_id}`    |
| `externalKeys.list`   | Admin | `GET /v1/organizations/external_keys`                      |
| `externalKeys.update` | Admin | `POST /v1/organizations/external_keys/{external_key_id}`   |
| `externalKeys.delete` | Admin | `DELETE /v1/organizations/external_keys/{external_key_id}` |

These permissions manage your organization's encryption key registrations. Of the three predefined roles, only `roles/claude.admin` includes them.

### Usage reports

| Permission            | Roles | Routes authorized                             |
| --------------------- | ----- | --------------------------------------------- |
| `usageAnalytics.list` | Admin | `GET /v1/organizations/usage_report/messages` |

Of the three predefined roles, only `roles/claude.admin` includes `usageAnalytics.list`.

## See also

* [Claude Models on Google Cloud (Anthropic managed)](https://platform.claude.com/docs/en/build-with-claude/claude-models-on-google-cloud/anthropic-managed)
* [IAM roles](https://platform.claude.com/docs/en/build-with-claude/claude-models-on-google-cloud/anthropic-managed#iam-roles)
* [Monitoring and logging](https://platform.claude.com/docs/en/build-with-claude/claude-models-on-google-cloud/anthropic-managed#monitoring-and-logging)

api/errors Changed · +4 / -4 lines

from line 383
383383 
384384 Message message = accumulator.message();
385385 message.content().stream()
386 .filter(ContentBlock::isText)
387 .findFirst()
388 .flatMap(ContentBlock::text)
389 .ifPresent(textBlock -> IO.println(textBlock.text()));
386 .filter(ContentBlock::isText)
387 .findFirst()
388 .flatMap(ContentBlock::text)
389 .ifPresent(textBlock -> IO.println(textBlock.text()));
390390 }
391391 ```
392392 

api/migration-2026-10-21 New page · 1339 lines, new page

## Upgrade steps ## Breaking changes ### Messages API #### Document blocks in assistant messages return an error #### Citations in user messages return an error #### Sampling parameters are removed ### Admin API and Claude Enterprise Analytics API #### `workspace_id` on API keys ## SDK and CLI changes ### `MessageParam` is a union with one type for each role ### One-value enums are constants ### Client-side compaction is removed from the tool runner ### Text Completions is removed ### `betas` is removed from the non-beta Models methods ### API keys are sent as `Authorization: Bearer` #### If a proxy or gateway reads `X-Api-Key` #### If you set or removed `X-Api-Key` on a request ### A client holds one credential ### Changes in one SDK or the CLI ### The license is Apache 2.0

A whole new page. There's nothing to diff it against, so here is what it says.

---
title: Migrating to API version 2026-10-21
url: https://platform.claude.com/docs/en/api/migration-2026-10-21
description: "Upgrade a Claude API integration from API version 2023-06-01 to 2026-10-21: the SDK version to install and the code changes to make."
---

This guide lists the code changes for moving from API version `2023-06-01` to `2026-10-21`. The [upgrade steps](https://platform.claude.com/docs/en/api/migration-2026-10-21#migration-checklist) say what to do, in order.

## Upgrade steps

<Steps>
  <Step title="Switch to 2026-10-21">
    If you use an SDK, your requests use the API version that was current when your SDK's major version was released. The versions shown for your language are the first to use `2026-10-21`, so upgrade to them. If you call the API without an SDK, set the `anthropic-version` header to `2026-10-21`.

    <Tabs>
      <Tab title="cURL">
        Set the `anthropic-version` header to `2026-10-21`:

        ```bash
        curl https://api.anthropic.com/v1/messages \
          -H "x-api-key: $ANTHROPIC_API_KEY" \
          -H "anthropic-version: 2026-10-21" \
          -H "content-type: application/json" \
          -d '{
            "model": "claude-opus-5-5",
            "max_tokens": 1024,
            "messages": [{"role": "user", "content": "Hello, Claude"}]
          }'
        ```
      </Tab>

      <Tab title="CLI">
        Upgrade the `ant` CLI to version 2.0.0. The Homebrew command and the release downloads stay the same.

        Go requires the path of a module at major version 2 or later to end in that version, so the CLI's module path is now `github.com/anthropics/anthropic-cli/v2`. If you install with `go install`, add `/v2` to the path:

        ```bash
        # Before
        go install 'github.com/anthropics/anthropic-cli/cmd/ant@latest'

        # After
        go install 'github.com/anthropics/anthropic-cli/v2/cmd/ant@latest'
        ```

        The old path keeps installing the latest v1 release, with no error or warning.
      </Tab>

      <Tab title="Python">
        Upgrade the `anthropic` package to version 2.0.0.
      </Tab>

      <Tab title="TypeScript">
        Upgrade the `@anthropic-ai/sdk` package to version 1.0.0.

        v1 of `@anthropic-ai/bedrock-sdk`, `@anthropic-ai/vertex-sdk`, `@anthropic-ai/foundry-sdk`, `@anthropic-ai/aws-sdk`, and `@anthropic-ai/google-cloud-sdk` needs v1 of `@anthropic-ai/sdk`, and their earlier versions need a version below 1. If you use one of them, move it to v1 in the same upgrade. Otherwise your package manager installs a second, older copy of `@anthropic-ai/sdk` for it.
      </Tab>

      <Tab title="C#">
        Upgrade the `Anthropic` package to version 13.0.0.
      </Tab>

      <Tab title="Go">
        Upgrade to version 2.0.0 of the Go SDK, whose module path is `github.com/anthropics/anthropic-sdk-go/v2`. A new major version of a Go module has a new import path, so change each import of `github.com/anthropics/anthropic-sdk-go`, and of its packages such as `github.com/anthropics/anthropic-sdk-go/option`, to start with `github.com/anthropics/anthropic-sdk-go/v2`. Then run `go get github.com/anthropics/anthropic-sdk-go/v2`.
      </Tab>

      <Tab title="Java">
        Upgrade the `com.anthropic:anthropic-java` artifact to version 3.0.0.
      </Tab>

      <Tab title="PHP">
        Run `composer require "anthropic-ai/sdk"` to upgrade the package to version 1.0.0. `composer update` alone stays within your current version constraint.
      </Tab>

      <Tab title="Ruby">
        Upgrade the `anthropic` gem to version 2.0.0.
      </Tab>
    </Tabs>

    <Warning>
      On Amazon Bedrock, `2026-10-21` is available only for Claude 4.7 and later models. If you call an older model on Bedrock, listed in [Claude on Amazon Bedrock (Opus 4.6 and earlier)](https://platform.claude.com/docs/en/build-with-claude/claude-on-amazon-bedrock-legacy), stay on the SDK version you use today, or, if you set the API version yourself, keep sending the version you send today. To move those calls to `2026-10-21`, first switch them to a Claude 4.7 or later model.
    </Warning>

    Release the upgrade together with your fixes from the next step.
  </Step>

  <Step title="Update your code for the breaking changes">
    Go through the [breaking changes](https://platform.claude.com/docs/en/api/migration-2026-10-21#breaking-changes), and update your code for each one that affects you. Also make the [code changes that the new SDK or CLI version needs](https://platform.claude.com/docs/en/api/migration-2026-10-21#sdk-changes).
  </Step>

  <Step title="Test against 2026-10-21">
    Test your integration against `2026-10-21` before you release.
  </Step>
</Steps>

## Breaking changes

### Messages API

These changes apply to requests to the Messages API (`POST /v1/messages`), to [token counting](https://platform.claude.com/docs/en/build-with-claude/token-counting), and to each request in a [Message Batch](https://platform.claude.com/docs/en/build-with-claude/batch-processing) created with `2026-10-21`.

#### Document blocks in assistant messages return an error

Affects you if you send `document` blocks directly in `assistant` messages.

In `2026-10-21`, a request that places a `document` block directly in an `assistant` message returns a 400 error. Send documents in `user` messages instead. A document nested inside another block, such as a web fetch tool result that Claude returned, is still accepted.

#### Citations in user messages return an error

Affects you if you send `citations` on `text` blocks in `user` messages, including inside `search_result` blocks.

In `2026-10-21`, a request that sends a `text` block with a non-empty `citations` array in a `user` message returns a 400 error. Remove `citations` from those blocks. Citations on `text` blocks in `assistant` messages, such as Claude's earlier replies that you send back, are still accepted, and so is the `"citations": {"enabled": true}` setting on documents and search results.

#### Sampling parameters are removed

Affects you if you send `temperature`, `top_p`, or `top_k` in Messages requests or Message Batch requests.

In `2026-10-21`, a request that sends `temperature`, `top_p`, or `top_k` returns a 400 error, even at the parameter's default value.

Remove the three parameters from your requests.

As of the latest major versions, the SDK and the CLI no longer have the three parameters.

Without these parameters, a request samples with their defaults, including a `temperature` of 1.0, the same as a `2023-06-01` request that leaves them out. Models that already accept only the defaults, such as Claude 4.7 and later models, sample as before. On models that accept other values, such as Claude Sonnet 4.6, a request that lowered `temperature` for more consistent output loses that effect. No other setting replaces it. If a request relies on a non-default value, either stop sending the parameter, or keep that request on `2023-06-01`.

### Admin API and Claude Enterprise Analytics API

Affects you if your code reads the Admin API or Claude Enterprise Analytics API response fields in the following tables.

In `2026-10-21`, responses from the Admin API and the [Claude Enterprise Analytics API](https://platform.claude.com/docs/en/manage-claude/analytics-api) no longer include the old names of renamed fields. Use the new names instead.

Admin API:

| Endpoints                                                                                                                                                                                                                                                        | Old name       | New name                                                                                                                                        |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| [RBAC groups](https://platform.claude.com/docs/en/api/beta/organization/rbac_groups/list): create, list, retrieve, and update                                                                                                                                    | `roles`        | `role_ids`                                                                                                                                      |
| [RBAC group members](https://platform.claude.com/docs/en/api/beta/organization/rbac_groups/members/list): list and add, and the response to [removing a member](https://platform.claude.com/docs/en/api/beta/organization/rbac_groups/members/remove)            | `group_id`     | `rbac_group_id`                                                                                                                                 |
| [RBAC roles](https://platform.claude.com/docs/en/api/beta/organization/rbac_roles/list): list and retrieve                                                                                                                                                       | `name`         | `display_name`                                                                                                                                  |
| [Organization](https://platform.claude.com/docs/en/api/organization/rate_limits/list) and [workspace](https://platform.claude.com/docs/en/api/organization/workspaces/rate_limits/list) rate limits: list, and the response to setting a workspace's rate limits | `group_type`   | `group.type`                                                                                                                                    |
| [API keys](https://platform.claude.com/docs/en/api/organization/api_keys/list): list, retrieve, and update                                                                                                                                                       | `workspace_id` | `scope` (different values; see [`workspace_id` on API keys](https://platform.claude.com/docs/en/api/migration-2026-10-21#api-key-workspace-id)) |

Claude Enterprise Analytics API:

| Endpoints                                                                                                                                                                                                                  | Old name      | New name              |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | --------------------- |
| Per-user [usage](https://platform.claude.com/docs/en/api/beta/organization/analytics/user_usage_report/list) and [cost](https://platform.claude.com/docs/en/api/beta/organization/analytics/user_cost_report/list) reports | `actor.email` | `actor.email_address` |
| [Activity summaries](https://platform.claude.com/docs/en/api/beta/organization/analytics/summaries/list)                                                                                                                   | `summaries`   | `data`                |

The `group_type` query parameter on the rate-limit list endpoints and the `workspace_id` filter on the API key list endpoint are unchanged.

#### `workspace_id` on API keys

Affects you if your code reads `workspace_id` on API keys.

In `2023-06-01`, an API key's `workspace_id` is the ID of the workspace the key is bound to, or `null` for a key bound to the Default Workspace and for a key that isn't bound to one workspace (an all-workspaces key).

In `2026-10-21`, `workspace_id` is removed. Read `scope` instead: for a key bound to a workspace, including the Default Workspace, `scope.workspace_id` is that workspace's real ID, and an all-workspaces key has a `scope` of `{"type": "organization"}`, with no `workspace_id`.

If your code treated a `null` `workspace_id` as the Default Workspace, compare `scope.workspace_id` with your Default Workspace's ID instead. To find that ID, call [List Workspaces](https://platform.claude.com/docs/en/api/organization/workspaces/list) with `include_default=true` and take the workspace named `"Default"`.

## SDK and CLI changes

Earlier SDK and CLI versions sent `anthropic-version: 2023-06-01` with every request. The versions under [Switch to 2026-10-21](https://platform.claude.com/docs/en/api/migration-2026-10-21#upgrade) send `2026-10-21`, including their Amazon Bedrock and Google Cloud clients. The changes in [Breaking changes](https://platform.claude.com/docs/en/api/migration-2026-10-21#breaking-changes) apply to them too: the removed parameters and the old field names are also gone from the SDK's types and from the CLI's flags and output.

### `MessageParam` is a union with one type for each role

In earlier versions, `MessageParam` was one type for all roles. Each role now has a type of its own: `UserMessageParam`, `AssistantMessageParam`, and `SystemMessageParam`. `BetaMessageParam` is split the same way, into `BetaUserMessageParam`, `BetaAssistantMessageParam`, and `BetaSystemMessageParam`. The requests that the SDK sends have the same fields and values as before.

<Tabs exclude="shell">
  <Tab title="Python">
    A message written as a dict literal, such as `{"role": "user", "content": "Hello"}`, needs no change.

    In v1, `anthropic.types.MessageParam` was one `TypedDict`. In v2 it is a union of the three types, and `anthropic.types.beta.BetaMessageParam` is a union of its three. Both names still work in annotations, such as `list[MessageParam]`.

    Calling either name now raises a `TypeError`, and a type checker flags it. Write the message as a dict:

    ```python
    # Before
    message = MessageParam(role="user", content="Hello")

    # After
    message: MessageParam = {"role": "user", "content": "Hello"}
    ```

    Subclassing either name also raises a `TypeError`. Subclass the type for the role, such as `UserMessageParam`.

    The remaining changes affect type checking only. A dict whose `role` is a variable that can hold more than one role matches no type in the union, so build the dict for one role at a time:

    ```python
    # Before
    def to_message(role: Literal["user", "assistant"], text: str) -> MessageParam:
        return {"role": role, "content": text}


    # After
    def to_message(role: Literal["user", "assistant"], text: str) -> MessageParam:
        if role == "user":
            return {"role": "user", "content": text}
        return {"role": "assistant", "content": text}
    ```

    Two more apply to `BetaMessageParam` only:

    * `clear_at` and `output_config` are keys of `BetaSystemMessageParam`, so a user or assistant message can't have them.
    * `content` is optional in `BetaSystemMessageParam`, so pyright reports `message["content"]`. Read it with `message.get("content")`, or check first that `message["role"]` is not `"system"`.
  </Tab>

  <Tab title="TypeScript">
    `MessageParam` was one interface. It is now `UserMessageParam | AssistantMessageParam | SystemMessageParam`, and `BetaMessageParam` is the same union of its three types. Only the types changed. A message literal that names its role, such as `{ role: "user", content: "Hello, Claude" }`, type-checks as it did, so most code needs no change.

    The system type differs from the other two. Its `content` is a string or text blocks, plus `tool_addition` and `tool_removal` blocks in `beta`, so an image block there no longer compiles. In `beta`, only it has `clear_at` and `output_config`, and its `content` is optional.

    Code that handles a message of any role has to check `role` before it replaces or adds content blocks, and before it reads `clear_at` or `output_config`:

    ```typescript
    // Before
    const copy: Anthropic.MessageParam = { ...message, content: blocks };

    // After
    const copy: Anthropic.MessageParam =
      message.role === "system" ? message : { ...message, content: blocks };
    ```

    The `content` of a `BetaMessageParam` can be `undefined`:

    ```typescript
    // Before
    const types =
      typeof message.content === "string" ? [] : message.content.map((block) => block.type);

    // After
    const types = Array.isArray(message.content) ? message.content.map((block) => block.type) : [];
    ```

    The type `BetaMessageParam["content"]` includes `undefined` too. For the content of a user or assistant message, use `BetaMessageContentParam`.

    An interface can't extend a union, and a class can't implement one. Use a type alias, or name the type of one role instead.

    ```typescript
    // Before
    interface StoredMessage extends Anthropic.MessageParam {
      id: string;
    }

    // After
    type StoredMessage = Anthropic.MessageParam & { id: string };
    ```
  </Tab>

  <Tab title="C#">
    In v12, `MessageParam` and `BetaMessageParam` were classes whose `Role` and `Content` you set. In v13 each is a union of the three classes for its roles, and the `Role` enums in `Anthropic.Models.Messages` and `Anthropic.Models.Beta.Messages` are gone.

    When you build a message, name the class for its role and delete `Role`. Each class converts to the union implicitly:

    ```csharp
    // Before
    Messages = [new() { Role = Role.User, Content = "Hello, Claude" }],

    // After
    Messages = [new UserMessageParam { Content = "Hello, Claude" }],
    ```

    `new UserMessageParam("Hello, Claude")` works too.

    The classes that hold `Content` change as well:

    * `MessageParamContent` is now `MessageContentParam`, and `BetaMessageParamContent` is now `BetaMessageContentParam`.
    * A system message has a content class of its own. A `SystemMessageParamContent` holds a string or a list of `TextBlockParam`, and a `BetaSystemMessageParamContent` holds a string or a list of `SystemMessageContentBlock`. A `List<ContentBlockParam>` or a `List<BetaContentBlockParam>` no longer converts to it.
    * `ClearAt` and `OutputConfig` are on `BetaSystemMessageParam` only.

    When you read a message, the union still has `Role` and `Content`, but you can't set them, so a `with` expression goes on the class for the role. `Role` now holds a `MessageParamRole` or a `BetaMessageParamRole`. `BetaMessageParam.Content` can be `null`, because `Content` is optional on `BetaSystemMessageParam`.

    ```csharp
    // Before
    if (message.Role.Value() == Role.Assistant) { }

    // After
    if (message.Role.Value() == MessageParamRole.Assistant) { }
    ```

    `TryPickUser`, `TryPickAssistant`, `TryPickSystem`, `Switch`, and `Match` give you the class for the role.
  </Tab>

  <Tab title="Go">
    In v1, `MessageParam` was one struct with a `Role` and a `Content` field. In v2 it is a union: `OfUser` holds a `*UserMessageParam`, `OfAssistant` an `*AssistantMessageParam`, and `OfSystem` a `*SystemMessageParam`. `BetaMessageParam` changes in the same way. The SDK sends the same JSON as before, apart from the order of the keys in a beta system message.

    `NewUserMessage`, `NewAssistantMessage`, `NewBetaUserMessage`, and the `ToParam` methods of `Message` and `BetaMessage` work as before, so code that builds its messages with them needs no change.

    A struct literal names the type for its role. Leave `Role` out, because each type marshals its own:

    ```go
    // Before
    anthropic.MessageParam{
    	Role:    anthropic.MessageParamRoleAssistant,
    	Content: blocks,
    }

    // After
    anthropic.MessageParam{OfAssistant: &anthropic.AssistantMessageParam{
    	Content: blocks,
    }}

Cut at 300 lines. The page has the rest.

Feedback