+
    ÂHàj!  ã                   ó€  € R t ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RIHt ^ RI	H
t
 ^ RIHt ^ RIHt ^ RIHt ^ RIHt ^ R	IHt ^ R
IHtHtHtHtHtHtHtHtHtHtH t H!t!H"t"H#t#H$t$H%t%H&t&H't'H(t(H)t)H*t*H+t+H,t, ]! R4      t-R R lt.R R lt/R R lt0]R 4       t1R t2]2R@R R ll4       t3]2R R l4       t4]2R 4       t5]2RAR R ll4       t6]2R R l4       t7]2RBR R ll4       t8]2RCR R  ll4       t9]2RDR! R" ll4       t:]2RER# R$ ll4       t;]2R% 4       t<]2R& 4       t=]2R' R( l4       t>]2RFR) R* ll4       t?]2R+ 4       t@]2RGR, R- ll4       tA]2RHR. R/ ll4       tB]2RIR0 R1 ll4       tC]2RFR2 R3 ll4       tD]2RFR4 R5 ll4       tE]2R6 R7 l4       tF]2RJR8 R9 ll4       tG]2RKR: R; ll4       tH]2R< 4       tIR= R> ltJ]KR?8X  d
   ]J! 4        R# R# )La<  MCP server exposing browser-harness helpers over stdio.

Run:
    uv run python -m mcp_server

The server starts on stdio and exposes one MCP tool per browser-harness
helper. Each tool ensures the daemon is running, calls the existing helper,
and returns JSON text. Errors are caught and returned as {"error": ...}.
N)Úcontextmanager)Údatetime)ÚDecimal)ÚPath©ÚAny)Ú	MCPServer)ÚImage)Úensure_daemon)Úcapture_screenshotÚcdpÚclick_at_xyÚ	close_tabÚcurrent_tabÚensure_real_tabÚ
fill_inputÚgoto_urlÚhttp_getÚjsÚ	list_tabsÚnew_tabÚ	page_infoÚ	press_keyÚscrollÚstart_recordingÚstop_recordingÚ
switch_tabÚ	type_textÚupload_fileÚwaitÚwait_for_elementÚwait_for_loadzbrowser-harnessc                ó0   € V ^8„  d   QhR\         R\         /# ©é   ÚvalueÚreturnr   )Úformats   "ÚŒ/home/ubuntu/.hermes/installs/5f0a1ff4045d044e/environments/88679baa1dc64374a0cd943b62eeefb9/venv/lib/python3.14/site-packages/mcp_server.pyÚ__annotate__r)   9   s   € ÷ ñ ”cð œcñ ó    c                ó*  € \        V \        4      '       d;   \        P                  ! V 4      '       g   \        P                  ! V 4      '       d   R# \        V \
        4      '       d/   V P                  4        UUu/ uF  w  rV\        V4      bK  	  upp# \        V \        \        34      '       d   V  Uu. uF  p\        V4      NK  	  up# \        V \        \        34      '       d   V  Uu. uF  p\        V4      NK  	  up# V # u uppi u upi u upi )zÞRecursively convert values to JSON-safe forms before json.dumps.

