+
    lHàj	 ã                   ó:  a € Rƒ t‰0 t R t^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	H
t ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIHt  ^ RIt ^ RIt^ RIHtHt ^ RIHt ^ RI H!t!H"t" ^ RI#H$t$H%t% ^ RI&H't' ^ R	I(H)t) ^ R
I*H+t,H-t- ]!'       d   ^ RI.H/t/ ^ RI0H1t1H2t2H3t3H4t4 ]Pj                  ! ]64      t7Rt8 ! R R]94      t:];! ]Px                  ]Pz                  ]P|                  ]P~                  04      t@R R ltAR]8/R R lltBR R ltC]Pˆ                  ! R4      RJsE\Š        '       g   ]7P�                  R4       R„tG/ tH] ^ k R R ltI ^ RIJHKtK  ! R R]94      tLRsM] ^k ]Pœ                  ! RRR 7      tO]Pœ                  ! R!R"R 7      tP];! 0 R…m4      tQR#tRR†R$ R% lltSR& R' ltT/ tU] ^k ^tVR( R) ltWR* R+ ltXR, R- ltYR. R/ ltZR0 R1 lt[R2 R3 lt\R4 R5 lt]R6 t^R7 t_R8 R9 lt`R: R; ltaR< R= ltbR> R? ltc ! R@ RA4      tdR†RB RC llteRD RE ltfRF RG ltgRH RI lthRJ RK ltiRLtjRMtkR†RN RO lltlR‡RP RQ lltmRR RS ltnRT RU ltoRˆRV RW lltpRsq] ^k RXR/RY RZ lltrR[tsR‰tt];! R\R]04      tuR^ R_ ltv. tw] ^k R` Ra ltxRb Rc ltyR†Rd Re lltzRf Rg lt{Rh Ri lt|Rj Rk lt}R†Rl Rm llt~Rn Ro ltRpt€Rqt�R‡Rr Rs llt‚RtRuRvR/Rw Rx lltƒRy Rz lt„R{ R| lt…R} R~ lt†RvR/R R€ llt‡R†R� R‚ lltˆR#   ] d    Rt EL�i ; i  ] d    Rt EL¨i ; i  ] d    RtK EL½i ; i)Ša{  MCP OAuth 2.1 client support: browser authorization-code flow with PKCE.

The SDK's ``OAuthClientProvider`` does discovery, client identification, PKCE, exchange and
refresh; this module supplies ``HermesTokenStorage`` (on-disk persistence), the localhost callback
listener and ``build_oauth_auth()`` (legacy entry point). client_id is Hermes' Client ID Metadata
Document URL (CIMD) when the server supports it, else RFC 7591 DCR. ``mcp_servers.<name>.oauth`` keys
(all optional): client_id, client_secret, scope, redirect_port, redirect_uri (proxy callback),
redirect_host, client_name, client_metadata_url, cimd, user_agent, timeout.)Úinstall_hintN)Úpartialmethod)ÚBaseHTTPRequestHandlerÚ
HTTPServer©ÚPath)ÚTYPE_CHECKINGÚAny)Úparse_qsÚurlparse)Úsecure_parent_dir)Úatomic_json_write)Úcontextvar_setÚget_dashboard_oauth_flow)ÚOAuthClientProvider)ÚOAuthClientInformationFullÚOAuthClientMetadataÚOAuthMetadataÚ
OAuthTokeng      N@c                   ó   € ] tR t^<tRtRtR# )ÚRefreshFenceTimeoutaÕ  The refresh fence could not be acquired within its bound.

Raised so the caller FAILS CLOSED. Submitting a refresh token we are not
certain we own is the whole defect class this fence exists to close: on a
provider with single-use refresh tokens it burns the credential and logs
the user out of a working session. Aborting this one refresh attempt is
strictly cheaper -- the next request retries, and by then the peer that
held the fence has published its replacement.
© N©Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__static_attributes__r   ó    Ú4/home/ubuntu/.hermes/hermes-agent/tools/mcp_oauth.pyr   r   <   s   † õr   r   c                ó0   € V ^8„  d   QhR\         R\         /# )é   ÚpathÚreturnr   )Úformats   "r    Ú__annotate__r&   M   s   € ÷ ;ñ ;œTð ;¤dñ ;r   c                 óF   € V P                  V P                  R ,           4      # )z.refresh.lock)Úwith_suffixÚsuffix)r#   s   &r    Ú_refresh_lock_pathr*   M   s   € Ø×Ñ˜DŸK™K¨/Õ9Ó:Ð:r   Útimeoutc                ó<   € V ^8„  d   QhR\         R\        R\        /# )r"   r#   r+   r$   )r   ÚfloatÚint)r%   s   "r    r&   r&   Q   s'   € ÷ Gñ G¤dð G¼ð GÔcfñ Gr   c             ƒ  óî  "  € \        V 4      p ^ RIHp V! VP                  4       \	        V4       \
        P                  ! V\
        P                  \
        P                  ,          R4      p\        P                  ! 4       T,           p   \        e=   \        P                  ! T\        P                   \        P"                  ,          4       T# \$        e)   \$        P&                  ! T\$        P(                  ^4       T# \        R4      h  \         d"   p\        RTP                   RT 24      ThRp?ii ; i  \         dˆ   pTP*                  \,        9  d   \        RT 24      Th\        P                  ! 4       T8¼  d   \        RTR	 R
TP                   R24      Rh\.        P0                  ! R4      G Rj  x€L 
   Rp?EK=  Rp?ii ; i  \2         d    \
        P4                  ! T4       h i ; i5i)ac  Take the fence that owns one refresh generation across read -> POST -> persist.

Token files are written atomically (``_write_json``), so a reader never
sees a torn file; the only cross-process hazard is the read-modify-write
of a single-use refresh token. The damaging interleaving is:

    A: get_tokens() -> R1
    B: get_tokens() -> R1
    A: POST R1              -> 200, receives R2
    B: POST R1              -> 400, credential already burned
    B: clear_tokens()       -> user is logged out of a live session

No lock scoped to one file read or write can prevent it: the fence must
be held across the POST. It lives in a ``.refresh.lock`` sibling of the
token file so the holder can still read/write the tokens normally.

Entered from the SDK's coroutine-driven auth flow, so the wait is an
``asyncio.sleep`` poll on a non-blocking lock: a peer's slow network
round trip must not freeze every other task on this event loop. No
in-process lock layer is needed: an advisory lock on a fresh descriptor
already excludes sibling tasks and threads of the same process.

Acquisition failure RAISES. Degrading to "proceed unlocked" would
reintroduce the exact race. Returns the locked descriptor; the caller
hands it back to ``release_refresh_fence`` from its own exit path (the
SDK drives the refresh as a generator, so no single ``with`` block can
span the critical section).
©Úmkdir_under_hermes_homeé€  zrefresh fence unavailable (z): Nz;refresh fence unsupported: no flock/msvcrt on this platformz.refresh fence unavailable on this filesystem: z!refresh fence held by a peer for z.0fzs (Ú)gš™™™™™©?)r*   Úhermes_constantsr1   Úparentr   ÚosÚopenÚO_RDWRÚO_CREATÚOSErrorr   ÚnameÚtimeÚ	monotonicÚfcntlÚflockÚLOCK_EXÚLOCK_NBÚmsvcrtÚlockingÚLK_NBLCKÚerrnoÚ_FENCE_CONTENTION_ERRNOSÚasyncioÚsleepÚBaseExceptionÚclose)r#   r+   Ú	lock_pathr1   ÚfdÚexcÚdeadlines   &$     r    Úacquire_refresh_fencerO   Q   s­  é € ô: # 4Ó(€IðÝ<á 	× 0Ñ 0Ô1Ü˜)Ô$Ü�WŠW�Y¤§	¡	¬B¯J©JÕ 6¸Ó>ˆô �~Š~Ó 'Õ)€HðØð*ÜÒ$Ü—K’K ¤E§M¡M´E·M±MÕ$AÔBð �	ô Ò'Ü—N’N 2¤v§¡¸Ô:ð
 �	ô .ØUóð øô! ô ô "Ø)¨)¯.©.Ð)9¸¸S¸EÐBó
àð	ûðûô( ô *Ø—9‘9Ô$<Ô<ô .ØHÈÈÐNóàðô —>’>Ó# xÔ/Ü-Ø;¸GÀC¸=ÈÈIÏNÉNÐK[Ð[\Ð]óàð ô —m’m DÓ)×)×)Ð)ûð*ûô ô Ü
�Š�ŒØðüs…   ‚G5�AD Á-G5Â
G ÂAD: ÃG5Ã/D: Ã?G5Ä D: ÄD7ÄD2Ä2D7Ä7G5Ä:GÅA5GÆ:F=Æ;GÇ G ÇGÇG Ç#G2Ç2G5c                ó(   € V ^8„  d   QhR\         RR/# )r"   rL   r$   N©r.   )r%   s   "r    r&   r&   ›   s   € ÷ ñ œcð  dñ r   c           	     ó>  €  \         e'   \         P                  ! V \         P                  4       M.\        e'   \        P                  ! V \        P
                  ^4       \        P                  ! \        4      ;_uu_ 4        \        P                  ! V 4       RRR4       R#   \         d     LPi ; i  + '       g   i     R# ; i  \        P                  ! \        4      ;_uu_ 4        \        P                  ! T 4       RRR4       i   + '       g   i     i ; i; i)zRUnlock and close a descriptor returned by ``acquire_refresh_fence``. Never raises.N)r>   r?   ÚLOCK_UNrB   rC   ÚLK_UNLCKr:   Ú
contextlibÚsuppressr6   rJ   )rL   s   &r    Úrelease_refresh_fencerW   ›   s©   € ð	ÜÒÜ�KŠK˜œEŸM™MÕ*ÜÒÜ�NŠN˜2œvŸ™°Ô2ô × Ò ¤×)Õ)Ü�HŠH�RŒL÷ *Ñ)øô ô Ùðú÷ *×)Ð)ûŒZ× Ò ¤×)Õ)Ü�HŠH�RŒL÷ *×)×)Ð)ýsQ   ‚-B! ¯C °.B! Â B2Â!B/Â,C Â.B/Â/C Â2C	Ã#DÃ)D		Ä 	DÄ	DÄDÚmcpú8MCP OAuth types not available -- OAuth MCP auth disabledc                ó0   € V ^8„  d   QhR\         R\        /# ©r"   r;   r$   ©Ústrr	   )r%   s   "r    r&   r&   ¶   s   € ÷ "ñ "”Sð "œSñ "r   c                ó„  € \         '       gG    ^ RIHp ^ RIHp VP                  \         R&   \
        R,           F  p\        W#4      \         V&   K  	  \         P                  V 4      #   \        \        3 dG    \         P                  \        P                  \
        4      4       Rs\        P                  R4        Lki ; i)zmSDK OAuth class *name*, importing the SDK on first call; None when unavailable (a broken SDK is probed once).)Úauthr   :é   NNFrY   )Ú_SDK_CLASSESÚ
mcp.clientr_   Ú
mcp.sharedr   Ú_SDK_CLASS_NAMESÚgetattrÚImportErrorÚAttributeErrorÚupdateÚdictÚfromkeysÚ_OAUTH_AVAILABLEÚloggerÚdebugÚget)r;   Ú_client_authÚ_shared_authÚ_names   &   r    Ú
_sdk_classrr   ¶   s�   € ÷ ‹<ð
	UÝ7Ý7à2>×2RÑ2RŒLÐ.Ñ/Ü)¨"×-Ð-�Ü&-¨lÓ&B”˜UÓ#ñ .ô ×Ñ˜DÓ!Ð!øô	 œ^Ð,ô 	UÜ×Ñ¤§¡Ô.>Ó ?Ô@Ø$ÐÜ�L‰LÐSÖTð	Uús   ŽAA( Á(AB?Â>B?)ÚAnyUrlc                   ó   € ] tR t^ÎtRtRtR# )ÚOAuthNonInteractiveErrorzHRaised when OAuth requires browser interaction in a non-interactive env.r   Nr   r   r   r    ru   ru   Î   s   † ÝRr   ru   Ú_oauth_interactive_enabledT)ÚdefaultÚ_oauth_interactive_forcedFÚ__hermes_user_skipped__c                óT   € V ^8„  d   QhR\         \        ,          R,          R\        /# )r"   Úhermes_homeNr$   ©r]   r   )r%   s   "r    r&   r&   å   s'   € ÷ ^ñ ^¤¤d¥
¨TÕ 1ð ^¼Tñ ^r   c                ó^   € ^ RI Hp \        V e   V 4      R,          # V! 4       4      R,          # )u<   ``HERMES_HOME/mcp-tokens/`` â€” per-profile token directory.)Úget_hermes_homez
mcp-tokens)r4   r~   r   )r{   r~   s   & r    Ú_get_token_dirr   å   s,   € å0ä˜{Ò6�ÓNÐQ]Õ]Ð]¹OÓ<MÓNÐQ]Õ]Ð]r   c                ó0   € V ^8„  d   QhR\         R\         /# r[   ©r]   )r%   s   "r    r&   r&   ì   s   € ÷ Gñ Gœð G¤ñ Gr   c                ór   € \         P                  ! RRV 4      P                  R4      R,          ;'       g    R# )zBSanitize a server name for use as a filename (no path separators).z[^\w\-]Ú_:Né€   Nrw   )ÚreÚsubÚstrip)r;   s   &r    Ú_safe_filenamerˆ   ì   s-   € ä�6Š6�*˜c 4Ó(×.Ñ.¨sÓ3°DÕ9×FÐF¸YÐFr   c                ó>   € V ^8„  d   QhR\         R\         R,          /# )r"   Úportr$   NrQ   )r%   s   "r    r&   r&   ú   s   € ÷ ñ œð ¤ t¥ñ r   c                óZ  € \         P                   ! \         P                  \         P                  4      p VP                  RV 34       TP                  4       ^,          p\        \        4      \        8¼  dm   \        R \         4       R4      pTf   MP\        P                  ! \        4      ;_uu_ 4        \        P                  T4      P                  4        RRR4       K„  T\        T&   T#   \         d    TP                  4        T '       d    R# h i ; i  + '       g   i     KË  ; i)zíBind ``127.0.0.1:port`` (0 = ephemeral) and park it until the waiter adopts it; None if taken.
The cap evicts ephemeral parks only: losing a pinned CIMD port (the only ones the published
document declares) mid-flow would reopen the race.ú	127.0.0.1Nc              3   ó>   "  € T F  q\         9  g   K  Vx € K  	  R # 5i©N)Ú_CIMD_PORTS)Ú.0Úps   & r    Ú	<genexpr>Ú!_bind_reserved.<locals>.<genexpr>  s   é € ÐPÑ&7 ÄKÑ;OŸ1š1Ó&7ùs   ‚“
)ÚsocketÚAF_INETÚSOCK_STREAMÚbindr:   rJ   ÚgetsocknameÚlenÚ_reserved_socketsÚ_MAX_RESERVED_SOCKETSÚnextrU   rV   Úpop)rŠ   ÚsockÚboundÚ
stale_ports   &   r    Ú_bind_reservedr¡   ú   sä   € ô �=Š=œŸ™¬×);Ñ);Ó<€DðØ�	‰	�; Ð%Ô&ð ×ÑÓ˜qÕ!€EÜ
ÔÓ
 Ô$9Ô
9ÜÑPÕ&7ÓPÐRVÓWˆ
ØÒØÜ× Ò ¤×)Õ)Ü×!Ñ! *Ó-×3Ñ3Ô5÷ *Ñ)à#Ô�eÑØ€Løô ô Ø�
‰
ŒßÚØð	ú÷ *×)Ð)ús   µC. Â5$DÃ."DÄDÄD*	c                ó$   € V ^8„  d   QhR\         /# ©r"   r$   rQ   )r%   s   "r    r&   r&     s   € ÷ ñ ¤ñ r   c                 ó   € \        ^ 4      # )zCPick an ephemeral callback port and keep its socket bound (parked).)r¡   r   r   r    Ú_reserve_callback_portr¥     s   € ä˜!ÓÐr   c                ó6   € V ^8„  d   QhRRR\         R,          /# ©r"   ÚstoragezHermesTokenStorage | Noner$   N©ri   )r%   s   "r    r&   r&     s   € ÷ 4ñ 4Ð!<ð 4ÄÈÅñ 4r   c                ó°   €  V e   \        V P                  4       4      MRp\        T\        4      '       d   T# R#   \        \        \        3 d     R# i ; i)z7The on-disk client registration for *storage*, or None.N)Ú
_read_jsonÚ_client_info_pathrg   Ú	TypeErrorÚ
ValueErrorÚ
isinstanceri   )r¨   Úinfos   & r    Ú_cached_client_infor±     sV   € ðØ:AÒ:MŒz˜'×3Ñ3Ó5Ô6ÐSWˆô
 ˜d¤D×)Ò)ˆ4Ð3¨tÐ3øô	 œI¤zÐ2ô Úðús   ‚; »AÁAc                óf   € V ^8„  d   QhRRR\         \        R,          \        R,          3,          /# r§   )Útupler]   r.   )r%   s   "r    r&   r&   !  s0   € ÷ ñ Ð9ð ¼eÄCÈ$ÅJÔPSÐVZÕPZÐDZÕ>[ñ r   c                óX  € R;r\        V 4      p\        V\        4      '       d   VP                  R4      MRp\        V\        \
        34      '       d   TMR Fµ  p \        \        V4      4      pVP                  VP                  r‡Tf/   TP                  R8X  d   TP                  '       d   \        T4      pTP                  R8H  ;'       d    TP                  R8H  ;'       d    TR9   p	Te   Kš  T	'       g   K¤  Tf   Kª  \        T4      pK·  	  W3#   \        \        3 d     KÐ  i ; i)zú``(https proxy URI, loopback callback port)`` from the cached client registration (None when
