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:

  1. Confirm the model is active and generally available.
  2. Check that @pydantic/genai-prices recognizes its exact id.
  3. Verify text, streaming, tool use, and configured thinking behavior.
  4. 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}'