Skip to content

feat(oauth): HERMES_OAUTH_* env flags to bypass Claude.ai content-filter triggers - #20865

Closed
masserfx wants to merge 3 commits into
NousResearch:mainfrom
masserfx:fix/oauth-content-filter-workarounds
Closed

feat(oauth): HERMES_OAUTH_* env flags to bypass Claude.ai content-filter triggers#20865
masserfx wants to merge 3 commits into
NousResearch:mainfrom
masserfx:fix/oauth-content-filter-workarounds

Conversation

@masserfx

@masserfx masserfx commented May 6, 2026

Copy link
Copy Markdown

Context

Anthropic's Claude.ai OAuth subscriptions enforce three undocumented content-filter rules that surface as a misleading HTTP 400 "out of extra usage" on Hermes deployments. This PR ships three opt-in env-var gates so OAuth deployments can disable the offending behaviours. All default off — zero behaviour change for API-key or Claude-Code-CLI users.

Companion to #20850 (recognises "out of extra usage" as the second variant of the existing oauth_long_context_beta_forbidden classifier). That PR enables reactive recovery for variant (2); this PR addresses the underlying triggers so the strip is permanent and the auxiliary-client path also works.

The three triggers (empirically bisected)

# Trigger New flag Effect
1 mcp_ tool-name prefix on tools not registered with the account's Claude Code MCP setup HERMES_OAUTH_NO_MCP_PREFIX=1 Skip the prefix; Hermes-internal routing works without it
2 SKILLS_GUIDANCE + MEMORY_GUIDANCE injected together HERMES_OAUTH_COMPACT_GUIDANCE=1 Drop SKILLS_GUIDANCE; MEMORY_GUIDANCE retained
3 context-1m-2025-08-07 beta on subscriptions without 1M entitlement, on auxiliary clients (title_generator, summarization) HERMES_OAUTH_FORCE_DROP_1M_BETA=1 Universal strip in _common_betas_for_base_url

Enabling all three is the recommended bundle for Claude.ai OAuth deployments.

Why each is needed

(1) mcp_ prefix. Today's behaviour adds mcp_ to every tool name when is_oauth=True (anthropic_adapter.py:1862). The filter rejects names not registered with the account's Claude Code MCP setup. Bisection: identical request with prefix → HTTP 400; without prefix → HTTP 200.

(2) Combined guidance blocks. Either alone passes; together they trip the filter. Bisection of MEMORY_GUIDANCE alone (1371c) → OK, SKILLS_GUIDANCE alone (385c) → OK, MEMORY + SKILLS only (1757c) → FAIL. The MEMORY_GUIDANCE is more useful in practice (cross-session preferences); dropping SKILLS_GUIDANCE is the lower-impact strip.

(3) Universal context-1m strip. The reactive recovery in run_agent.py:12232 flips _oauth_1m_beta_disabled on subscriptions that reject the beta — but only for the main runtime client. Auxiliary clients (agent/auxiliary_client.py builds them in 4+ spots) call build_anthropic_client(...) without drop_context_1m_beta=True, so title_generator and summarization fail every time on Claude.ai OAuth. Reading the env var inside _common_betas_for_base_url makes the strip universal without each call site having to thread the parameter.

Verification

Telegram gateway running 24/7 on claude-haiku-4-5 via a Claude.ai subscription:

Before: 120× HTTP 400 / day, every cron and every Telegram message dies.