absent): a DCR ``client_id`` is bound to its registered redirect URI, so a new random port under
it gets ``redirect_uri does not match any registered URIs``.NÚredirect_urisÚhttpsÚhttpú	/callbackr   >   Ú	localhostrŒ   )r±   r¯   ri   rn   Úlistr³   r   r]   ÚhostnamerŠ   r­   r®   ÚschemeÚnetlocr#   r.   )
r¨   ÚurirŠ   r°   ÚurisÚrawÚparsedÚparsed_hostnameÚparsed_portÚis_loopback_callbacks
   &         r    Ú_cached_redirectrÅ   !  s  € ð Ð€CÜ˜wÓ'€DÜ(2°4¼×(>Ò(>ˆ4�8‰8�OÔ$ÀD€DÜ! $¬¬u¨×6Ò6‰t¸BÒ>ˆð	Üœc #›hÓ'ˆFð ,2¯?©?¸F¿K¹K˜[ð Š;˜6Ÿ=™=¨GÔ3¸¿¿¸Ü�c“(ˆCØ%Ÿ}™}°Ñ6÷   Hð   H¸6¿;¹;È+Ñ;U÷   Hð   HÐZið  nHñ  [HÐØŒ<×0Ñ0°[Ô5LÜ�{Ó#ŠDñ ?ð ˆ9Ðøô œ:Ð&ô 	Úð	ús   Á+DÄD)Ä(D)c                ó$   € V ^8„  d   QhR\         /# r£   ©Úbool)r%   s   "r    r&   r&   8  s   € ÷ ñ œ4ñ r   c                 óü  €  \         P                  P                  4       '       g   R#  \
        P                  R8w  d   R#  ^ RIp ^ RIpTP                  ! \         P                  P                  4       4      pT P                  4       p\        T P                  P                  P                  T P!                  T4      T P#                  T4      4      4      #   \        \        3 d     R# i ; i  \$         d     R# i ; i)ag  A human can type on stdin. ``isatty()`` alone is wrong on Windows: the CRT reports True for
a DEVNULL / detached / CREATE_NO_WINDOW stdin (the gateway's), so a background process looked
interactive and launched browser OAuth flows nobody could finish. Confirm with the console API
there: ``GetConsoleMode`` fails on anything that is not a real console handle.FÚntTN)ÚsysÚstdinÚisattyrg   r®   r6   r;   ÚctypesrB   Úget_osfhandleÚfilenoÚc_ulongrÈ   ÚwindllÚkernel32ÚGetConsoleModeÚc_void_pÚbyrefÚ	Exception)rÎ   rB   ÚhandleÚmodes       r    Ú_stdin_is_consolerÚ   8  sÊ   € ð
Ü�y‰y×Ñ×!Ò!Ùð "ô 
‡w�w�$„ÙðÛÛØ×%Ò%¤c§i¡i×&6Ñ&6Ó&8Ó9ˆØ�~‰~ÓˆÜ�F—M‘M×*Ñ*×9Ñ9¸&¿/¹/È&Ó:QÐSY×S_ÑS_Ð`dÓSeÓfÓgÐgøô œJÐ'ô Úðûô ô Úðús$   ‚#C ÁBC, ÃC)Ã(C)Ã,C;Ã:C;c                ó$   € V ^8„  d   QhR\         /# r£   rÇ   )r%   s   "r    r&   r&   N  s   € ÷ ñ œñ r   c                 ó†   € \         P                  4       '       g   R# \        P                  4       '       d   R# \        4       # )z9True if we can reasonably expect to interact with a user.FT)rv   rn   rx   rÚ   r   r   r    Ú_is_interactiverÝ   N  s/   € ä%×)Ñ)×+Ò+ÙÜ ×$Ñ$×&Ò&ÙÜÓÐr   c                ó(   € V ^8„  d   QhR\         RR/# )r"   Úleadr$   Nr�   )r%   s   "r    r&   r&   W  s   € ÷ 

ñ 

¤Cð 

¨Dñ 

r   c                óB   € \        4       '       g   \        V  R24      hR# )aE  Raise ``OAuthNonInteractiveError`` unless interactive; *lead* is the boundary-specific first sentence.

``lead`` is the boundary-specific first sentence; this helper appends the shared, actionable ``hermes
mcp login`` next-step so the guidance wording lives in one place across every non-interactive OAuth
boundary (#57836).
zd Run `hermes mcp login <server>` interactively to (re)authorize, then restart or reload the gateway.N)rÝ   ru   )rß   s   &r    Ú_raise_if_non_interactiverá   W  s*   € ô ×ÒÜ&ØˆfÐxÐyó
ð 	
ñ r   c                 ó"   € \        \        R4      # )aZ  Treat the context as interactive despite no TTY (GUI-driven auth: the user IS present, just not
on stdin). Crosses the MCP event-loop thread like ``suppress_interactive_oauth``.

Opens the browser + localhost callback flow that the TTY heuristic would otherwise refuse. Same
ContextVar propagation story as suppress_interactive_oauth() (#35927).
T)Ú_contextvar_setrx   r   r   r    Úforce_interactive_oauthrä   d  s   € ô Ô4°dÓ;Ð;r   c                 ó"   € \        \        R4      # )uÓ  Disable stdin-based OAuth prompts for the current context; ContextVar-based so a
background-discovery thread's suppression reaches the coroutine on the MCP event-loop thread.

Uses a ContextVar so the suppression propagates from a background-discovery thread onto the coroutine
scheduled (via run_coroutine_threadsafe) on the dedicated MCP event-loop thread â€” where the OAuth
callback actually runs (#35927). A threading.local would not cross that thread boundary.
F)rã   rv   r   r   r    Úsuppress_interactive_oauthræ   n  s   € ô Ô5°uÓ=Ð=r   c                ó$   € V ^8„  d   QhR\         /# r£   rÇ   )r%   s   "r    r&   r&   y  s   € ÷ Pñ Pœ4ñ Pr   c                 óÚ  € \         P                  P                  R4      '       g&   \         P                  P                  R4      '       d   R# \         P                  R8X  g;   \	        \         R4      '       d'   \         P
                  ! 4       P                  R8X  d   R# \        \         P                  P                  R4      ;'       g     \         P                  P                  R	4      4      # )
z,True if opening a browser is likely to work.Ú
SSH_CLIENTÚSSH_TTYFrÊ   ÚunameÚDarwinTÚDISPLAYÚWAYLAND_DISPLAY)r6   Úenvironrn   r;   Úhasattrrë   ÚsysnamerÈ   r   r   r    Ú_can_open_browserrò   y  s…   € ä	‡z�z‡~�~�l×#Ò#¤r§z¡z§~¡~°i×'@Ò'@ÙÜ	‡w�w�$„œ7¤2 w×/Ò/´B·H²H³J×4FÑ4FÈ(Ô4RÙÜ”—
‘
—‘˜yÓ)×NÐN¬R¯Z©Z¯^©^Ð<MÓ-NÓOÐOr   c                ó>   € V ^8„  d   QhR\         R\        R,          /# )r"   r#   r$   N©r   ri   )r%   s   "r    r&   r&   ‚  s   € ÷ ñ ”Tð œd T�kñ r   c                óþ   € V P                  4       '       g   R#  \        P                  ! V P                  RR7      4      #   \        P                  \
        3 d"   p\        P                  RY4        Rp?R# Rp?ii ; i)zCRead a JSON file, returning None if it doesn't exist or is invalid.Nz	utf-8-sig)ÚencodingzFailed to read %s: %s)ÚexistsÚjsonÚloadsÚ	read_textÚJSONDecodeErrorr:   rl   Úwarning)r#   rM   s   & r    r«   r«   ‚  s\   € à�;‰;�=Š=ÙðÜ�zŠz˜$Ÿ.™.°+˜.Ó>Ó?Ð?øÜ× Ñ ¤'Ð*ô Ü�‰Ð.°Ô:Ýûðús   š%A  Á A<ÁA7Á7A<c                ó4   € V ^8„  d   QhR\         R\        RR/# )r"   r#   Údatar$   Nrô   )r%   s   "r    r&   r&   �  s!   € ÷ ;ñ ;”dð ;¤$ð ;¨4ñ ;r   c                óp   € ^ RI Hp V! V P                  4       \        V 4       \	        WR\
        R7       R# )u§   OAuth tokens/client info at 0600 from creation, parent tightened to 0700 (``secure_parent_dir``
refuses ``/``, top-level dirs and the install tree â€” #25821, #93050).r0   r2   )rÙ   rw   N)r4   r1   r5   r   r   r]   )r#   rþ   r1   s   && r    Ú_write_jsonr   �  s'   € õ 9á˜DŸK™KÔ(Ü�dÔÜ�d u´c×:r   c                ó0   € V ^8„  d   QhR\         R\        /# )r"   Úmodelr$   )r	   ri   )r%   s   "r    r&   r&   —  s   € ÷ <ñ <”sð <œtñ <r   c                ó(   € V P                  RRR7      # )z0The on-disk JSON shape of an SDK pydantic model.rø   T)rÙ   Úexclude_none)Ú
model_dump)r  s   &r    Ú_model_jsonr  —  s   € à×Ñ °dÐÓ;Ð;r   c                   óL  a € ] tR tRt o RtRR/V 3R lR lltV 3R lR lt]! ]R	4      t]! ]R
4      t	]! ]R4      t
]! ]R4      tV 3R lR lt]R=V 3R lR ll4       tV 3R lR ltV 3R lR ltV 3R lR ltV 3R lR ltV 3R lR ltV 3R lR ltV 3R lR lt]V 3R lR  l4       tV 3R! lR" ltV 3R# lR$ ltV 3R% lR& ltV 3R' lR( ltV 3R) lR* ltV 3R+ lR, ltR-R./V 3R/ lR0 lltV 3R1 lR2 ltR3R./V 3R4 lR5 lltV 3R6 lR7 lt V 3R8 lR9 lt!V 3R: lR; lt"R<t#V t$R# )>ÚHermesTokenStorageiœ  zµPersist OAuth state as ``HERMES_HOME/mcp-tokens/<server_name>`` + ``.json`` (tokens),
``.client.json`` (client info), ``.meta.json`` (server metadata), ``.cimd-off`` (CIMD refused).r{   Nc                óD   <€ V ^8„  d   QhRS[ RS[ S[,          R,          /# )r"   Úserver_namer{   Nr|   )r%   Ú__classdict__s   "€r    r&   ÚHermesTokenStorage.__annotate__   s$   ø€ ÷ .ñ .¡Cð .¹¹t½ÀdÕ9Jñ .r   c               ón   € \        V4      V n        Ve   \        V4      MR V n        R V n        R V n        R # rŽ   )rˆ   Ú_server_namer   Ú_hermes_homeÚloaded_issuerÚ_bound_issuer)Úselfr
  r{   s   &&$r    Ú__init__ÚHermesTokenStorage.__init__   s5   € Ü*¨;Ó7ˆÔØ1<Ò1HœD Ô-ÈdˆÔð *.ˆÔØ)-ˆÖr   c                ó&   <€ V ^8„  d   QhRS[ RS[/# )r"   r)   r$   r|   )r%   r  s   "€r    r&   r  ©  s   ø€ ÷ Rñ R™Cð R¡Dñ Rr   c                óV   € \        V P                  4      V P                   V 2,          # rŽ   )r   r  r  )r  r)   s   &&r    Ú_pathÚHermesTokenStorage._path©  s(   € Ü˜d×/Ñ/Ó0°d×6GÑ6GÐ5HÈÈÐ3QÕQÐQr   z.jsonz.client.jsonz
.meta.jsonz	.cimd-offc                ó:   <€ V ^8„  d   QhRS[ S[S[S[3,          /# r£   )r³   r   )r%   r  s   "€r    r&   r  ±  s#   ø€ ÷ Pñ P™e¡D©$±Ð$4Õ5ñ Pr   c                ó`   € V P                  4       V P                  4       V P                  4       3# rŽ   )Ú_tokens_pathr¬   Ú
_meta_path©r  s   &r    Ú_state_pathsÚHermesTokenStorage._state_paths±  s(   € Ø× Ñ Ó" D×$:Ñ$:Ó$<¸d¿o¹oÓ>OÐOÐOr   c                ó,   <€ V ^8„  d   QhRS[ RS[RS[/# )r"   r#   Úsdk_nameÚlabel)r   r]   )r%   r  s   "€r    r&   r  µ  s"   ø€ ÷ ñ ™$ð ©#ð ±cñ r   c           	     óŠ  € \        V 4      pVeC   \        V\        4      '       g-   \        P	                  RW \        V4      P                  4       R# Ve   \        V4      MRpVe=   VR8X  d6   \        V\        4      '       d    VP                  R4      '       d	   ^ RI	H
p TpVf   R# Ve	   V! V4        VP                  V4      #   \        \        \        3 df   pTp\        TR4      '       d0   RRP!                  R	 TP#                  R
R7       4       4      ,           p\        P	                  RY T4        Rp?R# Rp?ii ; i)z†Read *path* into SDK model *sdk_name*; None if absent, no SDK, or corrupt.
``fixup(data)`` may rewrite the raw dict before validation.Nz<Corrupt %s at %s -- ignoring: expected a JSON object, got %sr   Údevice_authorization_endpoint)ÚDeviceOAuthMetadataÚerrorszvalidation failed for z, c           
   3   ó€   "  € T F4  pR P                  \        \        VP                  RR4      4      4      x € K6  	  R# 5i)Ú.ÚlocNr   )ÚjoinÚmapr]   rn   )r�   Úes   & r    r’   Ú1HermesTokenStorage._load_model.<locals>.<genexpr>Ï  s3   é € ð >cÙBa¸Q�C—H‘HœS¤ a§e¡e¨E°2Ó&6Ó7×8Ð8ÓBaùs   ‚<>F)Úinclude_inputz Corrupt %s at %s -- ignoring: %s)r«   r¯   ri   rl   rü   Útyper   rr   rn   Útools.mcp_oauth_devicer%  Úmodel_validater®   r­   ÚKeyErrorrð   r*  r&  )	r#   r!  r"  Úfixuprþ   Úclsr%  rM   Údetails	   &&&&     r    Ú_load_modelÚHermesTokenStorage._load_model´  s!  € ô ˜$ÓˆØÒ¤J¨t´T×$:Ò$:ô �N‰NÐYØ ¬¨T«
×(;Ñ(;ô=áØ&*Ò&6Œj˜Ô"¸DˆØŠO ¨OÔ ;Ä
È4ÔQU×@VÒ@VØ—H‘HÐ<×=Ò=ÝBØ%ˆCØŠ;ÙØÒÙ�$ŒKð
	Ø×%Ñ% dÓ+Ð+øÜœI¤xÐ0ô 	ð ˆFÜ�s˜H×%Ò%Ø1°D·I±Iñ >cØBEÇ*Á*Ð[`À*ÔBaó>có 5cõ c�ä�N‰NÐ=¸uÈFÔSÝûð	ús   Â6C ÃEÃAD=Ä=Ec                ó$   <€ V ^8„  d   QhRS[ RR/# ©r"   rþ   r$   Nr©   )r%   r  s   "€r    r&   r  Ô  s   ø€ ÷ Oñ O¡tð O°ñ Or   c           	     ó8  € VP                  RR4      pVe5   \        \        V\        P                  ! 4       ,
          ^ 4      4      VR&   R# VP	                  R4      e©   \
        P                  ! \        \        \        4      ;_uu_ 4        V P                  4       P                  4       P                  \        VR,          4      ,           p\        \        V\        P                  ! 4       ,
          ^ 4      4      VR&   RRR4       R# R#   + '       g   i     R# ; i)aJ  Rewrite ``expires_in`` to seconds remaining from the stored absolute ``expires_at`` (not an
SDK field, so stripped): a relative value reloaded after restart would make ``is_token_valid()``
True for tokens that expired while down. Legacy files without it use the file mtime, clamped
to zero (self-heals on the next ``set_tokens``).Ú
expires_atNÚ
expires_in)r�   r.   Úmaxr<   rn   rU   rV   r:   r­   r®   r  ÚstatÚst_mtime)r  rþ   Úabsolute_expiryÚimplied_expirys   &&  r    Ú_rebase_expires_inÚ%HermesTokenStorage._rebase_expires_inÔ  sÅ   € ð
 Ÿ(™( <°Ó6ˆØÒ&Ü!$¤S¨¼4¿9º9»;Õ)FÈÓ%JÓ!KˆD�ÓØ�X‰X�lÓ#Ò/Ü×$Ò$¤W¬i¼×DÕDØ!%×!2Ñ!2Ó!4×!9Ñ!9Ó!;×!DÑ!DÄsÈ4ÐP\ÕK]ÓG^Õ!^�Ü%(¬¨^¼d¿iºi»kÕ-IÈ1Ó)MÓ%N��\Ñ"÷ EÑDñ 0ßD×DÐDús   Â
A2DÄD	c                ó$   <€ V ^8„  d   QhRS[ RR/# r9  r©   )r%   r  s   "€r    r&   r  á  s   ø€ ÷ &ñ &©ð &°$ñ &r   c                óV   € VP                  R R4      V n        V P                  V4       R# )Úhermes_issuerN)r�   r  rB  )r  rþ   s   &&r    Ú_fixup_loaded_tokensÚ'HermesTokenStorage._fixup_loaded_tokensá  s#   € à!ŸX™X o°tÓ<ˆÔØ×Ñ Ö%r   c                ó   <€ V ^8„  d   QhRR/# )r"   r$   zOAuthToken | Noner   )r%   r  s   "€r    r&   r  æ  s   ø€ ÷ hñ hÐ"5ñ hr   c              ƒ  óp   "  € R V n         V P                  V P                  4       RRV P                  4      # 5i)Nr   Útokens)r  r6  r  rG  r  s   &r    Ú
get_tokensÚHermesTokenStorage.get_tokensæ  s3   é € Ø!ˆÔØ×Ñ × 1Ñ 1Ó 3°\À8ÈT×MfÑMfÓgÐgùs   ‚46c                ó"   <€ V ^8„  d   QhRRRR/# )r"   rK  r   r$   Nr   )r%   r  s   "€r    r&   r  ê  s   ø€ ÷ 
Eñ 
E |ð 
E¸ñ 
Er   c              ƒ  ó  "  € \        V4      pVP                  R 4      e`   \        P                  ! \        \
        4      ;_uu_ 4        \        P                  ! 4       \        VR ,          4      ,           VR&   RRR4       V P                  '       d!   V P                  VR&   V P                  V n	        \        V P                  4       V4       \        P                  RV P                  4       R#   + '       g   i     L~; i5i)r<  Nr;  rF  zOAuth tokens saved for %s)r  rn   rU   rV   r­   r®   r<   r.   r  r  r   r  rl   rm   r  )r  rK  Úpayloads   && r    Ú
set_tokensÚHermesTokenStorage.set_tokensê  s¯   é € Ü˜fÓ%ˆà�;‰;�|Ó$Ò0Ü×$Ò$¤Y´
×;Õ;Ü(,¯	ª	«´c¸'À,Õ:OÓ6PÕ(P�˜Ñ%÷ <à××ÐØ'+×'9Ñ'9ˆG�OÑ$Ø!%×!3Ñ!3ˆDÔÜ�D×%Ñ%Ó'¨Ô1Ü�‰Ð0°$×2CÑ2CÖD÷ <×;üs%   ‚ADÁ0C.Á8DÂADÃ.C>	Ã9Dc                ó2   <€ V ^8„  d   QhRS[ R,          RR/# )r"   ÚissuerNr$   r�   )r%   r  s   "€r    r&   r  ö  s   ø€ ÷ =ñ =¡#¨¥*ð =°ñ =r   c                óH   € V'       d   \        V4      V n        R# RV n        R# )zCSet the authorization-server issuer stamped on future token writes.N)r]   r  )r  rT  s   &&r    Úbind_issuerÚHermesTokenStorage.bind_issuerö  s   € ç,2œS ›[ˆÖ¸ˆÖr   c                ó$   <€ V ^8„  d   QhRS[ RR/# )r"   rT  r$   Nr�   )r%   r  s   "€r    r&   r  ú  s   ø€ ÷ )ñ )¡3ð )¨4ñ )r   c                óX  € \        V P                  4       4      pVe   VP                  R4      '       d   R# \        V4      VR&    \	        V P                  4       V4       \        T4      T n	        R#   \
         d-   p\        P                  RT P                  T4        Rp?R# Rp?ii ; i)z¤Backfill ``hermes_issuer`` onto a pre-binding token file: adopt the currently discovered
issuer once instead of forcing a re-login, so the *next* read is protected.NrF  z+Could not stamp issuer on tokens for %s: %s)
r«   r  rn   r]   r   r:   rl   rm   r  r  )r  rT  rþ   rM   s   &&  r    Ústamp_issuerÚHermesTokenStorage.stamp_issuerú  sŽ   € ô ˜$×+Ñ+Ó-Ó.ˆØŠ<˜4Ÿ8™8 O×4Ò4ÙÜ # F£ˆˆ_Ñð	Ü˜×)Ñ)Ó+¨TÔ2ô ! ›[ˆÖøô ô 	Ü�L‰LÐFÈ×HYÑHYÐ[^Ô_Ýûð	ús   ÁA2 Á2B)Á=!B$Â$B)c                ó   <€ V ^8„  d   QhRR/# ©r"   r$   Nr   )r%   r  s   "€r    r&   r    s   ø€ ÷ Iñ I Tñ Ir   c                ó²  € \        V P                  4       4      pVe   VP                  R4      '       g   R# VP                  RR4       VP                  RR4       RV n         \        V P                  4       V4       \        P                  RT P                  4       R#   \         d-   p\        P                  RT P                  T4        Rp?R# Rp?ii ; i)z×Drop the refresh token (and its issuer record) from disk, keeping the access token: the
unexpired access token may still be used, but a refresh token must never go to a different
issuer than the one that granted it.NÚrefresh_tokenrF  z(Could not strip refresh token for %s: %szpRemoved issuer-mismatched refresh token for %s (re-authorization will be required when the access token expires))r«   r  rn   r�   r  r   r:   rl   rü   r  r°   )r  rþ   rM   s   &  r    Ústrip_refresh_tokenÚ&HermesTokenStorage.strip_refresh_token  s³   € ô ˜$×+Ñ+Ó-Ó.ˆØŠ<˜tŸx™x¨×8Ò8ÙØ�‰� $Ô'Ø�‰� $Ô'Ø!ˆÔð	Ü˜×)Ñ)Ó+¨TÔ2ô 	�‰ð 5Ø6:×6GÑ6Gö	Iøô ô 	Ü�N‰NÐEÀt×GXÑGXÐZ]Ô^Ýûð	ús   Á#B ÂCÂ*!CÃCc                ó&   <€ V ^8„  d   QhRS[ RS[/# )r"   rþ   r$   ©ri   rÈ   )r%   r  s   "€r    r&   r    s   ø€ ÷ ñ ©ð ±$ñ r   c                ón   € V P                  R4      '       d   V P                  R4      R9   d   RV R&   R# R# )zÓSet ``client_secret_post`` when a secret is present but no method is: some DCR providers
(Supabase) omit ``token_endpoint_auth_method``, the SDK defaults it to ``none`` and the
exchange fails without the secret.Úclient_secretÚtoken_endpoint_auth_methodÚclient_secret_postTF)NÚnoneÚ ©rn   )rþ   s   &r    Ú_coerce_secret_auth_methodÚ-HermesTokenStorage._coerce_secret_auth_method  s6   € ð
 �8‰8�O×$Ò$¨¯©Ð2NÓ)OÐSeÔ)eØ1EˆDÐ-Ñ.ÙÙr   c                ó   <€ V ^8„  d   QhRR/# )r"   r$   z!OAuthClientInformationFull | Noner   )r%   r  s   "€r    r&   r  $  s   ø€ ÷ ñ Ð'Jñ r   c              ƒ  óÐ   a a"  € . oS P                  S P                  4       R RVV 3R l4      pVe3   S^ ,          '       d$   \        S P                  4       \        V4      4       V# 5i)r   zclient infoc                 óD   <€ SP                  SP                  V 4      4      # rŽ   )Úappendrk  )rþ   Úcoercedr  s   &€€r    Ú<lambda>Ú4HermesTokenStorage.get_client_info.<locals>.<lambda>(  s   ø€ ˜Ÿ™¨×(GÑ(GÈÓ(MÔNr   )r6  r¬   r   r  )r  r°   rq  s   f @r    Úget_client_infoÚ"HermesTokenStorage.get_client_info$  s]   ùé € Ø ˆØ×ÑØ×"Ñ"Ó$Ð&BÀMÝNóPˆð Ò ¨§