json.dumps with allow_nan=False raises on NaN/Inf *before* calling
`default`, so non-finite floats from JS (NaN, Infinity) must be normalized
here instead.
N)Ú
isinstanceÚfloatÚmathÚisnanÚisinfÚdictÚitemsÚ
_normalizeÚlistÚtupleÚsetÚ	frozenset)r%   ÚkÚvs   &  r(   r3   r3   9   sÈ   € ô �%œ×Ò¤T§Z¢Z°×%6Ò%6¼$¿*º*ÀU×:KÒ:KÙÜ�%œ×ÒØ-2¯[©[¬]Ô;©]¡T Q�”:˜a“=Ò ©]Ò;Ð;Ü�%œ$¤˜×'Ò'Ù',Ó-¡u !”
˜1–¡uÑ-Ð-Ü�%œ#œyÐ)×*Ò*Ù',Ó-¡u !”
˜1–¡uÑ-Ð-Ø€Lùó <ùâ-ùâ-s   Á:DÂ5DÃ,Dc                ó0   € V ^8„  d   QhR\         R\         /# r#   r   )r'   s   "r(   r)   r)   K   s   € ÷ 
ñ 
œð 
¤ñ 
r*   c                ó:  € \        V \        4      '       d   V P                  RRR7      # \        V \        4      '       d   V P	                  4       # \        V \
        4      '       d   \        V 4      # \        V \        4      '       d   \        V 4      # \        V 4      # )zBFallback JSON encoder for values that json.dumps cannot serialize.zutf-8Úreplace)Úerrors)	r,   ÚbytesÚdecoder   Ú	isoformatr   r-   r   Ústr©r%   s   &r(   Ú_json_defaultrC   K   st   € ä�%œ×ÒØ�|‰|˜G¨Iˆ|Ó6Ð6Ü�%œ×"Ò"Ø�‰Ó Ð Ü�%œ×!Ò!Ü�U‹|ÐÜ�%œ×ÒÜ�5‹zÐÜˆu‹:Ðr*   c                ó0   € V ^8„  d   QhR\         R\        /# r#   )r   rA   )r'   s   "r(   r)   r)   X   s   € ÷ ñ ”ð œñ r*   c                óP   € \         P                  ! \        V 4      RR\        R7      # )zFSerialize a value to JSON text, never raising for unserializable data.F)Úensure_asciiÚ	allow_nanÚdefault)ÚjsonÚdumpsr3   rC   rB   s   &r(   Ú_dumprK   X   s$   € ä�:Š:Ü�5ÓØØÜô	ð r*   c               #  ó    "  € \         P                  p \         P                  \         n         Rx € V \         n        R#   T \         n        i ; i5i)uW  Redirect stdout to stderr for the duration of the context.

Some browser-harness helpers (start_recording, stop_recording) print status
messages. Under MCP stdio, any stdout output that isn't a valid JSON-RPC
message corrupts the protocol. Redirect stdout â†’ stderr so those messages
reach the client's logs without breaking the wire format.
N)ÚsysÚstdoutÚstderr)Úsaveds    r(   Ú_stderr_stdoutrQ   b   s1   é € ô �J‰J€EÜ—‘„C„JðÛàŒŽ
ø�UŒ�
üs   ‚*A­> ±A¾AÁAc                ó¸   a € \         P                  ! S 4      V 3R l4       p\        P                  S P                  S P
                  ;'       g    RR7      ! V4      # )a:  Wrap a helper so it is exposed as an MCP tool and returns JSON text.

Ensures the daemon is up, runs the helper, catches exceptions (including
daemon startup failures), and always returns a JSON string. Tool name and
description are taken from the wrapped function so the schema matches the
parameter annotations.
c                  óø   <€  \        4        \        4       ;_uu_ 4        S! V / VB pR R R 4       \        X4      #   + '       g   i     L; i  \         d!   p\        R\	        T4      /4      u R p?# R p?ii ; i)NÚerror)r
   rQ   rK   Ú	ExceptionrA   )ÚargsÚkwargsÚresultÚexcÚfns   *,  €r(   ÚwrapperÚ_tool.<locals>.wrapper|   sb   ø€ ð	.ÜŒOÜ×!Õ!Ù˜TÐ, VÑ,�÷ "ä˜“=Ð ÷ "×!ûô ô 	.Ü˜'¤3 s£8Ð,Ó-Õ-ûð	.ús1   ƒA Ÿ	;¨A »A	ÁA ÁA9ÁA4Á.A9Á4A9Ú )ÚnameÚdescription)Ú	functoolsÚwrapsÚSERVERÚtoolÚ__name__Ú__doc__)rZ   r[   s   f r(   Ú_toolrf   s   sJ   ø€ ô ‡_‚_�RÓô.ó ð.ô �;‰;˜BŸK™K°R·Z±Z×5EÐ5EÀ2ˆ;ÔFÀwÓOÐOr*   c                ó$   € V ^8„  d   QhR\         /# ©r$   Úurl©rA   )r'   s   "r(   r)   r)   Š   s   € ÷ &ñ &œñ &r*   c                ó   € R\        V 4      /# )z7Open a new browser tab. Returns the new tab's targetId.ÚtargetId)r   ©ri   s   &r(   Úbrowser_new_tabrn   ‰   s   € ð œ ›Ð%Ð%r*   c                ó$   € V ^8„  d   QhR\         /# rh   rj   )r'   s   "r(   r)   r)   �   s   € ÷ ñ ”cñ r*   c                ó   € \        V 4      # )zANavigate the current tab to `url`. Returns the navigation result.)r   rm   s   &r(   Úbrowser_gotorq   �   s   € ô �C‹=Ðr*   c                 ó   € \        4       # )zCReturn current tab metadata: url, title, viewport and scroll sizes.)r   © r*   r(   Úbrowser_page_infort   •   ó   € ô ‹;Ðr*   c                óH   € V ^8„  d   QhR\         R\         R\        R\         /# )r$   ÚxÚyÚbuttonÚclicks)ÚintrA   )r'   s   "r(   r)   r)   œ   s(   € ÷ ñ ”Sð œSð ¬#ð Äñ r*   c                ó$   € \        WW#R7       RR/# )zHClick at screen coordinates (x, y). `button` is 'left'/'right'/'middle'.)ry   rz   ÚokT)r   )rw   rx   ry   rz   s   &&&&r(   Úbrowser_clickr~   ›   s   € ô �˜VÕ3Ø�$ˆ<Ðr*   c                ó$   € V ^8„  d   QhR\         /# )r$   Útextrj   )r'   s   "r(   r)   r)   £   s   € ÷ ñ ”sñ r*   c                ó    € \        V 4       RR/# )z%Insert text into the focused element.r}   T)r   )r€   s   &r(   Úbrowser_typer‚   ¢   s   € ô ˆd„OØ�$ˆ<Ðr*   c                ó<   € V ^8„  d   QhR\         R\         R\        /# )r$   Úselectorr€   Úclear_first)rA   Úbool)r'   s   "r(   r)   r)   ª   s!   € ÷ ñ œ3ð ¤cð ¼ñ r*   c                ó$   € \        WVR7       RR/# )z0Fill an input matched by `selector` with `text`.)r…   r}   T)r   )r„   r€   r…   s   &&&r(   Úbrowser_fillrˆ   ©   s   € ô ˆx¨;Õ7Ø�$ˆ<Ðr*   c                ó0   € V ^8„  d   QhR\         R\        /# )r$   ÚkeyÚ	modifiers)rA   r{   )r'   s   "r(   r)   r)   ±   s   € ÷ ñ ”sð ¤sñ r*   c                ó"   € \        WR7       RR/# )zGPress a key. `modifiers` is a bitfield: 1=Alt, 2=Ctrl, 4=Meta, 8=Shift.)r‹   r}   T)r   )rŠ   r‹   s   &&r(   Úbrowser_pressr�   °   s   € ô ˆcÕ'Ø�$ˆ<Ðr*   c                óH   € V ^8„  d   QhR\         R\         R\         R\         /# )r$   rw   rx   ÚdyÚdx)r{   )r'   s   "r(   r)   r)   ¸   s(   € ÷ ñ ”cð œcð ¤sð ´sñ r*   c                ó$   € \        WW#R7       RR/# )zEScroll the wheel at (x, y) by `dy` vertical / `dx` horizontal pixels.)r�   r�   r}   T)r   )rw   rx   r�   r�   s   &&&&r(   Úbrowser_scrollr’   ·   s   € ô ˆ1�BÕØ�$ˆ<Ðr*   c                óX   € V ^8„  d   QhR\         R,          R\        R\        R,          /# )r$   ÚpathNÚfullÚmax_dim)rA   r†   r{   )r'   s   "r(   r)   r)   ¿   s1   € ÷ Pñ PÜ
��*ðPÜ#'ðPÜ:=À½*ñPr*   c                ó²   € \        WVR7      p \        P                  ! V 4      P                  w  r4\        P
                  P                  V 4      pRV RVRVRV/# )zƒCapture a PNG screenshot. If `path` is omitted, a temp file is used.
