hermes-agent/hermes_cli/subcommands
Max Hsu 075f93ad78 fix(mcp): auto-recover from invalid_client on stale OAuth client registration
Fixes #36767.

Two complementary recoveries for the recurring "delete three cache files and
re-auth by hand" ritual when an MCP server's dynamically-registered OAuth
client goes dead server-side (IdP redeploy / DB wipe / rebrand):

- Auto-heal (token-endpoint subset): HermesMCPOAuthProvider now sniffs
  auth-flow responses and, on a 400/401 `invalid_client` from the discovered
  token endpoint, backs up + deletes `<server>.client.json` and `.meta.json`
  and clears the in-memory client so the SDK re-runs RFC 7591 dynamic client
  registration on the next flow. Conservative by construction: only
  dynamically-registered (non config-supplied) clients, only the token
  endpoint, only on a word-boundary `invalid_client` match (so RFC 7591's
  `invalid_client_metadata` does not trip it); best-effort so a miss never
  breaks the live flow. Covers both code-exchange and refresh when the token
  endpoint was discovered. Tokens are preserved.

- `hermes mcp reauth [<name>|--all]`: the reporter's primary symptom — the
  IdP's in-browser "Redirect URI Mismatch" — produces no HTTP signal (the SDK
  only sees a callback timeout), so it cannot be auto-detected. The new
  command re-auths one or ALL `auth: oauth` servers, serially: one browser
  flow at a time, which also fixes the startup popup storm when several
  servers are stale at once. Single-server reauth is factored out of
  `mcp login` and shared.

Tests: +14 (poison helper x2; token-endpoint detection x5 incl. wrong-endpoint,
success-response, pre-registered, and invalid_client_metadata negative guards;
a bridge integration test driving the real async_auth_flow generator to prove
the detection hook preserves the bidirectional asend() forwarding contract;
reauth CLI x6). Verified against the pinned mcp==1.26.0: scripts/run_tests.sh
122/122 green for the touched suites; check-windows-footguns.py and ruff clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 00:35:27 -07:00
..
__init__.py refactor(cli): extract hermes cron parser into hermes_cli/subcommands/ (god-file Phase 2) 2026-06-07 22:18:14 -07:00
_shared.py refactor(cli): extract hermes cron parser into hermes_cli/subcommands/ (god-file Phase 2) 2026-06-07 22:18:14 -07:00
acp.py refactor(cli): promote 9 closure handlers to top-level + extract their parsers (god-file Phase 2 follow-up) 2026-06-07 22:56:23 -07:00
auth.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
backup.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
claw.py refactor(cli): promote 9 closure handlers to top-level + extract their parsers (god-file Phase 2 follow-up) 2026-06-07 22:56:23 -07:00
config.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
cron.py revert(cron): return cron job storage to per-profile (reverts #32117 + #50993) (#51116) 2026-06-22 17:53:50 -07:00
dashboard.py fix(security): close hermes-0day MCP-persistence attack surface 2026-06-21 19:05:27 -07:00
debug.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
doctor.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
dump.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
gateway.py feat(relay): Phase 5 Unit C — wake primitive (gateway side) (#51595) 2026-06-24 11:00:11 +10:00
gui.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
hooks.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
import_cmd.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
insights.py refactor(cli): promote 9 closure handlers to top-level + extract their parsers (god-file Phase 2 follow-up) 2026-06-07 22:56:23 -07:00
login.py fix(cli): deprecated hermes login fails gracefully for any provider 2026-06-17 12:55:40 +05:30
logout.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
logs.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
mcp.py fix(mcp): auto-recover from invalid_client on stale OAuth client registration 2026-06-26 00:35:27 -07:00
memory.py refactor(cli): promote 9 closure handlers to top-level + extract their parsers (god-file Phase 2 follow-up) 2026-06-07 22:56:23 -07:00
model.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
pairing.py refactor(cli): promote 9 closure handlers to top-level + extract their parsers (god-file Phase 2 follow-up) 2026-06-07 22:56:23 -07:00
plugins.py refactor(cli): promote 9 closure handlers to top-level + extract their parsers (god-file Phase 2 follow-up) 2026-06-07 22:56:23 -07:00
postinstall.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
profile.py fix(profile): make clone-from a full source selector 2026-06-13 07:33:58 -07:00
prompt_size.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
security.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
setup.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
skills.py feat(skills): find & diff user-modified bundled skills 2026-06-18 12:26:20 +05:30
slack.py feat(slack): add --no-assistant flag to manifest generation 2026-06-23 11:30:10 -07:00
status.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
tools.py refactor(cli): promote 9 closure handlers to top-level + extract their parsers (god-file Phase 2 follow-up) 2026-06-07 22:56:23 -07:00
uninstall.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
update.py fix(update): default pre-update backup to off (#52729) 2026-06-25 16:01:09 -07:00
version.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
webhook.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00
whatsapp.py refactor(cli): extract 25 more subcommand parsers into hermes_cli/subcommands/ 2026-06-07 22:18:14 -07:00