¤
Ü˜×.Ñ.Ó0´+¸dÓ2CÔDØˆùs   „<A&Á%A&c                ó"   <€ V ^8„  d   QhRRRR/# )r"   Úclient_infor   r$   Nr   )r%   r  s   "€r    r&   r  -  s!   ø€ ÷ Jñ JÐ1Mð JÐRVñ Jr   c              ƒ  óº   "  € \        V4      pV P                  V4       \        V P                  4       V4       \        P                  R V P                  4       R# 5i)zOAuth client info saved for %sN)r  rk  r   r¬   rl   rm   r  )r  rw  rþ   s   && r    Úset_client_infoÚ"HermesTokenStorage.set_client_info-  sE   é € Ü˜;Ó'ˆØ×'Ñ'¨Ô-Ü�D×*Ñ*Ó,¨dÔ3Ü�‰Ð5°t×7HÑ7HÖIùs   ‚AAc                ó"   <€ V ^8„  d   QhRRRR/# )r"   Úmetadatar   r$   Nr   )r%   r  s   "€r    r&   r  3  s   ø€ ÷ Gñ G¨Oð GÀñ Gr   c                óŒ   € \        V P                  4       \        V4      4       \        P	                  RV P
                  4       R# )z›Persist server metadata so a restarted process can refresh without re-discovery;
otherwise the SDK guesses ``{server_url}/token`` (404) and forces re-auth.zOAuth metadata saved for %sN)r   r  r  rl   rm   r  )r  r|  s   &&r    Úsave_oauth_metadataÚ&HermesTokenStorage.save_oauth_metadata3  s/   € ô 	�D—O‘OÓ%¤{°8Ó'<Ô=Ü�‰Ð2°D×4EÑ4EÖFr   c                ó   <€ V ^8„  d   QhRR/# )r"   r$   zOAuthMetadata | Noner   )r%   r  s   "€r    r&   r  9  s   ø€ ÷ Vñ VÐ%;ñ Vr   c                óD   € V P                  V P                  4       R R4      # )r   zOAuth metadata)r6  r  r  s   &r    Úload_oauth_metadataÚ&HermesTokenStorage.load_oauth_metadata9  s   € Ø×Ñ §¡Ó 1°?ÐDTÓUÐUr   c                ó   <€ V ^8„  d   QhRR/# r]  r   )r%   r  s   "€r    r&   r  <  s   ø€ ÷ 
Qñ 
Q Dñ 
Qr   c                óÖ   € V P                  4       p ^ RIHp V! VP                  4       VP	                  4        R#   \
         d"   p\        P                  RY4        Rp?R# Rp?ii ; i)z«Durably record that this server refused our CIMD document so a restart does not re-present
the refused client_id. Cleared by ``remove()`` so a fixed document gets a retry.r0   z)Could not record CIMD rejection at %s: %sN)Ú_cimd_rejected_pathr4   r1   r5   Útouchr:   rl   rm   )r  r#   r1   rM   s   &   r    Úmark_cimd_rejectedÚ%HermesTokenStorage.mark_cimd_rejected<  sT   € ð ×'Ñ'Ó)ˆð	QÝ@á# D§K¡KÔ0Ø�J‰JŽLøÜô 	QÜ�L‰LÐDÀd×PÒPûð	Qús   ’(< ¼A(ÁA#Á#A(c                ó    <€ V ^8„  d   QhRS[ /# r£   rÇ   )r%   r  s   "€r    r&   r  H  s   ø€ ÷ 3ñ 3™tñ 3r   c                ó>   € V P                  4       P                  4       # )z?True when this server has refused our metadata document before.)r†  r÷   r  s   &r    Úcimd_rejectedÚ HermesTokenStorage.cimd_rejectedH  s   € à×'Ñ'Ó)×0Ñ0Ó2Ð2r   Úkeep_metadataFc                ó$   <€ V ^8„  d   QhRS[ RR/# )r"   rŽ  r$   NrÇ   )r%   r  s   "€r    r&   r  L  s   ø€ ÷ &ñ &¡tð &¸ñ &r   c               óÌ   € V P                  4       V P                  4       V P                  4       .V'       d   RMV P                  4       3O F  pVP	                  RR7       K  	  R# )zóDelete all stored OAuth state for this server; ``keep_metadata`` spares ``.meta.json`` so a
re-login can still announce the discovered ``authorization_endpoint`` when the authorization
server's metadata document cannot be re-fetched (#115329).T©Ú
missing_okNr   )r  r¬   r†  r  Úunlink)r  rŽ  r‘   s   &$ r    ÚremoveÚHermesTokenStorage.removeL  sY   € ð ×#Ñ#Ó% t×'=Ñ'=Ó'?À×AYÑAYÓA[ð Dß'‘B¨d¯o©oÓ.?Ð-AóDˆAà�H‰H ˆHÖ%óDr   c                ó6   <€ V ^8„  d   QhRS[ S[S[3,          /# r£   )ri   r]   Úbytes)r%   r  s   "€r    r&   r  V  s   ø€ ÷ ñ ™$™s¡E˜zÕ*ñ r   c                óî   € / pV P                  4        FJ  p\        P                  ! \        4      ;_uu_ 4        VP	                  4       WP
                  &   RRR4       KL  	  V#   + '       g   i     Ka  ; i)zˆfilename -> bytes of the existing state files; ``restore()`` it to undo a ``remove()`` after
a failed re-auth so a valid token survives.N)r  rU   rV   r:   Ú
read_bytesr;   )r  Úsnapr‘   s   &  r    ÚsnapshotÚHermesTokenStorage.snapshotV  sY   € ð "$ˆØ×"Ñ"Ö$ˆAÜ×$Ò$¤W×-Õ-Ø Ÿ|™|›~�—V‘V‘÷ .Ñ-ñ %ð ˆ÷ .×-Ð-ús   ¸A#Á#A4Úonly_if_absentc                ó@   <€ V ^8„  d   QhRS[ S[S[3,          RS[RR/# )r"   r›  r�  r$   N)ri   r]   r—  rÈ   )r%   r  s   "€r    r&   r  _  s1   ø€ ÷ Sñ S¡¡S©% ZÕ 0ð SÁTð SÐVZñ Sr   c               ó²  € V'       dy   \         ;QJ d*    R V P                  4        4       F  '       g   K   RM	  RM! R V P                  4        4       4      '       d#   \        P                  RV P                  4       R# V P                  4        V'       g   R# \        V P                  4      p^ RIH	p V! V4       VP                  4        FÁ  w  rV \        P                  ! \        W5,          4      \        P                  \        P                  ,          \        P                   ,          \"        P$                  \"        P&                  ,          4      p\        P(                  ! VR4      ;_uu_ 4       pVP+                  V4       RRR4       KÃ  	  R#   + '       g   i     KØ  ; i  \,         d#   p	\        P/                  RYY4        Rp	?	EK  Rp	?	ii ; i)	zGRevert to a snapshot without overwriting a concurrent successful write.c              3   ó@   "  € T F  qP                  4       x € K  	  R # 5irŽ   )r÷   )r�   r#   s   & r    r’   Ú-HermesTokenStorage.restore.<locals>.<genexpr>a  s   é € Ð!PÑ<O°D§+¡+§- -Ó<Oùs   ‚TFz9Skipping OAuth rollback for %s because newer state existsNr0   Úwbz$Failed to restore OAuth state %s: %s)Úanyr  rl   r°   r  r”  r   r  r4   r1   Úitemsr6   r7   r]   ÚO_WRONLYr9   ÚO_TRUNCr>  ÚS_IRUSRÚS_IWUSRÚfdopenÚwriter:   rü   )
r  r›  r�  Ú	token_dirr1   Úfnamerþ   rL   ÚfhrM   s
   &&$       r    ÚrestoreÚHermesTokenStorage.restore_  s.  € çŸc›cÑ!P¸D×<MÑ<MÔ<OÓ!PŸcŸcšcÑ!P¸D×<MÑ<MÔ<OÓ!P×PÒPÜ�K‰KÐSÐUY×UfÑUfÔgÙØ�‰ŒßÙÜ" 4×#4Ñ#4Ó5ˆ	Ý<á 	Ô*Ø#Ÿ>™>Ö+‰KˆEðSÜ—W’WœS Õ!2Ó3´R·[±[Ä2Ç:Á:Õ5MÔPR×PZÑPZÕ5ZÔ\`×\hÑ\hÔko×kwÑkwÕ\wÓx�Ü—Y’Y˜r 4×(Ô(¨BØ—H‘H˜T”N÷ )Ñ(ó ,÷ )×(Ð(ûäô SÜ—‘ÐEÀu×RÓRûðSús7   ÃB!F)Å5FÆF)ÆF&Æ F)Æ&F)Æ)GÆ4GÇGc                ó    <€ V ^8„  d   QhRS[ /# r£   rÇ   )r%   r  s   "€r    r&   r  s  s   ø€ ÷ ñ ©Dñ r   c                óð  € V P                  4       pVP                  4       '       g   R# VP                  VP                  R,           4      p VP	                  VP                  4       4       VP                  RR7       V P                  4       P                  RR7       \        P                  RV P                  VP                  4       R#   \         d!   p\        P                  RY4        Rp?L…Rp?ii ; i)a  Discard a dead DCR client (``invalid_client`` at the token endpoint) plus stale ``meta.json``
so the SDK re-registers next flow; tokens are kept (a valid refresh token survives if
re-registration never completes). Keeps one ``.bak``. True if a client file was removed.Fz.bakz'Could not back up client info at %s: %sNTr‘  zŽMCP OAuth '%s': cached client registration rejected as invalid_client; removed client.json + meta.json (backup at %s) to force re-registration)r¬   r÷   Ú	with_namer;   Úwrite_bytesr™  r:   rl   rü   r“  r  r  )r  Úclient_pathÚbackuprM   s   &   r    Úpoison_client_registrationÚ-HermesTokenStorage.poison_client_registrations  sÓ   € ð ×,Ñ,Ó.ˆØ×!Ñ!×#Ò#ÙØ×&Ñ& {×'7Ñ'7¸&Õ'@ÓAˆð	XØ×Ñ˜{×5Ñ5Ó7Ô8ð 	×Ñ dÐÔ+Ø�‰Ó× Ñ ¨DÐ Ô1Ü�‰ðVà×Ñ˜vŸ{™{ô	,ñ øô ô 	XÜ�N‰NÐDÀk×WÑWûð	Xús   ÁC
 Ã
C5ÃC0Ã0C5c                ó    <€ V ^8„  d   QhRS[ /# r£   rÇ   )r%   r  s   "€r    r&   r  ‡  s   ø€ ÷ ,ñ ,¡4ñ ,r   c                ó>   € V P                  4       P                  4       # )z0True if we have tokens on disk (may be expired).)r  r÷   r  s   &r    Úhas_cached_tokensÚ$HermesTokenStorage.has_cached_tokens‡  s   € à× Ñ Ó"×)Ñ)Ó+Ð+r   c                ó    <€ V ^8„  d   QhRS[ /# r£   )r³   )r%   r  s   "€r    r&   r  ‹  s   ø€ ÷ 
ñ 
¡%ñ 
r   c                ó6  € . pV P                  4       V P                  4       3 FF  p VP                  4       pVP                  VP                  VP
                  VP                  34       KH  	  \        V4      #   \         d    TP                  R4        Kt  i ; i)z\``stat()`` of the token and client files: what any re-authorization rewrites. No bytes read.N)	r  r¬   r>  rp  Úst_mtime_nsÚst_sizeÚst_inor:   r³   )r  Ústampr#   Ústs   &   r    Úcredential_stampÚ#HermesTokenStorage.credential_stamp‹  s„   € àˆØ×&Ñ&Ó(¨$×*@Ñ*@Ó*BÓCˆDðFØ—Y‘Y“[�ð —‘˜bŸn™n¨b¯j©j¸"¿)¹)ÐDÖEñ Dô �U‹|Ðøô	 ô #Ø—‘˜T×"ð#ús   §A8Á8BÂB)r  r  r  r  rŽ   )%r   r   r   r   r   r  r  r   r  r¬   r  r†  r  Ústaticmethodr6  rB  rG  rL  rQ  rV  rZ  r`  rk  rt  ry  r~  r‚  rˆ  rŒ  r”  r›  r®  r¶  rº  rÃ  r   Ú__classdictcell__)r  s   @r    r  r  œ  st  ø‡ € ñgð.ÈT÷ .ò .÷Rð Rñ ! ¨Ó0€LÙ% e¨^Ó<ÐÙ˜u lÓ3€JÙ'¨¨{Ó;Ð÷Pð Pð ÷ñ ó ð÷>Oð O÷&ð &÷
hð h÷
Eð 
E÷=ð =÷)ð )÷Ið Ið$ ÷ó ð÷ð ÷Jð J÷Gð G÷Vð V÷
Qð 
Q÷3ð 3ð&¨e÷ &ò &÷ð ðSÈE÷ Sò S÷(ð ÷(,ð ,÷
ö 
r   r  c                óX   € V ^8„  d   QhR\         R\         R,          R\         R,          /# )r"   ÚcodeÚstateNÚissr�   )r%   s   "r    r&   r&   ™  s.   € ÷ Dñ D¤Sð D´°tµð DÄ#ÈÅ*ñ Dr   c                óL   €  ^ RI Hp T! YTR7      #   \         d    Y3u # i ; i)zÆRedirect parameters in the shape the installed SDK expects: mcp 2.0's ``callback_handler``
returns an ``AuthorizationCodeResult`` (the SDK reads ``.state``/``.iss`` off it); older SDKs take a tuple.)ÚAuthorizationCodeResult)rÈ  rÉ  rÊ  )Úmcp.shared.authrÌ  rf   )rÈ  rÉ  rÊ  rÌ  s   &&& r    Ú_authorization_code_resultrÎ  ™  s2   € ðÝ;ñ #¨¸sÔCÐCøô ô Øˆ{Òðús   ‚ ’#¢#c                óR   € V ^8„  d   QhR\         R\        \         \        3,          /# )r"   Úqueryr$   )r]   ri   r	   )r%   s   "r    r&   r&   £  s&   € ÷ Tñ T¤ð T¬¬c´3¨h­ñ Tr   c                óv   € \        V 4      pR Uu/ uF  q"VP                  VR.4      ^ ,          bK  	  up# u upi )zËcode/state/error/iss from a redirect query string. ``iss`` (RFC 9207 issuer) is kept: mcp 2.0
rejects a response omitting it when the server advertised ``authorization_response_iss_parameter_supported``.N)rÈ  rÉ  ÚerrorrÊ  )r
   rn   )rÐ  ÚparamsÚks   &  r    Ú_parse_redirect_queryrÕ  £  s<   € ô �e‹_€FÙ1RÓSÑ1R¨Aˆv�z‰z˜!˜d˜VÓ$ QÕ'Ò'Ñ1RÑSÐSùÒSs   �#6c                ó0   € V ^8„  d   QhR\         R\        /# )r"   Úresultr$   rc  )r%   s   "r    r&   r&   ª  s   € ÷ Rñ Rœ$ð R¤4ñ Rr   c                 ó^   € V P                  R 4      RJ;'       g    V P                  R4      RJ# )Ú	auth_codeNrÒ  rj  )r×  s   &r    Ú_result_takenrÚ  ª  s,   € Ø�:‰:�kÓ"¨$Ð.×QÐQ°&·*±*¸WÓ2EÈTÐ2QÐQr   c                óF   € V ^8„  d   QhR\         \        \        3,          /# r£   )r³   r/  ri   )r%   s   "r    r&   r&   ®  s   € ÷ ñ ¤¤d¬D jÕ 1ñ r   c                 óD   a€ RRRRRRRR/o ! V3R lR\         4      p V S3# )z]Fresh ``(HandlerClass, result_dict)`` per flow so concurrent flows don't stomp on each other.rÙ  NrÉ  rÒ  rÊ  c                   ób   <a € ] tR tRt o V 3R lV3R lltR
V 3R lR lltV 3R lR ltRtV tR	# )Ú(_make_callback_handler.<locals>._Handleri²  c                ó   <€ V ^8„  d   QhRR/# r]  r   )r%   r  s   "€r    r&   Ú5_make_callback_handler.<locals>._Handler.__annotate__³  s   ø€ ÷ 	Kñ 	K˜Dñ 	Kr   c                ób  <€ \        \        V P                  4      P                  4      p^ÈpVR,          '       g   VR,          '       g   RRr2M�\	        S4      '       d   RpMmSP                  VR,          VR,          VR,          VR,          R7       VR,          '       d   R	M*R
\        P                  ! VR,          ;'       g    R4       R2pV P                  V4       V P                  RR4       V P                  4        V P                  P                  RV R2P                  4       4       R# )éÈ   rÈ  rÒ  i”  z<h2>Not Found</h2>zZ<h2>Authorization already received</h2><p>You can close this tab and return to Hermes.</p>rÉ  rÊ  ©rÙ  rÉ  rÒ  rÊ  zT<h2>Authorization Successful</h2><p>You can close this tab and return to Hermes.</p>z'<h2>Authorization Failed</h2><p>Error: Úunknownz</p>zContent-Typeztext/html; charset=utf-8z<html><body>z</body></html>N)rÕ  r   r#   rÐ  rÚ  rh   ÚhtmlÚescapeÚsend_responseÚsend_headerÚend_headersÚwfilerª  Úencode)r  rÁ   ÚstatusÚbodyr×  s   &   €r    Údo_GETÚ/_make_callback_handler.<locals>._Handler.do_GET³  s  ø€ Ü*¬8°D·I±IÓ+>×+DÑ+DÓEˆFØˆFØ˜&—>”>¨&°¯/¬/ð  #Ð$8™Ü˜v×&Ò&às‘à—‘¨¨v­¸fÀW½oÐU[Ð\cÕUdÐjpÐqvÕjw�ÔxØrxÐy÷  sAô  sAÑnØFÄtÇ{Â{ÐSYÐZaÕSb×SoÐSoÐfoÓGpÐFqÐquÐvð à×Ñ˜vÔ&Ø×Ñ˜^Ð-GÔHØ×ÑÔØ�J‰J×Ñ˜|¨D¨6°Ð@×GÑGÓIÖJr   c                ó*   <€ V ^8„  d   QhRS[ RS[ RR/# )r"   rÈ  Úsizer$   Nr�   )r%   r  s   "€r    r&   rà  Æ  s'   ø€ ÷ 	Zñ 	Z¡Cð 	Z±Sð 	ZÀ4ñ 	Zr   c                ó‚   € \         P                  R V P                  \        V P                  4      P                  4       R# )zOAuth callback: %s %sN)rl   rm   Úcommandr   r#   )r  rÈ  rñ  s   &&&r    Úlog_requestÚ4_make_callback_handler.<locals>._Handler.log_requestÆ  s&   € Ü�L‰LÐ0°$·,±,ÄÈÏÉÓ@S×@XÑ@XÖYr   c                ó*   <€ V ^8„  d   QhRS[ RS[RR/# )r"   ÚfmtÚargsr$   Nr\   )r%   r  s   "€r    r&   rà  É  s"   ø€ ÷ 	;ñ 	;¡3ð 	;©sð 	;°tñ 	;r   c                ó>   € \         P                  R W,          4       R# )zOAuth callback: %sN)rl   rm   )r  r÷  rø  s   &&*r    Úlog_messageÚ4_make_callback_handler.<locals>._Handler.log_messageÉ  s   € Ü�L‰LÐ-¨s­zÖ:r   r   N)Ú-rü  )	r   r   r   r   rî  rô  rú  r   rÆ  )r  r×  s   @€r    Ú_HandlerrÞ  ²  s'   ù‡ € ÷	Kó 	K÷&	Zò 	Z÷	;ö 	;r   rý  )r   )rý  r×  s    @r    Ú_make_callback_handlerrþ  ®  s4   ø€ à)¨4°¸$ÀÈÈuÐVZÐ[€F÷;Ô)ô ;ð4 �VÐÐr   c                ó(   € V ^8„  d   QhR\         RR/# )r"   r×  r$   Nr©   )r%   s   "r    r&   r&   Ï  s   € ÷  [ñ  [¤4ð  [¨Dñ  [r   c                óœ  €  \         P                  P                  4       pT;'       g    RP                  4       pT'       d   \        T 4      '       d   R# TP                  4       \        9   d'   \        T R&   \        R\         P                  R7       R# RT9   d   TP                  R^4      ^,          MTp \        TP                  R4      4      pTR,          '       g-   TR,          '       g   \        R	\         P                  R7       R# \        T 4      '       d   R# T P#                  TR,          TR
,          TR,          TR,          R7       TR,          '       d   \        R\         P                  R7       R# R#   \        \        \
        3 d     R# i ; i  \
        \         3 d     \        R\         P                  R7        R# i ; i)uû   Read one stdin line as an OAuth redirect (full URL, bare query, or a ``_SKIP_TOKENS`` word that
exits without auth) into *result*. Parse failures, EOF and interrupts are swallowed â€” best-effort
fallback racing the HTTP listener, which stays primary.Nri  rÒ  z˜  OAuth skipped. Run `hermes mcp login <server>` later to authenticate, or set ``enabled: false`` on that server in config.yaml to disable persistently.©ÚfileÚ?uA     Could not parse pasted input as an OAuth redirect â€” ignoring.rÈ  uD     Pasted input did not contain ``code=`` or ``error=`` â€” ignoring.rÉ  rÊ  rã  u8     Got authorization code from paste â€” completing flow.)rË   rÌ   ÚreadlineÚKeyboardInterruptr:   r®   r‡   rÚ  ÚlowerÚ_SKIP_TOKENSÚ_USER_SKIPPED_SENTINELÚprintÚstderrÚsplitrÕ  Úremoveprefixr­   rh   )r×  ÚlinerÐ  rÁ   s   &   r    Ú_paste_callback_readerr  Ï  sg  € ðÜ�y‰y×!Ñ!Ó#ˆð �JˆJ�B×ÑÓ€Dß”= ×(Ò(ÙØ‡z�zƒ|”|Ô#Ü0ˆˆw‰Üð_ä—‘õ	ñ 	à%(¨D¤[ˆD�J‰J�s˜AÓ˜qÖ!°d€EðÜ& u×'9Ñ'9¸#Ó'>Ó?ˆð �&�>Œ> &¨§/¤/ÜÐTÔ[^×[eÑ[eÕfÙÜ�V×ÒÙØ
‡M�M˜F 6�N°&¸µ/ÈÐPWÍÐ^dÐejÕ^k€MÔlØˆf‡~„~ÜÐHÌsÏzÉz×Zñ øô3 œw¬
Ð3ô Úðûô  œ	Ð"ô ÜÐQÔX[×XbÑXbÕcÚðús#   ‚E> Â7F Å>FÆFÆ,GÇ
Gu±     Remote session detected. After you authorize, the provider redirects to
    {redirect_uri}
  which forwards to the callback listener on this machine â€” no SSH tunnel needed.
u     Remote session detected. After you authorize, the provider redirects to
    http://{host}:{port}/callback
  which only the listener on THIS machine can receive. Two options:

    1. Easiest â€” when your browser shows a connection error after
       authorizing, copy the full URL from the address bar and paste
       it at the prompt below. The pasted ``code=...&state=...`` is
       enough to complete the flow.

    2. Or forward the port first in a separate terminal:
         ssh -N -L {port}:127.0.0.1:{port} <user>@<this-host>
       then open the URL above and let it redirect normally.

  See: https://hermes-agent.nousresearch.com/docs/guides/oauth-over-ssh
c          
      óh   € V ^8„  d   QhR\         R\        R\         R,          R\         R,          RR/# )r"   Úauthorization_urlrŠ   Úredirect_uriNÚredirect_hostr$   )r]   r.   )r%   s   "r    r&   r&   	  s>   € ÷ ,ñ ,Üð,Ü"%ð,Ü58¸4µZð,ÜPSÐVZÕPZð,à	ñ,r   c                óÐ  € \        RV  R2\        P                  R7       \        P                  ! R4      '       g   \        P                  ! R4      '       d{   V'       d1   \        \
        P                  VR7      \        P                  R7       MBV'       d;   \        \        P                  T;'       g    RVR7      \        P                  R7       \        4       '       g   R	pMOR
p\        P                  ! \        4      ;_uu_ 4        \        P                  ! V 4      pRRR4       V'       d   RMRp\        RV R2\        P                  R7       R#   + '       g   i     L=; i)zKPrint the URL (always, as the fallback) and open the browser when possible.zL
  MCP OAuth: authorization required.
  Open this URL in your browser:

    Ú
r  ré   rê   )r  rŒ   )ÚhostrŠ   u8   Headless environment detected â€” open the URL manually.FNzBrowser opened automatically.u8   Could not open browser â€” please open the URL manually.z  (z)
)r	  rË   r
  r6   ÚgetenvÚ_SSH_HINT_PROXYr%   Ú_SSH_HINT_LOOPBACKrò   rU   rV   r×   Ú
webbrowserr7   )r  rŠ   r  r  ÚnoteÚopeneds   &&&&  r    Ú_announce_authorization_urlr  	  sù   € ô 
Ð\Ð]nÐ\oÐoqÐ
rÔy|÷  zDñ  zDõ  EÜ	‡y‚y�×Ò¤"§)¢)¨I×"6Ò"6ßÜ”/×(Ñ(°lÐ(ÓCÌ#Ï*É*ÖUßÜÔ$×+Ñ+°×1MÐ1MÀ+ÐTXÐ+ÓYÔ`c×`jÑ`jÕkÜ×ÒØI‰àˆÜ× Ò ¤×+Õ+Ü—_’_Ð%6Ó7ˆF÷ ,ç28Ñ.Ð>xˆÜ	ˆC�ˆv�Sˆ/¤§
¡
×+÷ ,×+ús   Ä	EÅE%	c                óX   € V ^8„  d   QhR\         R\        R,          R\        R,          /# )r"   rŠ   r  Nr  )r.   r]   )r%   s   "r    r&   r&     s,   € ÷ ñ ¤ð ´C¸$µJð ÔVYÐ\`ÕV`ñ r   c                ó"   a aa€ R V VV3R llpV# )aò  Redirect handler closing over this flow's port (a closure, not ``_oauth_port``, keeps concurrent
flows isolated). ``redirect_uri`` is a configured proxy callback (None for loopback) and only tailors the
hint; ``redirect_host`` is the loopback hostname the provider will actually redirect to (see
:func:`_resolve_redirect_uri`).

