Skip to content

feat(anthropic): support ANTHROPIC_CUSTOM_HEADERS for anthropic_messages models - #50796

Open
danliustc wants to merge 2 commits into
NousResearch:mainfrom
danliustc:feat/anthropic-custom-headers
Open

feat(anthropic): support ANTHROPIC_CUSTOM_HEADERS for anthropic_messages models#50796
danliustc wants to merge 2 commits into
NousResearch:mainfrom
danliustc:feat/anthropic-custom-headers

Conversation

@danliustc

Copy link
Copy Markdown

What

Adds support for the ANTHROPIC_CUSTOM_HEADERS environment variable so custom (and other) models running on the anthropic_messages wire format can attach arbitrary request headers.

Why

Custom models on the Anthropic-wire format had no way to inject custom request headers:

  • The OpenAI-side model.default_headers mechanism explicitly skips anthropic_messages/bedrock_converse modes (run_agent.py:4039 returns early for those api_modes).
  • ANTHROPIC_CUSTOM_HEADERS was not read anywhere in the codebase.

This blocks Anthropic-compatible gateways that require extra headers for project/tenant routing, WAF tokens, etc. (e.g. x-project: <project>).

How

  • New _parse_anthropic_custom_headers() parses the env var following Claude Code's convention: one Name: Value pair per line (newline-separated), whitespace-trimmed, lines without a : ignored.
  • New _apply_anthropic_custom_headers() merges those headers onto the client-level default_headers, with user values taking precedence over SDK/provider defaults (e.g. anthropic-beta) while preserving them.
  • Wired into all three Anthropic SDK client constructors: build_anthropic_client, _build_anthropic_client_with_bearer_hook (Azure Entra), and build_anthropic_bedrock_client.
  • No-op when the env var is unset.

Usage

# Single header
export ANTHROPIC_CUSTOM_HEADERS="x-project: my-project"

# Multiple headers (newline-separated)
export ANTHROPIC_CUSTOM_HEADERS=$'x-project: my-project\nx-team: infra'

This mirrors Claude Code's ANTHROPIC_CUSTOM_HEADERS convention; the OpenAI-wire equivalent remains model.default_headers in config.yaml.

Tests

  • Added 3 tests in tests/agent/test_anthropic_adapter.py: single header, multiple headers, and no-op when unset. All pass.
  • The pre-existing 14 failures in this test file (keychain/OAuth env-related) are unrelated to this change — verified they fail identically on a clean tree.

Docs

  • Documented the env var in website/docs/integrations/providers.md next to the anthropic_messages custom-provider example.

🤖 Generated with Claude Code

@alt-glitch alt-glitch added type/feature New feature or request comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/anthropic Anthropic native Messages API P3 Low — cosmetic, nice to have labels Jun 22, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for identifying a real Anthropic-wire gap. Current main still excludes custom-provider headers from this path: run_agent.py:4474 skips anthropic_messages and bedrock_converse before applying the existing resolver.

Problems

  • The new global ANTHROPIC_CUSTOM_HEADERS applies to every Anthropic SDK client, including native Anthropic and Bedrock. Current header configuration is endpoint-scoped: hermes_cli/config.py:5042-5074 matches providers / custom_providers by base_url, which prevents a proxy's tenant or auth header from being sent elsewhere.
  • The added tests cover only the static build_anthropic_client path. The bearer-hook and Bedrock constructor changes need coverage, including preservation of their existing anthropic-beta defaults.

Suggested changes

  • Reuse the existing extra_headers configuration/resolution path for Anthropic Messages clients and preserve endpoint scope.
  • Add constructor-specific regression tests for bearer-hook and Bedrock behavior.

This is an automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026
danliustc and others added 2 commits July 15, 2026 13:52
…ges models

Custom models on the anthropic_messages wire format had no way to attach
custom request headers: the OpenAI-side model.default_headers mechanism
explicitly skips anthropic_messages/bedrock_converse modes, and the env var
was not read anywhere.

Add ANTHROPIC_CUSTOM_HEADERS (newline-separated `Name: Value` pairs, mirroring
Claude Code's convention) and merge it onto the client-level default_headers in
build_anthropic_client, the Entra-bearer-hook client, and the Bedrock client.
User values take precedence over SDK/provider defaults (e.g. anthropic-beta)
while preserving them, letting custom endpoints behind a gateway send headers
like `x-project`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address hermes-sweeper review on NousResearch#50796:

- Reuse the existing endpoint-scoped extra_headers resolution
  (hermes_cli.config.get_custom_provider_extra_headers) for Anthropic
  Messages clients, matching providers/custom_providers entries by
  base_url. Config headers take precedence over the env var.
- Scope ANTHROPIC_CUSTOM_HEADERS to clients targeting an explicit custom
  base_url: native Anthropic (no base_url) and Bedrock never see it, so
  a proxy's tenant/auth header cannot leak to other providers.
- Match config entries against both the caller's original base_url and
  its normalized (v1-stripped) form, since config may record either.
- Add regression tests: bearer-hook constructor (env + config headers,
  beta preservation), Bedrock exclusion, native-Anthropic exclusion,
  config-over-env precedence, and normalized-URL matching.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@danliustc
danliustc force-pushed the feat/anthropic-custom-headers branch from b8371ef to 7cc7476 Compare July 15, 2026 05:57
@danliustc

Copy link
Copy Markdown
Author

Thanks for the review — addressed in 7cc7476 (branch also rebased onto current main).

Endpoint scoping (problem 1):

  • Anthropic Messages clients now reuse the existing endpoint-scoped resolution path: hermes_cli.config.get_custom_provider_extra_headers is applied in the client builders, matching providers / custom_providers entries by base_url (both the caller's original URL form and its normalized /v1-stripped variant, since config may record either). Config extra_headers take precedence over the env var.
  • ANTHROPIC_CUSTOM_HEADERS is retained for Claude Code parity, but no longer global: it only applies to clients constructed with an explicit custom base_url. Native Anthropic (no base_url) and Bedrock never see it, so a proxy's tenant/auth header cannot be sent elsewhere. Bedrock is fully excluded from both mechanisms.

Test coverage (problem 2): added constructor-specific regression tests:

  • bearer-hook constructor: env var + config extra_headers both applied, anthropic-beta defaults preserved;
  • Bedrock constructor: env var not applied, beta defaults intact;
  • native Anthropic (no base_url): env var not applied;
  • config-over-env precedence and normalized-URL matching.

Docs updated to present config extra_headers as the recommended endpoint-scoped mechanism with the env var as the Claude-Code-compatible alternative, including the precedence order.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have provider/anthropic Anthropic native Messages API sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants