Anthropic Messages
Anthropic is not OpenAI with different header names. The Messages API has its own shape: top-level system, flat tool declarations, content blocks, prompt-cache breakpoints, signed thinking blocks, a token-counting endpoint, and a refusal stop reason that is a successful response. This battery speaks that wire directly.
@anthropic-ai/sdk is an optional peer dependency. Install it when you use this battery:
pnpm add @nhtio/adk@1.20260726.0 @anthropic-ai/sdkA consumer who deep-imports @nhtio/adk/batteries/llm/anthropic_messages without the peer gets an honest module-not-found for @anthropic-ai/sdk. The aggregate barrel @nhtio/adk/batteries/llm also resolves this peer, because it re-exports the battery.
import { AnthropicMessagesAdapter } from '@nhtio/adk/batteries/llm/anthropic_messages'
const executor = new AnthropicMessagesAdapter({
model: 'claude-sonnet-5-20260701',
apiKey: process.env.ANTHROPIC_API_KEY,
maxTokens: 1024,
autoAck: true,
})
// executor.executor() is the DispatchExecutorFn you hand to TurnRunner.Node-first, not browser-first
The adapter exposes dangerouslyAllowBrowser because the Anthropic SDK exposes it. That does not make browser API-key use safe: shipping a first-party Anthropic key to a browser exposes it to every user of the page. The supported browser architecture is a gateway/server that owns the Anthropic key and applies your auth, quotas, and policy there. If you deliberately enable browser use anyway, the battery sends what you asked for; it is not a tested target.
What this wire buys you
- Prompt caching.
cacheBreakpointscontrols wherecache_controlis emitted:'auto'(default),'system-only', or'off'.cacheTtlis optional: omit it for normal ephemeral caching, or set'5m'/'1h'. Auto placement targets the last system block, then the last content block of the most recenttool_result-bearing user turn, then the final user turn's last block. Anthropic caps breakpoints at four; if that cap would be exceeded the adapter warns rather than pretending extra breakpoints were sent. - Signed thinking replay. Thinking blocks with signatures are persisted with replay tag
anthropic-messages-thinking-v1. The signature is bound to the exact conversation prefix that produced it. ADK history can change through thrift, compaction, shedding, different tools, or a different system prompt; when the prefix no longer matches, the battery drops the whole stale thinking block instead of replaying a block Anthropic will reject. - Native refusal outcomes.
stop_reason: 'refusal'is an HTTP 200 terminal outcome, not an exception. It carriesstop_details, so handle it as a completed model turn with policy metadata. - Token counting.
adapter.countTokens(input, overrides?)calls Anthropic's count-tokens endpoint and returns{ inputTokens, raw }.inputmay be a normal ADKDispatchContextor a pre-built request withmessagesand optionalsystem/tools.
The gotchas, because they will get you
maxTokens is required. Anthropic requires max_tokens; this adapter's option is maxTokens, and there is no useful default that is safe for every application.
Deprecated sampling fields are never defaulted. temperature, topP, and topK are typed but marked @deprecated. Models after Opus 4.6 reject these fields with a 400 in common cases. The adapter sends them only if you set them, and logs a warning before doing so. Prefer prompting, model selection, or a gateway that knowingly rewrites requests.
There is no request repair. The battery warns and sends the request you built:
- Anthropic requires
tool_use.id/tool_result.tool_use_idto match^[a-zA-Z0-9_-]+$. A badToolCall.idis warned about and sent as-is; Anthropic will usually answer 400. Mint conforming IDs in your caller. - Unsupported JSON-Schema keywords in
output_config.formatare warned about, not stripped. Prune keywords such as length, numeric-bound, and item-count constraints yourself before sending to Anthropic. - Fable-class model hazards are warned about and sent: forced
tool_choice,thinking: { type: 'disabled' }, andbudget_tokens. Send{ type: 'adaptive' }thinking on Fable-class models and avoid forced tool use there unless your gateway deliberately normalizes it.
Context overflow is a 400. Anthropic can return a 400 whose body says prompt is too long; the adapter classifies that as E_ANTHROPIC_MESSAGES_CONTEXT_OVERFLOW, not a generic bad request.
Upstream 529 is retriable. The ADK uses status 529 for its own fatal option/context exceptions, but an Anthropic HTTP 529 overloaded_error is a transient upstream overload and is retried according to the adapter's retry config.
Media support is narrower than ADK's media model. Images and documents can be represented natively; the shipped code accepts base64 PDF and plain-text document sources. Audio and video cannot; they follow unsupportedMediaPolicy and, by default, throw E_ANTHROPIC_MESSAGES_UNSUPPORTED_MEDIA_MODALITY.
Server-side refusal fallbacks are intentionally not in v1. They live on Anthropic's beta Messages surface, with separate request/response/stream types, so this battery does not expose a flag that silently switches API families. Full option and exception details are in Assembly → LLM batteries.