Using a closure instead of reading the module-level ``_oauth_port`` avoids cross-server state pollution
when multiple MCP servers run OAuth concurrently (fixes #44588).
c                ó(   € V ^8„  d   QhR\         RR/# )r"   r  r$   Nr�   )r%   s   "r    r&   Ú,_make_redirect_handler.<locals>.__annotate__&  s   € ÷ Zñ Z´3ð Z¸4ñ Zr   c              “   ó˜   <"  € \        4       pVe   VP                  V 4      G R j  x€L
  R # \        R4       \        V SSS4       R #  L!5i)NzvMCP OAuth requires browser authorization but no interactive session is available (non-interactive/background context).)r   Úpublish_authorization_urlrá   r  )r  Údashboard_flowrŠ   r  r  s   & €€€r    Ú_redirect_handlerÚ1_make_redirect_handler.<locals>._redirect_handler&  sS   øé € Ü1Ó3ˆØÒ%Ø ×:Ñ:Ð;LÓM×MÐMÙô 	"ð Eô	
ô 	$Ð$5°t¸\È=ÖYñ Nùs   ƒ#A
¦A§"A
r   )rŠ   r  r  r$  s   fff r    Ú_make_redirect_handlerr&    s   ú€ ÷Zò Zð$ Ðr   c                ó<   € V ^8„  d   QhR\         R\        R\        /# )r"   rŠ   Úhandler_clsr$   )r.   r/  r   )r%   s   "r    r&   r&   ;  s!   € ÷ ñ ¤ð ´4ð ¼Jñ r   c                ód  €  \        RV 3VRR7      p\        P                  V R4      pVe8   VP                  P	                  4        W3P                  4       uVn        Vn        MRVn        VP                  4        VP                  4        V#   \         d   p\        RT  RT R24      ThRp?ii ; i)	u   Bind the callback listener on *port*, adopting a parked reserved socket (closes the selectâ†’bind
TOCTOU window). ``allow_reuse_address`` is set BEFORE binding (a no-op afterwards) so a lingering
TIME_WAIT socket from a previous flow cannot block the next.rŒ   F)Úbind_and_activateNTzOAuth callback port z is already in use (zk). Close any other in-progress login, or set a free `oauth.redirect_port` in the server config, then retry.)r   rš   r�   r”   rJ   r˜   Úserver_addressÚallow_reuse_addressÚserver_bindÚserver_activater:   ru   )rŠ   r(  ÚserverÚreservedrM   s   &&   r    Ú_start_callback_serverr1  ;  sÁ   € ðÜ˜[¨$Ð/°ÐPUÔVˆÜ$×(Ñ(¨¨tÓ4ˆØÒØ�M‰M×ÑÔ!Ø3;×=QÑ=QÓ=SÐ0ˆFŒM˜6Õ0à)-ˆFÔ&Ø×ÑÔ Ø×ÑÔ ð €Møô ô Ü&Ø" 4 &Ð(<¸S¸Eð BTð Tó
ð ð	ûðús   ‚BB ÂB/ÂB*Â*B/c                ó>   € V ^8„  d   QhR\         R\        R,          /# )r"   r×  Úcimd_urlN©ri   r]   )r%   s   "r    r&   r&   Q  s"   € ÷ _ñ _œdð _¬c°D­jñ _r   c                ó2  € V R,          \         8X  d   \        R4      hV R,          '       d   \        RV R,           24      hV R,          f#   V'       d   RV R2MRp\        RV,           4      h\        V R,          V R	,          V P	                  R
4      4      # )zHTurn a filled/empty result dict into the SDK's callback value, or raise.rÒ  Úuser_skippedzOAuth authorization failed: rÙ  z• If the browser showed an invalid-client error instead of an approval prompt, the authorization server rejected Hermes' Client ID Metadata Document (z€); set ``cimd: false`` under that server's ``oauth:`` block in config.yaml to authorize via dynamic client registration instead.ri  uq   OAuth callback timed out â€” no authorization code received. Ensure you completed the browser authorization flow.rÉ  rÊ  )r  ru   ÚRuntimeErrorrÎ  rn   )r×  r3  Úhints   && r    Ú_callback_outcomer9  Q  s°   € àˆg…Ô0Ô0Ü& ~Ó6Ð6Øˆg‡„ÜÐ9¸&À½/Ð9JÐKÓLÐLØˆkÕÒ"÷
 ðDØDLÀ:ð Nmñmð ð	 	ô
 'Øð  CGõ  Gó
ð 	
ô & f¨[Õ&9¸6À'½?ÈFÏJÉJÐW\ÓL]Ó^Ð^r   c                óJ   € V ^8„  d   QhR\         R\        R,          R\        /# )r"   rŠ   r3  Nr+   )r.   r]   r-   )r%   s   "r    r&   r&   c  s%   € ÷ 7ñ 7¤ð 7¬s°T­zð 7Ì5ñ 7r   c                ó   a aa€ VV V3R lpV# )a¼  Callback waiter bound to one flow's port. ``timeout`` is where ``oauth.timeout`` applies (mcp 2.0
dropped the provider's own). ``cimd_url`` only tailors the timeout message: a server refusing the
document aborts at the authorization endpoint, so no redirect arrives and a bare "timed out" would
hide the cause. Raises ``OAuthNonInteractiveError`` on timeout or when non-interactive.

Closing over the port (instead of reading the module-level ``_oauth_port``) keeps concurrent OAuth flows
isolated: flow A's waiter listens on flow A's port even when flow B's ``_configure_callback_port``
overwrites the legacy global afterwards (#34260, the callback-side sibling of the #44588
redirect-handler fix).
c               “   ó  <"  € \        4       p V e(   S'       g    \        V P                  4       G R j  x€L
 !  # \        R4       \	        4       w  r\        SV4      p\        P                  ! VP                  RR/RR7      P                  4        \        4       '       dM   V fI   \        R\        P                  RR7       \        P                  ! \        V3RR7      P                  4        R	p VS8  d;   \        V4      '       g*   \         P"                  ! R
4      G R j  x€L
  VR
,          pKA  VP%                  4        VP'                  4        \)        VS4      #  EL) L>  TP%                  4        TP'                  4        i ; i5i)Nz®OAuth callback requires an interactive session but none is available (non-interactive/background context); skipping browser authorization without binding a callback listener.Úpoll_intervalg      Ð?T)ÚtargetÚkwargsÚdaemonz’
  Or paste the redirect URL here (or the ``?code=...&state=...`` portion) and press Enter. Type ``skip`` + Enter to continue without this server:)r  Úflush)r>  rø  r@  ç        g      à?)r   rÎ  Úwait_for_callbackrá   rþ  r1  Ú	threadingÚThreadÚserve_foreverÚstartrÝ   r	  rË   r
  r  rÚ  rG   rH   ÚshutdownÚserver_closer9  )r#  r(  r×  r/  Úelapsedr3  rŠ   r+   s        €€€r    Ú_waitÚ$_make_callback_waiter.<locals>._waitn  sJ  øé € Ü1Ó3ˆØÒ%¯dô .°^×5UÑ5UÓ5W×/WÑXÐXô 	"ð\ô	]ô 5Ó6ÑˆÜ'¨¨kÓ:ˆô
 	×Ò × 4Ñ 4¸oÈtÐ=TÐ]aÔb×hÑhÔjô ×Ò Ò!7ÜðIä—Z‘Z tõ-ô ×ÒÔ$:À&ÀÐSWÔX×^Ñ^Ô`Øˆð	"Ø˜GÔ#¬M¸&×,AÒ,AÜ—m’m CÓ(×(Ð(Ø˜3•’à�O‰OÔØ×ÑÔ!Ü  ¨Ó2Ð2òK 0Xñ@ )øð �O‰OÔØ×ÑÕ!üs?   ƒFšF²E³B:FÃ.1E  ÄEÄ E  Ä/-FÅE  Å "FÆFr   )rŠ   r3  r+   rK  s   fff r    Ú_make_callback_waiterrM  c  s   ú€ ÷*3ðX €Lr   r{   c                óX   € V ^8„  d   QhR\         R\         \        ,          R,          RR/# )r"   r
  r{   Nr$   r|   )r%   s   "r    r&   r&   ¡  s+   € ÷ >ñ >¤Sð >¼#Ä½*ÀtÕ:Kð >ÐW[ñ >r   c               óf   € \        WR7      P                  4        \        P                  RV 4       R# )z8Delete stored OAuth tokens and client info for a server.)r{   zOAuth tokens removed for '%s'N)r  r”  rl   r°   )r
  r{   s   &$r    Úremove_oauth_tokensrP  ¡  s"   € ä�{Ô<×CÑCÔEÜ
‡K�KÐ/°Ö=r   zKhttps://nousresearch.github.io/hermes-agent/docs/oauth/client-metadata.jsonrŒ   r¹   c                ó0   € V ^8„  d   QhR\         R\        /# )r"   Úurlr$   ©r]   rÈ   )r%   s   "r    r&   r&   ²  s   € ÷ nñ nœCð n¤Dñ nr   c                óî  €  ^ RI Hp V! V 4      '       g   R# \        V 4      p\        VP                  ;'       g    VP
                  4      pT;'       g…    TP                  ;'       gq    \        ;QJ d?    R TP                  P                  R4       4       F  '       g   K   R'       * # 	  R'       * # ! R TP                  P                  R4       4       4      '       * #   \        \        3 d     R# i ; i)u  True when *url* is usable as a CIMD client_id on the installed SDK (ImportError = SDK predates
CIMD â†’ DCR only). The SDK checks only https + non-root path; userinfo, fragments and dot segments
are rejected here because they fail mid-browser-flow as an opaque invalid-client page.)Úis_valid_client_metadata_urlFc              3   ó*   "  € T F	  qR9   x € K  	  R# 5i)r(  N>   r(  ú..r   )r�   Úsegs   & r    r’   Ú%_is_valid_cimd_url.<locals>.<genexpr>¾  s   é € Ð6lÑUkÈc¸kÖ7IÓUkùs   ‚Ú/T)Úmcp.client.auth.utilsrU  r   rÈ   ÚusernameÚpasswordrf   r®   Úfragmentr£  r#   r  )rR  rU  rÁ   Úhas_userinfos   &   r    Ú_is_valid_cimd_urlr`  ²  sÂ   € ðÝFÙ+¨C×0Ò0ÙÜ˜#“ˆÜ˜FŸO™O×>Ð>¨v¯©Ó?ˆð ×lÐl §¡×lÐl·3³3Ñ6lÐU[×U`ÑU`×UfÑUfÐgjÔUkÓ6l·3´3ÔmÐm²3ÔmÐm°3Ñ6lÐU[×U`ÑU`×UfÑUfÐgjÔUkÓ6lÓ3lÔmÐmøô œÐ$ô Úðús   ‚C ˜"C »C ÃC4Ã3C4c                ó2   € V ^8„  d   QhR\         R,          /# r]  rQ   )r%   s   "r    r&   r&   È  s   € ÷ Eñ Eœ˜t�ñ Er   c                 ó¸   € \          F6  p V \        9  g   K  \        V 4      f   K  \        P                  V 4       V u # 	  \        '       d   \        ^ ,          # R# )u—  Reserve a pinned CIMD callback port, or None when none is usable. Holding the bound socket makes
contention cooperative: a sibling finds the bind refused and moves down the range. Once every pinned
port belongs to this process the range wraps rather than falling back to DCR â€” a reused port only
bites if both servers authorize at the same moment (reported by the waiter); DCR may be unsupported entirely.

