d9e88e19e2
Port from openai/codex#39615: the authorization server discovered for an MCP server can change (protected-resource metadata edit, server migration, DNS takeover). Without binding, Hermes would send the stored refresh token to whatever issuer the server now advertises — handing a long-lived credential to a different authorization server. - HermesTokenStorage records hermes_issuer alongside cached tokens (stripped before OAuthToken.model_validate; never sent on the wire). - Both provider classes (tools/mcp_oauth.py legacy path and tools/mcp_oauth_manager.py managed path) stamp the discovered issuer on every token save and enforce the binding on _initialize. - On mismatch: refresh token is stripped from memory and disk; the unexpired access token keeps working; full re-auth happens at expiry. - Legacy token files without an issuer adopt the current one once (no forced re-login for existing installs — deliberate divergence from Codex, which requires reauth). Validated: 12 new tests + 163 existing MCP OAuth tests green; sabotage run confirms the new tests fail without the enforcement; E2E against the real manager provider class with a temp HERMES_HOME confirms mismatch strips and match preserves.