Set `max_dim` to downscale results larger than that dimension.)r”   r•   r–   r”   ÚwidthÚheightÚ
size_bytes)r   r	   ÚopenÚsizeÚosr”   Úgetsize)r”   r•   r–   r˜   r™   rœ   s   &&&   r(   Úbrowser_screenshotrŸ   ¾   sO   € ô  4¸GÔD€DÜ—J’J˜tÓ$×)Ñ)�M€EÜ�7‰7�?‰?˜4Ó €DØ�D˜' 5¨(°F¸LÈ$ÐOÐOr*   c                 ó   € \        4       # )zList open page tabs.)r   rs   r*   r(   Úbrowser_list_tabsr¡   Ê   ru   r*   c                 ó   € \        4       # )z0Return the active tab's targetId, url and title.)r   rs   r*   r(   Úbrowser_current_tabr£   Ð   s   € ô ‹=Ðr*   c                ó$   € V ^8„  d   QhR\         /# )r$   Útargetrj   )r'   s   "r(   r)   r)   ×   s   € ÷ -ñ -œsñ -r*   c                ó   € R\        V 4      /# )zDSwitch to tab by `targetId` or URL substring. Returns the sessionId.Ú	sessionId)r   ©r¥   s   &r(   Úbrowser_switch_tabr©   Ö   s   € ð œ FÓ+Ð,Ð,r*   c                ó2   € V ^8„  d   QhR\         R,          /# )r$   r¥   Nrj   )r'   s   "r(   r)   r)   Ý   s   € ÷ ñ œc D�jñ r*   c                ó    € \        V 4       RR/# )z5Close a tab. Without `target`, closes the active tab.r}   T)r   r¨   s   &r(   Úbrowser_close_tabr¬   Ü   s   € ô ˆfÔØ�$ˆ<Ðr*   c                 ó   € \        4       # )zMSwitch to a real (non-internal) tab if the current one is chrome:// or stale.)r   rs   r*   r(   Úbrowser_ensure_real_tabr®   ã   s   € ô ÓÐr*   c                ó$   € V ^8„  d   QhR\         /# )r$   Úseconds©r-   )r'   s   "r(   r)   r)   ê   s   € ÷ ñ œ%ñ r*   c                ó    € \        V 4       RR/# )zWait for `seconds`.r}   T)r   )r°   s   &r(   Úbrowser_waitr³   é   s   € ô 	ˆ„MØ�$ˆ<Ðr*   c                ó$   € V ^8„  d   QhR\         /# )r$   Útimeoutr±   )r'   s   "r(   r)   r)   ñ   s   € ÷ 2ñ 2¤5ñ 2r*   c                ó   € R\        V R7      /# )z6Wait until the current tab's readyState is 'complete'.r}   ©rµ   )r!   r·   s   &r(   Úbrowser_wait_for_loadr¸   ð   s   € ð ”-¨Ô0Ð1Ð1r*   c                ó<   € V ^8„  d   QhR\         R\        R\        /# )r$   r„   rµ   Úvisible)rA   r-   r†   )r'   s   "r(   r)   r)   ÷   s)   € ÷ Pñ PÜðPÜ!ðPÜ37ñPr*   c                ó    € R\        WVR7      /# )zhWait for an element matching `selector` to appear. Set `visible=True` to
also require it to be rendered.r}   )rµ   rº   )r    )r„   rµ   rº   s   &&&r(   Úbrowser_wait_for_elementr¼   ö   s   € ð Ô" 8ÀgÔNÐOÐOr*   c                ó>   € V ^8„  d   QhR\         R\         R,          /# )r$   Ú
expressionÚ	target_idNrj   )r'   s   "r(   r)   r)      s   € ÷ /ñ /œ3ð /¬3°­:ñ /r*   c                ó   € \        WR7      # )zOEvaluate a JavaScript expression in the current tab (or an iframe `target_id`).)r¿   )r   )r¾   r¿   s   &&r(   Ú
browser_jsrÁ   ÿ   s   € ô ˆjÔ.Ð.r*   c                ó>   € V ^8„  d   QhR\         R\        R,          /# )r$   ÚmethodÚparamsN)rA   r1   )r'   s   "r(   r)   r)     s   € ÷ )ñ )œð )¤T¨D¥[ñ )r*   c                ó.   € \        V 3/ T;'       g    / B # )zJCall a raw Chrome DevTools Protocol method. `params` are passed as kwargs.)r   )rÃ   rÄ   s   &&r(   Úbrowser_cdprÆ     s   € ô ˆvÑ(˜&Ÿ,˜, BÑ(Ð(r*   c                ó0   € V ^8„  d   QhR\         R\         /# )r$   r„   r”   rj   )r'   s   "r(   r)   r)     s   € ÷ ñ ¤#ð ¬Sñ r*   c                ó    € \        W4       RR/# )zJSet files on a file input matched by `selector`. `path` is the local file.r}   T)r   )r„   r”   s   &&r(   Úbrowser_upload_filerÉ     s   € ô �ÔØ�$ˆ<Ðr*   c                óJ   € V ^8„  d   QhR\         R\        R\        R,          /# )r$   ri   rµ   ÚheadersN)rA   r-   r1   )r'   s   "r(   r)   r)     s*   € ÷ Eñ Eœ#ð E¬ð E¼tÀd½{ñ Er*   c                ó    € R\        WVR7      /# )zHTTP GET `url` (browser-less). Returns the response body. Optional
`headers` dict for authentication or custom request headers.r€   )rË   rµ   )r   )ri   rµ   rË   s   &&&r(   Úbrowser_http_getrÍ     s   € ð ”H˜S¸7ÔCÐDÐDr*   c                óL   € V ^8„  d   QhR\         R,          R\         R,          /# )r$   r^   NÚtitlerj   )r'   s   "r(   r)   r)     s&   € ÷ Fñ F¤#¨¥*ð F¼CÀ$½Jñ Fr*   c                ó   € R\        WR7      /# )z-Start recording actions to a local directory.Úrecording_dir©r^   rÏ   )r   rÒ   s   &&r(   Úbrowser_start_recordingrÓ     s   € ð œ_°$ÔDÐEÐEr*   c                 ó   € R\        4       /# )z3Stop the active recording and return its directory.rÑ   )r   rs   r*   r(   Úbrowser_stop_recordingrÕ     s   € ð œ^Ó-Ð.Ð.r*   c                ó   € V ^8„  d   QhRR/# )r$   r&   Nrs   )r'   s   "r(   r)   r)   %  s   € ÷ ñ ˆdñ r*   c                 ó.   € \         P                  4        R# )z.Run the Browser Harness MCP server over stdio.N)rb   Úrunrs   r*   r(   ÚmainrÙ   %  s   € ä
‡J�J†Lr*   Ú__main__)zabout:blank)Úlefté   )T)é    )iÔþÿÿrÝ   )NFN)N)g      ð?)g      .@)g      $@F)g      4@N)NN)Lre   r`   rI   r.   r�   rM   Ú
contextlibr   r   Údecimalr   Úpathlibr   Útypingr   Ú
mcp.serverr   ÚPILr	   Úbrowser_harness.adminr
   Úbrowser_harness.helpersr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r    r!   rb   r3   rC   rK   rQ   rf   rn   rq   rt   r~   r‚   rˆ   r�   r’   rŸ   r¡   r£   r©   r¬   r®   r³   r¸   r¼   rÁ   rÆ   rÉ   rÍ   rÓ   rÕ   rÙ   rd   rs   r*   r(   Ú<module>ræ      sy  ðñó Û Û Û 	Û 
Ý %Ý Ý Ý Ý å  Ý å /÷÷ ÷ ÷ ÷ ÷ ñ ñ4 
Ð$Ó	%€õõ$
õð ñó ðò Pð, ö&ó ð&ð
 ôó ðð
 ñó ðð
 öó ðð ôó ðð öó ðð öó ðð öó ðð öPó ðPð ñó ðð
 ñó ðð
 ô-ó ð-ð
 öó ðð ñó ðð
 öó ðð ö2ó ð2ð
 öPó ðPð ö/ó ð/ð
 ö)ó ð)ð
 ôó ðð öEó ðEð öFó ðFð
 ñ/ó ð/õ
ð
 ˆzÔÙ†Fñ r*   