Holding the bound socket until ``_wait_for_callback`` adopts it does the same job here as
``_reserve_callback_port`` does for ephemeral ports (#22161): a fixed port is just as stealable in the
minutes between selection and the browser redirect arriving.
N)r�   Ú_assigned_cimd_portsr¡   rp  )rŠ   s    r    Ú_pick_cimd_portrd  È  sL   € ÷ ˆØÔ+Ö+´¸tÓ0DÔ0PÜ ×'Ñ'¨Ô-ØŠKñ ÷ ';Ó&:Ô Õ"ÐDÀÐDr   c                ó>   € V ^8„  d   QhR\         R,          R\        /# )r"   r¨   Nr$   )r  rÈ   )r%   s   "r    r&   r&   Ù  s$   € ÷ qñ qÔ#5¸Õ#<ð qÄñ qr   c                ó    €  V e   V P                  4       MRpTRJ;'       d    \	        TRR4      RJ#   \        \        \        3 d     R# i ; i)uü   True when cached metadata shows this server doesn't advertise CIMD. The SDK decides CIMD vs DCR
in its 401 branch â€” after Hermes must fix the redirect URI â€” so cached metadata closes the gap;
only a genuinely unknown server pays the optimistic pin.NFÚ%client_id_metadata_document_supportedT)r‚  rg   r­   r®   re   )r¨   r|  s   & r    Ú_server_declined_cimdrh  Ù  s]   € ðØ4;Ò4G�7×.Ñ.Ô0ÈTˆð ˜4Ð×pÐp¤G¨HÐ6]Ð_cÓ$dÐlpÐ$pÐpøô œI¤zÐ2ô Úðús   ‚3 ³AÁAc                óz   € V ^8„  d   QhR\         R\        R,          R\        \        \        3,          R,          /# ©r"   Úcfgr¨   Nr$   )ri   r  r³   r]   r.   )r%   s   "r    r&   r&   ä  s8   € ÷ 1ñ 1œð 1Ô(:¸TÕ(Að 1ÌUÔSVÔX[ÐS[Í_Ð_cÕMcñ 1r   c                ó0  € V P                  R4      ;'       g    \        pV P                  R4      RJ ;'       Eg@    \        V4      '       * ;'       Eg'    V P                  R4      ;'       Eg    V P                  R4      ;'       gô    V P                  R4      ;'       gÛ    V P                  R4      ;'       g    RR8g  ;'       gµ    \        4       R	J;'       g¡    V P                  R
4      ;'       gˆ    V P                  R4      ;'       go    V P                  R4      ;'       g    R\        9  ;'       gE    \        V4      R	J;'       g0    VR	J;'       d    VP                  4       ;'       g    \        V4      pV'       d   R	M	\        4       pVf   R	# W$3# )u  ``(client_id URL, pinned callback port)``, or None to use DCR. Each ineligibility case means the
redirect URI is not one the document declares, the client identity is already settled, or the
server is known not to want a document â€” a metadata URL would be rejected.Úclient_metadata_urlÚcimdFÚ	client_idre  Úclient_namerf  rh  Nr  Úredirect_portr  rŒ   )	rn   Ú_CIMD_CLIENT_METADATA_URLr`  r   Ú_CIMD_REDIRECT_HOSTSr±   rŒ  rh  rd  )rk  r¨   rR  Ú
ineligiblerŠ   s   &&   r    Ú_maybe_use_cimdru  ä  s“  € ð �'‰'Ð'Ó
(×
EÐ
EÔ,E€Cà�‰�‹˜5Ð ÷ 	*ñ 	*Ü! #Ó&Ô&÷	*ñ 	*ð �7‰7�;Ó÷	*ñ 	*ð $'§7¡7¨?Ó#;÷	*ð 	*ð
 �7‰7�=Ó!÷	*ð 	*ð
 '*§g¡gÐ.JÓ&K×&UÐ&UÈvÐZ`Ñ%`÷	*ð 	*ô $Ó%¨TÐ1÷	*ð 	*ð �7‰7�>Ó"÷	*ð 	*ð '*§g¡g¨oÓ&>÷	*ð 	*ð �G‰G�OÓ$×3Ð3¨Ô<PÑP÷	*ð 	*ô ˜wÓ'¨tÐ3÷	*ð 	*ð ˜4Ð×;Ð; G×$9Ñ$9Ó$;÷	*ð 	*ô ! Ó)ð ÷ ‰4¤?Ó#4€DØ’<ˆ4Ð0 c [Ð0r   c                óR   € V ^8„  d   QhR\         R\         \        \        3,          /# )r"   rk  r$   )ri   r]   r	   )r%   s   "r    r&   r&   ü  s"   € ÷ 7ñ 7œdð 7¤t¬C´¨H¥~ñ 7r   c                ó@   € V P                  R4      pV'       d   RV/# / # )z«``client_metadata_url=`` kwargs for ``OAuthClientProvider`` when CIMD applies; omitted entirely
on a DCR flow because an SDK too old for CIMD rejects the keyword outright.Ú	_cimd_urlrm  rj  )rk  rR  s   & r    Úcimd_provider_kwargsry  ü  s%   € ð �'‰'�+Ó
€Cß+.Ð! 3Ð'Ð6°BÐ6r   c                ó>   € V ^8„  d   QhR\         R\        R,          /# )r"   rk  r$   Nr4  )r%   s   "r    r&   r&     s"   € ÷ Fñ F¤$ð F¬3°­:ñ Fr   c                ó    € V P                  R4      p\        V\        4      '       d'   VP                  4       '       d   VP                  4       # R# )zúConfigured ``oauth.user_agent`` for token-endpoint requests (exchange + refresh only, never MCP
traffic or discovery), or None; a null/empty YAML value never sends a blank header. No other
headers are configurable (secrets would land in config.yaml).Ú
user_agentN)rn   r¯   r]   r‡   )rk  Úuas   & r    Útoken_request_user_agentr~    s9   € ð 
�‰�Ó	€BÜ# B¬×,Ò,°·±·²ˆ2�8‰8‹:ÐEÀÐEr   c                ó0   € V ^8„  d   QhR\         R\        /# )r"   Úconfigr$   )ri   r-   )r%   s   "r    r&   r&     s   € ÷ Añ A¤$ð A¬5ñ Ar   c                óÂ   € R R lpV P                  R4      ;'       g    / p\        V! V P                  R4      R4      V! VP                  R4      R4      R,           4      # )	uv  Connect bound for an interactive OAuth login probe (CLI ``hermes mcp login``, dashboard and
Desktop re-auth): the server's ``connect_timeout`` or its ``oauth.timeout`` callback window
(default 300 s) plus 15 s headroom for the token exchange, whichever is longer. A fixed 315 s
floor here made raising ``oauth.timeout`` alone a no-op â€” the probe timed out first (#116278).c                ó0   € V ^8„  d   QhR\         R\         /# )r"   rw   r$   )r-   )r%   s   "r    r&   Ú+login_connect_timeout.<locals>.__annotate__  s   € ÷ ñ ¤ð ¬5ñ r   c                 ó`   €  \        R \        V 4      4      #   \        \        3 d    Tu # i ; i)rB  )r=  r-   r­   r®   )Úvaluerw   s   &&r    Ú_secondsÚ'login_connect_timeout.<locals>._seconds  s1   € ð	Ü�sœE %›LÓ)Ð)øÜœ:Ð&ô 	ØŠNð	ús   ‚ —-¬-ÚoauthÚconnect_timeoutrB  r+   ç     Àr@g      .@)rn   r=  )r€  r†  Ú	oauth_cfgs   &  r    Úlogin_connect_timeoutrŒ    sY   € õ
ð
 —
‘
˜7Ó#×)Ð) r€IÜ‰x˜Ÿ
™
Ð#4Ó5°sÓ;Ù˜	Ÿ™ iÓ0°%Ó8¸4Õ?óAð Ar   c                óJ   € V ^8„  d   QhR\         R\        R,          R\        /# rj  )ri   r  r.   )r%   s   "r    r&   r&     s(   € ÷ )ñ )¤$ð )Ô1CÀdÕ1Jð )ÔVYñ )r   c                ót  € \        4       p\        V P                  R4      ;'       d    V P                  R4      4      pVe8   V'       g0   ^ V R&   V P                  R4      ;'       g    VP                  V R&   ^ # \	        V4      w  rEV'       d+   V P                  R4      '       g   V'       g   W@R&   ^ V R&   ^ # \        W4      pVe
   Vw  V R&   pMb\        V P                  R^ 4      4      ;'       g    T;'       g    \        4       pV\        9   d!   V\        9  d   \        P                  V4       WpR&   VsV# )ud  Resolve the callback port into ``cfg['_resolved_port']`` (0 = non-loopback URI). Precedence:
dashboard flow / cached https redirect URI â†’ CIMD pinned port (sets ``cfg['_cimd_url']``) â†’
``oauth.redirect_port`` â†’ cached registration port â†’ fresh ephemeral port (the only parked one).
Also sets the legacy ``_oauth_port``.

NOTE: also sets the legacy module-level ``_oauth_port`` so existing calls to ``_wait_for_callback`` keep
working. The legacy global is the root cause of issue #5344 (port collision on concurrent OAuth flows);
replacing it with a ContextVar is out of scope for this consolidation PR.
ro  rq  Ú_resolved_portr  rx  )r   rÈ   rn   r  rÅ   ru  r.   r¥   r�   rc  rp  Ú_oauth_port)rk  r¨   r#  Úpinned_loopbackÚ
cached_uriÚcached_portrn  rŠ   s   &&      r    Ú_configure_callback_portr”    s  € ô .Ó/€Nô ˜3Ÿ7™7 ;Ó/×LÐL°C·G±G¸OÓ4LÓM€OØÒ!¯/Ø !ˆÐÑØ!Ÿg™g nÓ5×TÐT¸×9TÑ9TˆˆNÑÙÜ.¨wÓ7Ñ€Jß˜#Ÿ'™' .×1Ò1¿/Ø(ˆNÑØ !ˆÐÑÙÜ˜3Ó(€DØÒØ!%ÑˆˆKÑ™$ä�3—7‘7˜?¨AÓ.Ó/×ZÐZ°;×ZÐZÔBXÓBZˆà”;Ô 4Ô/CÔ#CÜ ×'Ñ'¨Ô-ð !ÐÑØ€KØ€Kr   c                ó<   € V ^8„  d   QhR\         R\        R\        /# )r"   rk  rŠ   r$   )ri   r.   r]   )r%   s   "r    r&   r&   F  s&   € ÷ jñ jœtð j¬3ð j´3ñ jr   c                óx   € V P                  R4      ;'       g#    RV P                  R4      ;'       g    R RV R2# )a#  Configured ``redirect_uri`` (proxy) or ``http://<redirect_host>:<port>/callback``; the single
derivation so client metadata and pre-registered info stay identical. ``redirect_host`` only changes
the hostname (some WAFs reject a literal ``127.0.0.1``); the listener still binds ``127.0.0.1``.r  zhttp://r  rŒ   Ú:r¸   rj  )rk  rŠ   s   &&r    Ú_resolve_redirect_urir˜  F  s@   € ð �7‰7�>Ó"×iÐi¨°·±¸Ó0H×0WÐ0WÈKÐ/XÐXYÐZ^ÐY_Ð_hÐ&iÐir   zClaude Codezmcp:connectc                óX   € V ^8„  d   QhR\         R,          R\         R,          R\        /# )r"   r
  NÚ
server_urlr$   rS  )r%   s   "r    r&   r&   S  s/   € ÷ eñ e¤c¨D¥jð eÄSÈ4ÅZð eÔ[_ñ er   c                ó  € ^ RI HpHp T;'       g    RP                  4       pV! VR4      '       g   V! VR4      '       d
   RV9   d   R# RT ;'       g    RP                  4       9   ;'       d    V'       * ;'       g    RV! V4      9   # )z<True when this MCP server is Figma's hosted remote endpoint.)Úbase_url_host_matchesÚbase_url_hostnameri  zmcp.figma.comz	figma.comz/mcpTÚfigma)Úutilsrœ  r�  r  )r
  rš  rœ  r�  rR  s   &&   r    Ú_is_figma_remote_mcpr   S  s|   € ç>Ø×Ð˜×
"Ñ
"Ó
$€CÙ˜S /×2Ò2Ñ7LÈSÐR]×7^Ò7^ÐciÐmpÔcpÙà�{×(Ð( b×/Ñ/Ó1Ñ1×dÐd¸3´w×7cÐ7cÀ'ÑM^Ð_bÓMcÑBcÐdr   r
  ri  rš  c                óV   € V ^8„  d   QhR\         R\        R\        R,          R\         /# )r"   rk  r
  rš  Nr$   r4  )r%   s   "r    r&   r&   ]  s0   € ÷ ñ ¤tð ¼Sð ÔSVÐY]ÕS]ð Ôimñ r   c               ó.  € \        W4      '       d„   V P                  R4      '       g/   \        V R&   \        P	                  RT;'       g    T\        4       V P                  R4      '       g
   \
        V R&   V P                  R4      ;'       g    RV R&   V # )u¶   Mutate *cfg* with provider-specific OAuth workarounds (before building client metadata /
pre-registering); returns *cfg*. Only fills keys the user left unset â€” explicit values win.rp  uf   MCP OAuth '%s': Figma DCR allowlist â€” registering as client_name=%r (override via oauth.client_name)Úscoperf  rg  )r   rn   Ú_FIGMA_DCR_CLIENT_NAMErl   r°   Ú_FIGMA_DEFAULT_SCOPE)rk  r
  rš  s   &$$r    Úapply_oauth_provider_defaultsr¦  ]  s…   € ô ˜K×4Ò4Ø�w‰w�}×%Ò%Ü!7ˆC�ÑÜ�K‰KØxØ×)Ð)˜zÔ+AôCð �w‰w�w×ÒÜ/ˆC�‰Lð -0¯G©GÐ4PÓ,Q×,iÐ,iÐUiˆÐ(Ñ)Ø€Jr   c                ó(   € V ^8„  d   QhR\         RR/# )r"   rk  r$   r   r©   )r%   s   "r    r&   r&   n  s   € ÷ <ñ <¤ð <Ð)>ñ <r   c                ó  € V P                  R4      pVf   \        R4      h\        R4      pV P                  R4      ;'       g    V P                  R4      '       d   RMRpR	V P                  R	R
4      R\        \	        W4      4      .RRR.RR.RVRV P                  RR4      /pV P                  R4      '       d   V R,          VR&    VP                  V4      #   \         d'    TP                  RR4       TP                  T4      u # i ; i)zGBuild OAuthClientMetadata; requires ``_configure_callback_port`` first.r�  NzI_configure_callback_port() must be called before _build_client_metadata()r   rf  re  rg  rh  rp  zHermes Agentrµ   Úgrant_typesÚauthorization_coder_  Úresponse_typesrÈ  Úapplication_typeÚnativer£  )rn   r®   rr   rs   r˜  r1  r×   r�   )rk  rŠ   Úmetadata_clsÚauth_methodÚmetadata_kwargss   &    r    Ú_build_client_metadatar±  n  s  € à�7‰7Ð#Ó$€DØ‚|ÜÐdÓeÐeÜÐ3Ó4€Là—'‘'Ð6Ó7×yÐyÐTW×T[ÑT[Ð\k×TlÒTlÑ<PÐrx€Kà�s—w‘w˜}¨nÓ=Øœ&Ô!6°sÓ!AÓBÐCØÐ,¨oÐ>Ø˜6˜(Ø$ kð 	˜CŸG™GÐ$6¸ÓAð'C€Oð ‡w�wˆw×ÒØ#& w¥<ˆ˜Ñ ð<Ø×*Ñ*¨?Ó;Ð;øÜô <Ø×ÑÐ.°Ô5Ø×*Ñ*¨?Ó;Ò;ð<ús   ÃC Ã.D
Ä	D
c                óN   € V ^8„  d   QhR\         R\        R\        R,          RR/# )r"   r¨   Únew_client_idÚnew_client_secretNr$   )r  r]   )r%   s   "r    r&   r&   ˆ  s6   € ÷ Vñ VÜðVÜ03ðVÜHKÈdÍ
ðVØW[ñVr   c                óœ  € \        V P                  4       4      p\        V\        4      '       d   VP	                  R4      MRpV'       d1   WA8X  d-   VP	                  R4      ;'       g    RT;'       g    R8X  d   R# RpV P                  4       V P                  4       3 F.  pVP                  4       '       g   K   VP                  4        RpK0  	  V'       d/   \        P                  RV P                  WAV P                  4       R# R#   \         d8   p\        P                  RT P                  TP                  T4        Rp?K§  Rp?ii ; i)a  Drop cached tokens when the configured client identity changes: tokens minted under the old
``client_id`` fail refresh with ``invalid_client``, and pre-registered clients are exempt from
auto-poison, so stale tokens would wedge every request until a manual wipe. Compares on-disk
``client.json`` BEFORE it is overwritten; a matching identity is a no-op.

Matching identity is a no-op so live sessions and valid tokens are preserved. Port of
cline/cline#12983's "invalidate tokens when OAuth client changes" invariant.
ro  Nre  FTzAMCP OAuth '%s': could not remove stale %s after client change: %szŸMCP OAuth '%s': configured OAuth client changed (client_id %r -> %r); discarded tokens minted under the previous client. Re-authorize with: hermes mcp login %s)r«   r¬   r¯   ri   rn   r  r  r÷   r“  r:   rl   rü   r  r;   )r¨   r³  r´  ÚexistingÚold_client_idÚremovedr#   rM   s   &&&     r    Ú#_invalidate_tokens_on_client_changer¹  ˆ  s-  € ô ˜'×3Ñ3Ó5Ó6€HÜ1;¸HÄd×1KÒ1K�H—L‘L Ô-ÐQU€Mß˜]Ô;ÀÇÁÈoÓA^×AfÐAfÐbfÐl}÷  mFð  mFð  BFô  AGÙØ€GØ×%Ñ%Ó'¨×);Ñ);Ó)=Ó>ˆØ�{‰{�}Š}Ùð	FØ�K‰KŒMØŠGñ ?÷ Ü�‰ðJà× Ñ  -À×@TÑ@Tö	Vñ øô ô 	FÜ�N‰NÐ^Ð`g×`tÑ`tÐvz×vÑvð  BE÷  Fò  Fûð	Fús   Â;D	Ä	EÄ,EÅEc                ó8   € V ^8„  d   QhR\         R\        RRRR/# )r"   r¨   rk  Úclient_metadatar   r$   N)r  ri   )r%   s   "r    r&   r&   ¦  s1   € ÷ Zñ ZÔ'9ð ZÄð ZÐWlð ZÐquñ Zr   c           
     ó  € VP                  R4      pV'       g   R# \        R4      p\        WVP                  R4      4       RVR\        WR,          4      .RVP                  RVP
                  R	VP                  /R Uu/ uF$  qQP                  V4      '       g   K  WQV,          bK&  	  upCp\        V P                  4       \        VP                  V4      4      4       \        P                  R