After (all 3 flags + companion #20850):

user:      Test po fixu
assistant: Super! 👍 Vypadá to, že je všechno v pořádku.

Test plan

  • test_force_drop_1m_beta_via_env — env var strips context-1m-2025-08-07
  • test_force_drop_1m_beta_default_off — default behaviour preserved
  • test_oauth_no_mcp_prefix_env_skips_tool_renaming — env var skips prefix
  • test_oauth_mcp_prefix_default_on — default mcp_* prefix preserved
  • test_oauth_drop_context_1m_beta_strips_only_1m (existing) still passes
$ venv/bin/python -m pytest tests/agent/test_anthropic_adapter.py -k "drop_context_1m or force_drop or mcp_prefix or no_mcp" -v
6 passed in 2.30s

🤖 Generated with Claude Code

…iggers

Anthropic's Claude.ai OAuth subscriptions enforce three undocumented
content-filter rules that cause Hermes-on-OAuth deployments (gateway,
cron, etc.) to fail with a misleading HTTP 400 "out of extra usage" on
every request that carries non-trivial system prompts or tools.

This PR introduces three env-var gates so an OAuth deployment can opt
out of the offending behaviours without changing default behaviour for
existing API-key or Claude-Code-CLI deployments.

## Triggers identified (via empirical bisection)

1. **`mcp_*` tool name prefix.** Hermes mimics the Claude Code CLI
   convention by prefixing every tool with `mcp_`. The filter rejects
   `mcp_*` names that aren't registered with the account's Claude Code
   MCP setup. Renamed tools (no prefix) pass.

2. **SKILLS_GUIDANCE + MEMORY_GUIDANCE combined.** Either guidance block
   alone passes; injecting both together trips the filter. Skipping
   SKILLS_GUIDANCE while keeping MEMORY_GUIDANCE preserves most of the
   useful behaviour.

3. **`context-1m-2025-08-07` beta on subscriptions without 1M entitlement.**
   Already handled reactively in `run_agent.py` (drop + retry), but
   auxiliary clients (`title_generator`, summarization, etc.) build their
   Anthropic client without `drop_context_1m_beta=True` and fail
   immediately with "long context beta is not yet available" — no retry
   path. The new env flag makes the strip universal across every
   `build_anthropic_client` caller.

## Env flags (all default off — zero behaviour change for existing users)

| Env var | Effect | Affects |
|---|---|---|
| `HERMES_OAUTH_NO_MCP_PREFIX=1` | Skip mcp_ tool prefix on OAuth | `_oauth_mcp_prefix_enabled()` in adapter |
| `HERMES_OAUTH_COMPACT_GUIDANCE=1` | Skip SKILLS_GUIDANCE injection | `tool_guidance` build in run_agent |
| `HERMES_OAUTH_FORCE_DROP_1M_BETA=1` | Universal context-1m strip | `_common_betas_for_base_url` |

Recommended bundle for Claude.ai OAuth deployments: enable all three.

## Verification

Telegram gateway running 24/7 on `claude-haiku-4-5` via Claude.ai
subscription failed every message with HTTP 400 "out of extra usage" —
identical session post-fix returns:

  user:      Test po fixu
  assistant: Super! 👍 Vypadá to, že je všechno v pořádku.

## Test plan

- [x] `test_force_drop_1m_beta_via_env` — env var strips context-1m
- [x] `test_force_drop_1m_beta_default_off` — default behaviour preserved
- [x] `test_oauth_no_mcp_prefix_env_skips_tool_renaming` — env var disables prefix
- [x] `test_oauth_mcp_prefix_default_on` — default behaviour preserved
- [x] Existing `test_oauth_drop_context_1m_beta_strips_only_1m` still passes
- [x] All 6 tests pass under pytest-xdist

```
$ venv/bin/python -m pytest tests/agent/test_anthropic_adapter.py -k "drop_context_1m or force_drop or mcp_prefix or no_mcp" -v
6 passed in 2.30s
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@alt-glitch alt-glitch added type/feature New feature or request P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/anthropic Anthropic native Messages API area/auth Authentication, OAuth, credential pools labels May 6, 2026
masserfx and others added 2 commits May 21, 2026 09:11
Both scripts have been live on the Hetzner host for a while but never
versioned, so they could (and did) drift from what the gateway expects.

- hermes-agent-updater.sh — cron-driven auto-updater. Now tracks the
  masserfx fork branch (not NousResearch/main, which lacks the OAuth
  content-filter workarounds the gateway depends on) and syncs deps via
  `uv sync` rather than `pip install`, since the venv is uv-managed and
  has no pip/ensurepip inside.

- hermes-agent-warmup.py — post-restart health probe that exercises the
  patched OAuth path with a minimal messages.create call. Import of
  `_oauth_mcp_prefix_enabled` is now wrapped in try/except so the script
  degrades gracefully on checkouts that don't have the symbol yet.

Paths and remote names are hard-coded for the leos@hetzner deployment;
adapt before reusing on another box.
…d + paperclip report fix

Two production fixes from a server health-check, archived as source-of-truth
under scripts/ops/ (manual-deploy, not auto-deployed):

- systemd/hermes-agent.service: add ExecStartPre DNS readiness guard.
  network-online.target does not guarantee a working resolver, causing a boot
  race where the gateway failed to resolve api.telegram.org, got stuck on a
  sticky fallback IP and leaked a dead socket. Guard waits up to 60s for DNS
  (visible log, no silent fallback) then starts anyway.

- paperclip/daily_report.py: control plane moved to localhost:3100 (public
  paperclip.frigeble.com:443 was taken over by the Plane PM proxy). Read base
  URL from PAPERCLIP_API_URL; board credentials now come solely from .env with
  fail-fast (removed hardcoded password default — was a plaintext secret).
- paperclip/run_daily_report.sh: source BOARD_EMAIL/BOARD_PASSWORD from .env.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for documenting the OAuth symptoms and supplying focused adapter tests. This automated hermes-sweeper review is closing this because the proposed user-facing mechanism conflicts with a standing configuration policy.

  • AGENTS.md:102-107 requires non-secret behavioral settings to use config.yaml, not new HERMES_* environment variables. All three substantive changes here are opt-in behavior gates (HERMES_OAUTH_NO_MCP_PREFIX, HERMES_OAUTH_COMPACT_GUIDANCE, and HERMES_OAUTH_FORCE_DROP_1M_BETA).
  • The tool-name portion is also superseded: merged PR fix(anthropic): no single-underscore mcp_ tool names on the OAuth wire (plan-limit billing) #47723 (f9c8d95e43662d754eb296551695e0be554bc58e) is on current main, and agent/anthropic_adapter.py:2552-2581 now normalizes OAuth wire names to mcp__, avoiding the single-underscore classifier trigger while retaining OAuth tool routing.
  • Current main also avoids sending the 1M beta to native Anthropic by default (agent/anthropic_adapter.py:310-328, 596-625), covering the stated subscription/auxiliary-call failure mode without a global environment override.

If a remaining user-configurable behavior is needed after the current OAuth path is evaluated, it should be proposed through the documented config.yaml mechanism rather than a new .env flag. The deployment-specific updater, Paperclip, and systemd files should be kept out of a focused core OAuth change.


Closed as not-planned per standing maintainer policy (env-var-for-config). This is a design-direction decision, not a code-quality judgment — see the Contribution Rubric in AGENTS.md for what the project is looking for. If you believe this policy was misapplied to your change, comment here and a maintainer will take a look.

@teknium1 teknium1 closed this Jul 12, 2026
@teknium1 teknium1 added the sweeper:not-planned Sweeper: closed per standing maintainer policy (design direction) label Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists provider/anthropic Anthropic native Messages API sweeper:not-planned Sweeper: closed per standing maintainer policy (design direction) type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants