Anthropic provider
AnthropicProvider calls Anthropic’s Messages API for text, image input, client tools, native web search, streaming, prompt cache accounting, and extended thinking.
Runs: cloud. Needs:
ANTHROPIC_API_KEY(the only secret the built-in registry resolves). Models: fetched live, no static list.
Direct integrations can construct the provider with an auth token, structured SDK credentials, or a custom base URL. The built-in registry does not resolve those. A model in the selector does not prove every request option works with it, so check thinking mode, sampling parameters, stop reasons, and tool behavior before making it a default.
Files
| Concern | Path |
|---|---|
| Provider | packages/runtime/src/providers/anthropic-provider.ts |
| Recommended models | packages/runtime/src/recommended-models.ts |
| Provider registration | packages/runtime/src/providers/index.ts |
| Provider tests | packages/runtime/tests/providers/anthropic-provider*.test.ts |
| Catalog tests | packages/runtime/tests/recommended-models.test.ts |
| Cost calculation | packages/runtime/src/providers/cost-calculator.ts |
ClaudeAgentProvider is a separate integration backed by the Claude Agent SDK
and a local Claude subscription. Changes to AnthropicProvider do not affect it.
Model discovery
getAvailableLanguageModels() calls /v1/models on the configured API base URL.
It follows the Models API pagination
cursor and uses each model’s display_name. Discovery retries transient errors,
including Anthropic’s 529 overload response, and honors Retry-After as described
in Anthropic API errors.
There is no static fallback list. If discovery fails, the provider returns no
Anthropic models for that fetch. The curated entry in recommended-models.ts
is independent of discovery and must name an active model.
Model lifecycle
Use Anthropic’s model deprecation table
before adding or changing a recommended model. Do not recommend a -latest
alias: it can move between snapshots and does not communicate a lifecycle date
or version. Anthropic’s dateless ids from Claude 4.6 onward are pinned model
versions, not moving aliases.
When changing the recommendation:
- Confirm the model is active and generally available.
- Check that
@pydantic/genai-pricesrecognizes its exact id. - Verify text, streaming, tool use, and configured thinking behavior.
- Update the catalog test anchor in the same change.
Compatibility checks
Thinking
NodeTool uses manual thinking on models that accept budget_tokens:
{ "thinking": { "type": "enabled", "budget_tokens": 4096 } }
This shape is not valid for every current model. thinkingPolicy() in
anthropic-provider.ts matches model ids by substring:
| Policy | Models | Behavior |
|---|---|---|
adaptive_required |
Fable 5 and 5.1, Mythos 5 and Preview, Opus 5.5, Sonnet 5.5 | Thinks on every request. thinking: disabled and a forced tool choice throw. |
adaptive_default |
Opus 5, Sonnet 5, Haiku 5.5 | Thinks unless the caller disables it or forces a tool. |
adaptive_optional |
Opus 4.7 and 4.8 | Thinks only when the caller asks for adaptive thinking. |
manual |
Every other id | Takes a budget_tokens budget. |
The 5.5 ids contain the 5 ids as substrings, so they are checked first. Add a
new model family to that function before recommending it. Anthropic’s
extended thinking guide
lists the rules per model. Opus 5 only accepts disabled thinking at effort high
or lower, which the provider does not check.
Manual budget_tokens is deprecated but still accepted for Claude Opus 4.6 and
Claude Sonnet 4.6. Claude Haiku 4.5 also accepts manual thinking. Manual budgets
must be at least 1,024 tokens and less than max_tokens.
Tests must cover both streaming and non-streaming request shapes. Thinking with tools must preserve signed thinking blocks unchanged across the tool-result turn.
Sampling parameters
Anthropic rejects non-default temperature, top_p, and top_k on current
adaptive-only/default model families. The provider omits them for every model
whose thinkingPolicy() is not manual (rejectsCustomSampling()), following
Anthropic’s
parameter deprecation table.
Stop reasons
Every successful Messages response has a stop_reason. Anthropic documents
end_turn, max_tokens, stop_sequence, tool_use, pause_turn, refusal,
and model_context_window_exceeded in its
stop-reason guide.
pause_turn requires another request with the assistant content preserved; the
provider continues these turns internally. A refusal is an HTTP 200 response
and may contain no content. Claude Fable 5 also returns stop_details. The
provider surfaces refusals and truncated output instead of treating them as a
normal completed answer.
Tools and content
The provider maps NodeTool client tools to Anthropic input_schema definitions
without dropping supported schema constraints and maps web_search to
web_search_20250305. Anthropic still lists that web
search version as GA, alongside newer versions in the
tool reference.
The adapter also passes strict, input_examples, cache_control,
defer_loading, and allowed_callers when configured. Tool execution failures
are returned with Anthropic’s is_error marker.
Prompt caching
Every request carries two cache breakpoints. The first sits at the end of the
system prompt. Anthropic renders tools ahead of the system prompt, so this one
breakpoint caches the tool catalog and the prompt. The second sits on the last
cacheable block of the conversation, so the next tool round or turn reads the
history from the cache. Breakpoints set on tools count against Anthropic’s
limit of four. Pass promptCaching: false for a gateway that rejects
cache_control. The cacheControl option sends top-level automatic caching
in place of the tail breakpoint. Cache hits show up as
cache_read_input_tokens in usage.
A cached prefix survives only while its bytes stay the same. Keep turn-scoped context out of the system prompt and append it to the last user message, as the chat turn does with memory, invoked skills, and the user’s open documents.
Image input supports JPEG, PNG, GIF, and WebP through inline base64 blocks. Document input supports inline PDF data, HTTPS PDF URLs, and plain text, including document titles, context, and citation configuration. See Anthropic’s PDF support for API limits. Uploaded Anthropic Files API ids are not part of NodeTool’s shared message contract. Citation ranges and source metadata remain structured on text content; the provider does not append citation Markdown to Claude’s text.
Cost tracking
Claude token prices come from @pydantic/genai-prices. Check a new model id
before recommending it:
node -e "const {calcPrice}=require('@pydantic/genai-prices'); \
console.log(calcPrice({input_tokens:1000,output_tokens:500},'claude-sonnet-5','anthropic'))"
Do not add Claude token prices to the local non-token modality table. Update the upstream catalog or bump the dependency when it already contains the model.
Anthropic reports uncached input separately from cache reads and writes. The provider adds those values before sending usage to the shared cost calculator.
Verify
Run the focused catalog and provider tests first:
npm test --workspace=packages/runtime -- \
--run tests/recommended-models.test.ts \
tests/providers/anthropic-provider.test.ts \
tests/providers/anthropic-provider-coverage.test.ts
Then run the required repository checks:
npm run typecheck
npm run lint
npm run test
With ANTHROPIC_API_KEY configured, inspect discovery and run a small request:
npm run dev:nodetool -- models by-provider anthropic --kind llm --json
npm run dev:nodetool -- node run nodetool.agents.Agent \
--props '{"prompt":"hello","model":{"type":"language_model","provider":"anthropic","id":"claude-sonnet-5"},"max_tokens":50}'