W0P                  4       R# u upi )z=If cfg has a pre-registered client_id, persist it to storage.ro  Nr   re  rµ   r�  r©  r«  rf  z$Pre-registered client_id=%s for '%s')re  rp  r£  )rn   rr   r¹  r˜  r©  r«  rf  r   r¬   r  r1  rl   rm   r  )r¨   rk  r»  ro  Úinfo_clsÚkeyÚ	info_dicts   &&&    r    Ú_maybe_preregister_clientrÀ  ¦  sè   € à—‘˜Ó$€IßÙÜÐ6Ó7€HÜ'¨¸C¿G¹GÀOÓ<TÔUà�YØÔ/°Ð9IÕ5JÓKÐLØ�×2Ñ2Ø˜/×8Ñ8Ø$ o×&PÑ&Pð!`ñ %NÓ
^Ñ$M˜S×QXÑQXÐY\×Q]Œ=ˆ3�C•Š=Ñ$MÑ
^ð!`€Iô �×)Ñ)Ó+¬[¸×9PÑ9PÐQZÓ9[Ó-\Ô]Ü
‡L�LÐ7¸×DXÑDXÖYùò _s   Â DÂDc          	      óz   € V ^8„  d   QhR\         R\        \         ,          R\         R,          R\         R,          /# )r"   r
  rM   rš  Nr$   )r]   rI   )r%   s   "r    r&   r&   ¸  s;   € ÷ #+ñ #+Üð#+Ü(¬3Õ.ð#+Ü?BÀT½zð#+ÜUXÐ[_ÕU_ñ#+r   c               ó´  a€ \        V4      pVP                  4       o^ RIHp VP	                  V4      '       d   R# RV9   dW   RS9   g   RS9   dI   \
        ;QJ d    V3R lR 4       F  '       g   K   RM	  RM! V3R lR 4       4      '       d	   R	V  R
V  2# RV9   ;'       g    RS9   ;'       dp    \
        ;QJ d    V3R lR 4       F  '       g   K   RM	  RM! V3R lR 4       4      ;'       g.    SP                  4       R9   ;'       g    RV9   ;'       d    RS9   pV'       g   R# \        W4      '       d   R	V  R\         R\        : RV  2# R	V  R2# )a  Turn a DCR 403/Forbidden or 404 into a useful next step; None for anything else so the caller keeps the
original text. Figma gates DCR on exact ``client_name`` (auto-set to ``Claude Code``), so this fires
when the user overrode it or an older Hermes is running.)Ú_DISCOVERY_CONTEXT_LEADNÚ404z	not foundz	/registerc              3   ó,   <"  € T F	  qS9   x € K  	  R # 5irŽ   r   ©r�   rÔ  Úlowereds   & €r    r’   Ú4humanize_oauth_registration_error.<locals>.<genexpr>Å  s   øé € ÐNÑ*M Q˜–LÓ*Mùó   ƒTFÚ'uk  ' does not support automatic client registration â€” the registration request returned 404 (typical for Google's hosted Gmail/Drive MCP servers and other providers without RFC 7591 dynamic client registration). Create an OAuth client for this provider and add it under config.yaml as `oauth: {client_id: ..., client_secret: ...}`, then re-run:
  hermes mcp login Ú403Ú	forbiddenc              3   ó,   <"  € T F	  qS9   x € K  	  R # 5irŽ   r   rÆ  s   & €r    r’   rÈ  Ì  s   øé € ÐFÑ"E˜Q�ŽLÓ"EùrÉ  uF   ' is Figma's remote MCP â€” DCR is allowlisted by exact client_name ("zK" and "Codex" work; most other names 403). Hermes defaults to client_name: z} automatically. If you set oauth.client_name yourself, change it to one of those, or clear it and re-run:
  hermes mcp login u<  ' only allows pre-approved OAuth clients â€” it rejected client registration (403), so no browser flow can start. Options: set oauth.client_name to a name the provider allowlists, add a pre-registered client (oauth: {client_id: ..., client_secret: ...}), or use the provider's stdio / API-key / local server instead.)ÚregistÚdcrzdynamic client>   rÌ  ú403 forbiddenúhttp 403: forbidden)	r]   r  Útools.mcp_oauth_providerrÃ  Ú
startswithr£  r‡   r   r¤  )r
  rM   rš  ÚmsgrÃ  Úlooks_like_registrationrÇ  s   &&$   @r    Ú!humanize_oauth_registration_errorrÖ  ¸  sh  ø€ ô
 ˆc‹(€CØ�i‰i‹k€GÝ@Ø
‡~�~Ð-×.Ò.Ùð 	�Œ˜+¨Ô0°KÀ7Ô4Jß“ÔNÑ*MÓN——’ÔNÑ*MÓN×NÒNà�ˆ}ð að bmÐ`mðoð	pð
  %¨™|×EÐE¨{¸gÑ/E÷ 6ð 6ß‹ÔFÑ"EÓF��ŠÔFÑ"EÓFÓF÷ 	5ð 	5Ø�=‰=‹?ÐSÑS÷	5ð 	5à�S‰L×3Ð3˜[¨GÑ3ð ÷ #ÙÜ˜K×4Ò4à�ˆ}ð Ü(Ð)ð *Ü2Ñ5ð 6VØVaÐUbðdð	eð ˆKˆ=ð *ð 	*ð+r   c                óN   € V ^8„  d   QhR\         R\         R\        R,          RR/# )r"   r
  rš  Úoauth_configNr$   zOAuthClientProvider | None)r]   ri   )r%   s   "r    r&   r&   Þ  s3   € ÷ Fñ F¤#ð F´3ð FÄdÈTÅkð FÐ]yñ Fr   c           
     ó¨  € \         '       d   \        R4      f%   \        P                  R\	        R4       2V 4       R# ^ RIHpHp V! WV4      w  rV\        4       '       g)   VP                  4       '       g   \        RV  RV  R24      hV! WVR	R
7      p\        f-   ^ RIHp \        RV\        R4      3RRR\        R\        /4      s\        RRV/VB # )uÙ   ``httpx.Auth`` OAuth handler for an MCP server; None if the SDK lacks OAuth. Legacy API â€” new code
uses :func:`tools.mcp_oauth_manager.get_manager` so state is shared across config-time, runtime and reconnect paths.r   NzHMCP OAuth requested for '%s' but SDK auth types are not available. Run: rX   )Úbuild_provider_kwargsÚprepare_oauth_configzMCP OAuth for 'z€': non-interactive environment and no cached tokens found. The OAuth flow requires browser authorization. Run `hermes mcp login z[` interactively first to complete initial authorization, then cached tokens will be reused.T)Ússh_proxy_hint)ÚHermesProviderMixinÚHermesOAuthClientProviderr   zMSDK provider plus Hermes' token-endpoint fixes (see ``HermesProviderMixin``).r   Ú_hermes_loggerrš  r   )rk   rr   rl   rü   r   rÒ  rÚ  rÛ  rÝ   rº  ru   rÞ  rÝ  r/  r   )	r
  rš  rØ  rÚ  rÛ  rk  r¨   r?  rÝ  s	   &&&      r    Úbuild_oauth_authrà  Þ  sï   € ÷ ÓœzÐ*?Ó@ÒHÜ�‰ÐaÜ& uÓ-Ð.ð0Ø1<ô	>áßTá'¨ÀÓN�L€CÜ×Ò W×%>Ñ%>×%@Ò%@Ü&Ø˜k˜]ð +EØEPÀMð RHðHóIð 	Iñ # 3ÀÔE€FÜ Ò(Ý@ä$(Ð)DÐGZÔ\fÐg|Ó\}ÐF~ØÐfØœ(Ð$4´fðA>ó %?Ð!ô %ÑE°
ÐE¸fÑEÐEr   c                ó\  € V ^8„  d   Qh/ ^ \         9   d   \        \        \        3,          ;R&   ^\         9   d   \        R,          ;R&   ^\         9   d%   \        \        \
        P
                  3,          ;R&   ^\         9   d
   \        ;R&   ^\         9   d   \        \        ,          ;R&   # )r"   ra   Nr�  rš   rÞ  rc  )Ú__conditional_annotations__ri   r]   r	   r.   r”   rº   )r%   s   "r    r&   r&      s•   € ÷ Oñ O÷b "Ò !Œd”3œ�8�nÑ !ñcO÷d Ò ŒS�4�ZÑ ñeO÷h 1Ò 0”4œœVŸ]™]Ð*Õ+Ñ 0ñiO÷x &Ò %œ3Ñ %ñyO÷F %Ò $”dœ3•iÑ $òGOr   )r   r   r   r   r   >   ÚnÚqÚsÚnoÚquitÚskipÚcancelrŽ   )NN)NrŠ  )iòl  iól  iôl  iõl  iöl  )Šrâ  r   Úpmr   rG   rU   ÚcontextvarsrE   rå  Úimportlib.utilÚutilÚ_importlib_utilrø   Úloggingr6   r…   r”   r>  rË   rD  r<   r  Ú	functoolsr   r>   rf   rB   Úhttp.serverr   r   Úpathlibr   Útypingr   r	   Úurllib.parser
   r   r4   r   rŸ  r   Útools.mcp_dashboard_oauthr   rã   r   Úmcp.client.authr   rÍ  r   r   r   r   Ú	getLoggerr   rl   Ú_REFRESH_FENCE_TIMEOUT_SECONDSr7  r   Ú	frozensetÚEWOULDBLOCKÚEAGAINÚEACCESÚEDEADLKrF   r*   rO   rW   Ú	find_specrk   rm   rd   ra   rr   Úpydanticrs   ru   r�  Ú
ContextVarrv   rx   r  r  r   rˆ   rš   r›   r¡   r¥   r±   rÅ   rÚ   rÝ   rá   rä   ræ   rò   r«   r   r  r  rÎ  rÕ  rÚ  rþ  r  r  r  r  r&  r1  r9  rM  rÞ  rP  rr  r�   rs  r`  rc  rd  rh  ru  ry  r~  rŒ  r”  r˜  r¤  r¥  r   r¦  r±  r¹  rÀ  rÖ  rà  r&   )râ  s   @r    Ú<module>r     sD  øðöOõ Û Û Û Û Û Ý (Û Û Û 	Û 	Û Û Û 
Û Û Û Ý #ðÛðÛ÷ ;Ý ß %ß +å .Ý #ß açÝ3ßjÓjà	×	Ò	˜8Ó	$€ð
 "&Ð ô	˜,ô 	ñ % e×&7Ñ&7¸¿¹ÀuÇ|Á|ÐUZ×UbÑUbÐ%cÓdÐ õ;ðGÐ@^÷ GõTð& #×,Ò,¨UÓ3¸4Ð?Ð ßÓØ
‡L�LÐKÔLà~Ð Ø!€Ó !õ"ð$Ýô
S˜|ô Sð €Ó ð )×3Ò3Ð4PÐZ^Ô_Ð Ø'×2Ò2Ð3NÐX]Ô^Ð ñ ÒHÓI€Ø2Ð ÷^õGð /1Ð Ó 0ØÐ õõ.õ
4õõ.õ,õ

ò<ò>õPõõ;õ<÷
yñ y÷zDõTõRõõB [ðL\ð ð
Pð ÷",÷(õ<õ,_÷$7ðv "&Ð Ó %ð>Èd÷ >ð jÐ ð 2€Ù  +¨{Ð!;Ó<Ð õnð& #%Ð Ó $õEõ"q÷1õ07õFõA÷)õXjð 'Ð Ø$Ð ÷eðÀ2ð Ð`d÷ õ"<õ4Võ<Zð$#+ØLP÷#+÷LFñ Føðy% ô ØƒEðûð ô ØƒFðûðH ô ØƒFðús6   ÁK- ÁK= ÅL Ë-	K:Ë9K:Ë=	L
Ì	L
Ì	LÌL