Ë
    tPêjª°  ã                   ót  — U d Z ddlZddlZddlZddlZddlZddlZddlmZm	Z	m
Z
mZmZ ddlmZ ddlmZmZmZ ddlmZ ddlmZ ej,                  rddlmZ  G d	„ d
e«      Z G d„ de«      Zej6                  j9                  dd«      Zeed<   dZeed<   ej6                  j9                  dd«      Z eed<   ej6                  j9                  dd«      Z!eed<   dZ"eed<   dZ#eed<   dZ$eed<   dZ%eed<   de&fd„Z'dˆde(d e)de(fd!„Z*de&fd"„Z+d#e(de(fd$„Z, ejZ                  d%«      Z.eed&<   d'e(de(fd(„Z/ddd)œd*e
e(e(e0e(e(f   z  f   d'e(dz  d#e(dz  de(e0e(e(f   z  dz  fd+„Z1d,Z2eed-<   d.e3de
e(e3f   dz  fd/„Z4d0e3de&fd1„Z5d0e3de&fd2„Z6d3eddfd4„Z7d5e(d#e(de(fd6„Z8d7e3de(fd8„Z9d7ede	e(   fd9„Z:d:e(d7ede	e(   fd;„Z;d<e(de(fd=„Z<d:e(d7ed>ee(   de(dz  fd?„Z=d@e(de>e(e(f   fdA„Z?d5e(dBee(   de>e(e(f   dz  fdC„Z@d5e(d7edz  de(fdD„ZA	 d‰d:e(dEee(   dz  de&fdF„ZBdŠd#e(dGe&ddfdH„ZCdIe3de(dz  fdJ„ZDdIe3dee3   dz  fdK„ZEdLe3de(dz  fdM„ZFdLe3dNe(de3fdO„ZG ejZ                  dP«      ZHeedQ<   dRe
e(e(f   dz  ddfdS„ZI G dT„ dUeJ«      ZK ejZ                  dV«      ZLeedW<   dXee   dz  de>e0e(e(f   eMe0e(ef      f   fdY„ZNdZe(deOe(   fd[„ZPd\ee(   deOe(   fd]„ZQdZe(d^e
e(e(f   de(fd_„ZRd`e
e(e(f   d^e
e(e(f   de0e(e(f   fda„ZSde(de(fdb„ZTdddcœdde
e(e(f   dz  dee
e(e(f   dz  de0e(e(f   dz  fdf„ZUdgZVeedh<   e>e(e)z  dif   ZWdjee>e>eWe(f   dif   dz     de>e>eWe(f   dif   dz  fdk„ZXd‹dZe3dleWde>e>eWe(f   dif   dz  fdm„ZY	 d‹dZe3dne
eWe(f   dleWde3fdo„ZZdˆdZe3dpe)de>e(dif   dz  fdq„Z[dIe3de3fdr„Z\dIe3dZe3de&fds„Z] e^h dt£«      Z_eedu<   e_ e^h dv£«      z  Z`eedw<   dxZaeedy<   dzZbeed{<   de(dz  fd|„Zcde(fd}„Zdde^e(   fd~„Zede^e(   fd„Zfd€ee(   de^e(   fd�„Zgddd‚œdle(dƒe
e(e(f   dz  d„e(dz  dd…fd†„Zhdƒe
e(e(f   dz  de
e(e(f   fd‡„Ziy)Œz
MCP Server Utilities
é    N)ÚIterableÚIteratorÚMappingÚMutableMappingÚMutableSequence)ÚSet)ÚAnyÚFinalÚProtocol)Úquote)Ú	MCPServer©ÚRequestc                   óv   — e Zd Zedefd„«       Zededz  fd„«       Zededz  fd„«       Zededz  fd„«       Zy)Ú_McpServerLikeÚreturnc                  ó   — y ©N© ©Úselfs    úq/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/_experimental/mcp_server/utils.pyÚ	server_idz_McpServerLike.server_id   s   € Ø #ó    Nc                  ó   — y r   r   r   s    r   Úserver_namez_McpServerLike.server_name   ó   € Ø),r   c                  ó   — y r   r   r   s    r   Úaliasz_McpServerLike.alias   s   € Ø#&r   c                  ó   — y r   r   r   s    r   Úshort_prefixz_McpServerLike.short_prefix   s   € Ø*-r   )	Ú__name__Ú
__module__Ú__qualname__ÚpropertyÚstrr   r   r   r!   r   r   r   r   r      sZ   „ ØØ#˜3Ò#ó Ø#ØØ,˜S 4™ZÒ,ó Ø,ØØ&�s˜T‘zÒ&ó Ø&ØØ-˜c D™jÒ-ó Ù-r   r   c                   ó`   — e Zd ZU edz  ed<   ededz  fd„«       Zedeeef   dz  fd„«       Zy)ÚMcpServerPayloadLikeNr   r   c                  ó   — y r   r   r   s    r   r   z McpServerPayloadLike.server_name$   r   r   c                  ó   — y r   r   r   s    r   Útool_name_to_display_namez.McpServerPayloadLike.tool_name_to_display_name&   s   € ØEHr   )	r"   r#   r$   r&   Ú__annotations__r%   r   r   r+   r   r   r   r(   r(   !   sD   … Ø�‰:ÓàØ,˜S 4™ZÒ,ó Ø,ØØH¨7°3¸°8Ñ+<¸tÑ+CÒHó ÙHr   r(   ÚLITELLM_MCP_SERVER_NAMEzlitellm-mcp-serverz1.0.0ÚLITELLM_MCP_SERVER_VERSIONÚLITELLM_MCP_SERVER_DESCRIPTIONzMCP Server for LiteLLMÚMCP_TOOL_PREFIX_SEPARATORú-z#{server_name}{separator}{tool_name}ÚMCP_TOOL_PREFIX_FORMATé   ÚSHORT_MCP_TOOL_PREFIX_LENGTHÚ>0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzÚ_BASE62_ALPHABETÚ4ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzÚ_BASE52_ALPHA_ALPHABETr   c                  ó‚   — t         j                  j                  dd«      } | j                  «       j	                  «       dv S )z¼Return True when the short-ID tool prefix mode is enabled.

    Read at call time (not import time) so tests and runtime config changes
    take effect without reimporting the module.
    Ú!LITELLM_USE_SHORT_MCP_TOOL_PREFIXÚ )Ú1ÚtrueÚyesÚon)ÚosÚenvironÚgetÚstripÚlower)Úraws    r   Ú is_short_mcp_tool_prefix_enabledrF   a   s4   € ô —‘—‘Ð CÀRÓH€CØ�9‰9‹;×ÑÓÐ"<Ð<Ð<r   r   Úattemptc                 óº  — | st        d«      ‚|dk(  r| n| › d|› �}t        j                  |j                  d«      «      j	                  «       }t
        j                  |dd d«      }g }t        t        «      D ]H  }|t        dz
  k(  }|rt        nt        }t        |t        |«      «      \  }}	|j                  ||	   «       ŒJ d	j                  t        |«      «      S )
u°  Derive the deterministic three-character prefix for a server.

    Uses SHA-256 of ``f"{server_id}#{attempt}"`` and folds the first eight
    bytes into a fixed-length string whose first character is drawn from
    ``_BASE52_ALPHA_ALPHABET`` (so the prefix never starts with a digit)
    and whose remaining characters are drawn from the full base62
    alphabet.  Pass ``attempt > 0`` to rehash to a different prefix when
    the natural hash collides with a prefix already assigned to another
    server (see ``MCPServerManager._assign_unique_short_prefix``).  An
    empty ``server_id`` raises ``ValueError`` â€” short prefixes require a
    stable identifier to be deterministic.
    z:compute_short_server_prefix requires a non-empty server_idr   ú#úutf-8Né   Úbigé   r;   )Ú
ValueErrorÚhashlibÚsha256ÚencodeÚdigestÚintÚ
from_bytesÚranger4   r8   r6   ÚdivmodÚlenÚappendÚjoinÚreversed)
r   rG   ÚseedrR   ÚvalueÚcharsÚpositionÚis_first_charÚalphabetÚidxs
             r   Úcompute_short_server_prefixrb   k   sÑ   € ñ ÜÐUÓVÐVà&¨!š|‘)°I°;¸aÀ¸yÐ1I€DÜ—N‘N 4§;¡;¨wÓ#7Ó8×?Ñ?ÓA€FÜ�N‰N˜6 " 1˜: uÓ-€Eð !€EÜÔ6Ó7ò $ˆØ Ô$@À1Ñ$DÑDˆÙ-:Õ)Ô@PˆÜ˜E¤3 x£=Ó1‰
ˆˆsØ�‰�X˜c‘]Õ#ð	$ð
 �7‰7”8˜E“?Ó#Ð#r   c                  óN   — 	 t        j                  d«       y# t        $ r Y yw xY w)zF
    Returns True if the MCP module is available, False otherwise
    ÚmcpTF)Ú	importlibÚimport_moduleÚImportErrorr   r   r   Úis_mcp_availablerh   Œ   s*   € ðÜ×Ñ Ô&ØøÜò Ùðús   ‚ ˜	$£$r   c                 ó&   — | j                  dd«      S )zD
    Normalize server name by replacing spaces with underscores
    ú Ú_)Úreplace)r   s    r   Únormalize_server_namerm   —   s   € ð ×Ñ˜s CÓ(Ð(r   z
[^a-z0-9_]Ú_MCP_ALIAS_HEADER_INVALID_REr   c                 ó¶   — t         j                  d| j                  «       j                  «       «      }t	        j                  dd|«      }|j                  d«      S )z�
    Sanitize an MCP server alias for x-mcp-{alias}-{header} HTTP headers.

    Must stay in sync with ui/litellm-dashboard/src/utils/mcpHeaderUtils.ts.
    rk   z_+)rn   ÚsubrD   rC   Úre)r   Ú	sanitizeds     r   Úsanitize_mcp_alias_for_headerrs   ¡   sF   € ô -×0Ñ0°°e·k±k³m×6IÑ6IÓ6KÓL€IÜ—‘�u˜c 9Ó-€IØ�?‰?˜3ÓÐr   )r   r   Úmcp_server_auth_headersc                ó  — | sy| j                  «       D ��ci c]  \  }}|j                  «       |“Œ }}}||fD ]L  }|sŒ|j                  «       g}t        |«      }|r||vr|j                  |«       |D ]  }	|	|v sŒ||	   c c S  ŒN yc c}}w )zð
    Resolve server-specific auth headers with case-insensitive matching.

    Tries the raw alias/server_name (lowercased) and the header-safe sanitized
    alias so dashboard clients using sanitize_mcp_alias_for_header() still match.
    N)ÚitemsrD   rs   rX   )
rt   r   r   ÚkÚvÚnormalized_headersÚ
identifierÚkeys_to_tryrr   Úkeys
             r   Ú!lookup_mcp_server_auth_in_headersr}   ¬   s°   € ñ #Øà:Q×:WÑ:WÓ:Y× Z±$°!°Q §¡£¨A¡Ð ZÐÑ Zà˜kÐ*ò 	/ˆ
ÙØØ!×'Ñ'Ó)Ð*ˆÜ1°*Ó=ˆ	Ù˜¨+Ñ5Ø×Ñ˜yÔ)Øò 	/ˆCØÐ(Ò(Ø)¨#Ñ.Ô.ñ	/ð	/ð ùó ![s   —B	Útool_allowlist_enforcedÚMCP_TOOL_ALLOWLIST_ENFORCED_KEYÚmcp_infoc                 óÌ   — | €y t        | t        «      r| S t        | t        «      r*	 t        j                  | «      }t        |t        «      r|S d S y # t
        t        f$ r Y y w xY wr   )Ú
isinstanceÚdictr&   ÚjsonÚloadsrN   Ú	TypeError)r€   Úparseds     r   Ú_parse_mcp_info_dictrˆ   Í   si   € ØÐØÜ�(œDÔ!ØˆÜ�(œCÔ ð	Ü$(§J¡J¨xÓ$8ˆFô $ F¬DÔ1ˆvÐ;°tÐ;Øøô œIÐ&ò 	Ùð	ús   §A ÁA#Á"A#Ú
mcp_serverc                 óp   — t        t        | dd «      «      }|syt        |j                  t        «      «      S )Nr€   F)rˆ   ÚgetattrÚboolrB   r   )r‰   r€   s     r   Ú!is_server_tool_allowlist_enforcedr�   Û   s0   € Ü*¬7°:¸zÈ4Ó+PÓQ€HÙØÜ�—‘Ô<Ó=Ó>Ð>r   c                 óT   — t        | dd«      xs g }t        | «      xs t        |«      S )zAWhether server-level allowed_tools whitelist filtering is active.Úallowed_toolsN)r‹   r�   rŒ   )r‰   r�   s     r   Úserver_applies_tool_allowlistr�   â   s+   € ä#*¨:°ÈÓ#MÒ#SÐQS€MÜ,¨ZÓ8ÒO¼DÀÓ<OÐOr   Úpayloadc                 ó°  — t        | d«      r#| j                  rt        | j                  d¬«       t        | d«      r#| j                  rt        | j                  d¬«       t        | d«      r!| j                  rt        | j                  «       t        | dd«      }t        | dd«      }|s|rt        |«      }n|rt        |«      }t        | d«      r|| _        yy)aL  
    Validate and normalize MCP server payload fields (server_name, alias, and
    tool_name_to_display_name).

    This function:
    1. Validates that server_name and alias don't contain the MCP_TOOL_PREFIX_SEPARATOR
    2. Validates that tool_name_to_display_name values satisfy Bedrock's tool-name pattern
    3. Normalizes alias by replacing spaces with underscores
    4. Sets default alias if not provided (using server_name as base)

    Args:
        payload: The payload object containing server_name and alias fields

    Raises:
        HTTPException: If validation fails
    r   T)Úraise_http_exceptionr   r+   N)Úhasattrr   Úvalidate_mcp_server_namer   r+   Úvalidate_tool_display_namesr‹   rm   )r‘   r   r   s      r   Ú)validate_and_normalize_mcp_server_payloadr—   è   s»   € ô$ ˆw˜Ô&¨7×+>Ò+>Ü  ×!4Ñ!4È4ÕPô ˆw˜Ô  W§]¢]Ü  §¡ÀTÕJô ˆwÐ3Ô4¸×9ZÒ9ZÜ# G×$EÑ$EÔFô   ¨°$Ó7€EÜ%,¨W°mÀTÓ%J€Ká‘[Ü% kÓ2‰Ù	Ü% eÓ,ˆô ˆw˜Ô Øˆ�ð !r   Únamec                 óP   — t        |«      }t        j                  |t        | ¬«      S )z0Add server name prefix to any MCP resource name.)r   Ú	separatorÚ	tool_name)rm   r2   Úformatr0   )r˜   r   Úformatted_server_names      r   Úadd_server_prefix_to_namerž     s.   € ä#8¸Ó#EÐä!×(Ñ(Ø)Ü+Øð )ó ð r   Úserverc                 óè   — t        «       r+t        | dd«      }|r|S t        | dd«      }|rt        |«      S t        | dd«      }|r|S t        | dd«      }|r|S t        | d«      rt        | dd«      }|S y)ue  Return the prefix for a server.

    When the short-prefix mode is enabled (``LITELLM_USE_SHORT_MCP_TOOL_PREFIX``)
    a three-character base62 ID is returned.  We prefer the cached
    ``server.short_prefix`` value when set â€” that field is populated at
    registration time by ``MCPServerManager._assign_unique_short_prefix``
    and resolves natural-hash collisions deterministically â€” and only fall
    back to the natural hash for ad-hoc / temp-server objects without a
    cached value.  In default mode the historical behaviour is preserved:
    alias if present, else server_name, else server_id.
    r!   Nr   r   r   r;   )rF   r‹   rb   r”   )rŸ   Úcachedr   r   r   Úfallback_server_ids         r   Úget_server_prefixr£     s‹   € ô (Ô)Ü$+¨F°NÀDÓ$IˆÙØˆMÜ'.¨v°{ÀDÓ'Iˆ	ÙÜ.¨yÓ9Ð9ä& v¨w¸Ó=€EÙØˆÜ%,¨V°]ÀDÓ%I€KÙØÐÜˆv�{Ô#Ü)0°¸ÀbÓ)IÐØ!Ð!Ør   c              #   óÖ  ‡K  — t        «       Šdt        dz  dt        t           fˆfd„} |t        | «      «      E d{  –—†   |t	        | dd«      «      E d{  –—†  t	        | dd«      }|r	  |t        |«      «      E d{  –—†   |t	        | dd«      «      E d{  –—†   |t	        | dd«      «      E d{  –—†   ||«      E d{  –—†  y7 Œ�7 Œv7 ŒO# t        $ r Y ŒXw xY w7 ŒE7 Œ,7 Œ­w)	aw  Yield every prefix form that may appear in tool names for ``server``.

    Always includes the *current* prefix returned by ``get_server_prefix``.
    Additionally yields the historical (alias / server_name / server_id) and
    short-ID forms so the routing layer can resolve tool names regardless of
    which prefix mode was active when the client first observed them.
    r\   Nr   c              3   óH   •K  — | r| ‰vr‰j                  | «       | –— y y y ­wr   )Úadd)r\   Úseens    €r   Ú_emitz)iter_known_server_prefixes.<locals>._emitH  s(   øè ø€ Ù�U $Ñ&Ø�H‰H�UŒOØ‹Kð 'ˆ5ùs   ƒ"r!   r   r   r   )Úsetr&   r   r£   r‹   rb   rN   )rŸ   r¨   r   r§   s      @r   Úiter_known_server_prefixesrª   >  sï   øè ø€ ô “%€Dð”S˜4‘Zð ¤H¬S¡Mõ ñ
 Ô& vÓ.Ó/×/Ð/Ù”W˜V ^°TÓ:Ó;×;Ð;ä#*¨6°;ÀÓ#E€IÙð	ÙÔ8¸ÓCÓD×DÐDñ ”W˜V W¨dÓ3Ó4×4Ð4Ù”W˜V ]°DÓ9Ó:×:Ð:Ù�YÓ×Ñð 0øØ;øð
 EùÜò 	Ùð	úð 5øØ:øØús‡   ƒ;C)¾C¿C)ÁCÁC)Á.C ÂCÂC ÂC)ÂC#ÂC)Â8C%Â9C)ÃC'Ã	C)ÃC)ÃC Ã	C ÃC)ÃC Ã C)Ã%C)Ã'C)r›   c              #   óP   K  — | –— t        |«      D ]  }t        | |«      –— Œ y­w)aA  Yield every name that denotes the bare ``tool_name`` on ``server``: the bare name,
    then its wire spelling under each prefix ``iter_known_server_prefixes`` accepts.
    ``get_server_prefix`` covers only the currently published one, and that moves with the
    alias and with ``LITELLM_USE_SHORT_MCP_TOOL_PREFIX``.
    N)rª   rž   )r›   rŸ   Úprefixs      r   Úiter_known_tool_name_spellingsr­   \  s.   è ø€ ð ‚OÜ,¨VÓ4ò ;ˆÜ'¨	°6Ó:Ó:ñ;ùs   ‚$&Úoperation_idc                 óB   — | j                  dd«      j                  «       S )a»  Return the tool name ``_register_openapi_tools`` registers ``operation_id`` under.

    The single transform between a spec's operationId and the name the gateway serves.
    Policy recovers the link by replaying this exact function, which is what keeps it from
    deciding for a tool it does not name: two operationIds that register as two tools
    necessarily normalize to two names here, because this is the map that registered them.
    rj   rk   )rl   rD   )r®   s    r   Úopenapi_tool_namer°   g  s    € ð ×Ñ  SÓ)×/Ñ/Ó1Ð1r   Únamesc                 ó®   ‡‡— t        |dd«      rt        nt        Št        | |«      D �ch c]
  } ‰|«      ’Œ c}Št	        ˆˆfd„|D «       d«      S c c}w )aÖ  Return the entry of ``names`` that denotes ``tool_name`` on ``server``, else ``None``.

    The single question every tool-name-keyed site asks: the allow list, the deny list,
    ``allowed_params`` and the discovery filter, so discovery hides exactly what dispatch
    refuses. It spans every spelling routing accepts and no more, because a tool's identity
    is the exact name routing dispatches; anything looser lets one policy decide two tools.

    On an OpenAPI server the configured entry holds the spec's operationId while routing
    holds :func:`openapi_tool_name` of it, so both sides go through that map first. Doing it
    with the registering function rather than a lookalike is the whole safety argument: a
    coarser one collapses operationIds that registration keeps apart.

    Callers read the returned entry rather than testing a container's values, which is what
    stops an explicitly empty ``allowed_params`` list from reading as "nothing configured".
    Ú	spec_pathNc              3   ó8   •K  — | ]  } ‰|«      ‰v sŒ|–— Œ y ­wr   r   )Ú.0r˜   Ú	normalizeÚ	spellingss     €€r   ú	<genexpr>z(match_known_tool_name.<locals>.<genexpr>„  s   øè ø€ ÒH˜$©9°T«?¸iÒ+G”ÑHùs   ƒ“)r‹   r°   r&   r­   Únext)r›   rŸ   r±   Úspellingr¶   r·   s       @@r   Úmatch_known_tool_namer»   r  sP   ù€ ô  -4°F¸KÈÔ,NÕ(ÔTW€IÜ<ZÐ[dÐflÓ<mÖn°™	 (Õ+Òn€IÜÔH %ÔHÈ$ÓOÐOùò os   ªAÚprefixed_namec                 óv   — t         | v r.| j                  t         d«      }t        |«      dk(  r
|d   |d   fS | dfS )a  Return the unprefixed name plus the server name used as prefix.

    Cuts at the FIRST separator, so the two halves are only trustworthy as a
    pair: they reassemble into ``prefixed_name`` exactly, which is what makes
    this safe for routing. Reading one half on its own is a guess about where the
    boundary fell, and that guess is wrong whenever the prefix itself contains
    the separator. Callers that compare a half against configuration must use
    :func:`match_known_server_prefix` or :func:`strip_known_server_prefix`.
    rM   é   r   r;   )r0   ÚsplitrW   )r¼   Úpartss     r   Úsplit_server_prefix_from_namerÁ   ‡  sH   € ô ! MÑ1Ø$×*Ñ*Ô+DÀaÓHˆÜˆu‹:˜Š?Ø˜‘8˜U 1™XÐ%Ð%Ø˜"ÐÐr   Úknown_prefixesc                 óÌ   — t        |D �ch c]  }|sŒt        |«      ’Œ c}t        d¬«      }|D ].  }|t        z   }| j	                  |«      sŒ|| t        |«      d fc S  yc c}w )aÌ  Return ``(matched_prefix, bare_name)`` when ``name`` carries a known prefix.

    Candidates are normalized and tried LONGEST first, so a prefix that itself
    contains :data:`MCP_TOOL_PREFIX_SEPARATOR` (the UUID ``server_id`` used when
    a server has no alias, or a legacy hyphenated alias) wins over a shorter
    prefix that is merely its leading segment. Returns ``None`` when no candidate
    matches, i.e. ``name`` carries none of these prefixes.
    T)r|   ÚreverseN)Úsortedrm   rW   r0   Ú
startswith)r˜   rÂ   r¬   Ú
candidatesÚseparator_suffixeds        r   Úmatch_known_server_prefixrÉ   ˜  sx   € ô Ø5CÖN¨6ÂvÔ	˜vÕ	&ÒNÜØô€Jð
 ò ;ˆØ#Ô&?Ñ?ÐØ�?‰?Ð-Õ.Ø˜4¤Ð$6Ó 7Ð 9Ð:Ð:Ò:ð;ð ùò 	Os
   ŠA!’A!c                 ó^   — |€t        | «      d   S t        | t        |«      «      }|€| S |d   S )a  Strip ``server``'s registered prefix from a prefixed tool/resource name.

    Unlike :func:`split_server_prefix_from_name`, which guesses the boundary at
    the first separator, this removes exactly ``{known_prefix}{separator}`` for
    one of the server's actual registered prefixes. It therefore stays correct
    when a prefix itself contains the separator (e.g. the UUID ``server_id``
    used as the fallback prefix when a server has no alias, or a legacy
    hyphenated alias), where the first-separator split would cut inside the
    prefix and never match the stored bare tool name.

    Returns ``name`` unchanged when ``server`` is known but none of its prefixes
    match (the name is already unprefixed). Falls back to the legacy split only
    when ``server`` is ``None``.
    r   rM   )rÁ   rÉ   rª   )r˜   rŸ   Úmatcheds      r   Ústrip_known_server_prefixrÌ   ­  s?   € ð €~Ü,¨TÓ2°1Ñ5Ð5Ü.¨tÔ5OÐPVÓ5WÓX€GØ�?ˆ4Ð2¨°©
Ð2r   Úknown_server_prefixesc                 ó6   — t         | vry|�t        | |«      duS y)a•  
    Check if tool name has a known MCP server prefix.

    When ``known_server_prefixes`` is provided the function verifies that the
    name actually starts with one of those prefixes followed by the separator,
    matching the longest candidate first so a prefix containing the separator
    still resolves.  Without it the check falls back to the legacy heuristic
    (separator present anywhere in the name), which can produce false
    positives for non-MCP tools whose names contain hyphens
    (e.g. ``text-to-speech``, ``code-review``).

    Args:
        tool_name: Tool name to check.
        known_server_prefixes: Optional set of normalized server prefixes
            currently registered in the MCP manager.  Pass this whenever
            the caller has access to the server registry so that the check
            is accurate.

    Returns:
        True if tool name is prefixed, False otherwise.
    FNT)r0   rÉ   )r›   rÍ   s     r   Úis_tool_name_prefixedrÏ   Â  s.   € ô2 !¨	Ñ1ØàÐ(Ü(¨Ð4IÓJÐRVÐVÐVð r   r“   c                 ó�   — | rDt         | v r;dt         › d| › �}|r"ddlm} ddlm}  ||j
                  d|i¬«      ‚t        |«      ‚yy)	aO  
    Validate that MCP server name does not contain 'MCP_TOOL_PREFIX_SEPARATOR'.

    Args:
        server_name: The server name to validate
        raise_http_exception: If True, raises HTTPException instead of generic Exception

    Raises:
        Exception or HTTPException: If server name contains 'MCP_TOOL_PREFIX_SEPARATOR'
    zServer name cannot contain 'z/'. Use an alternative character instead Found: r   ©ÚHTTPException©ÚstatusÚerror©Ústatus_codeÚdetailN)r0   ÚfastapirÒ   Ú	starletterÔ   ÚHTTP_400_BAD_REQUESTÚ	Exception)r   r“   Úerror_messagerÒ   rÔ   s        r   r•   r•   å  sk   € ñ Ô0°KÑ?Ø6Ô7PÐ6Qð  RAð  BMð  ANð  OˆÙÝ-Ý(á¨F×,GÑ,GÐQXÐZgÐPhÔiÐiä˜MÓ*Ð*ð @€{r   Úresultc                 óŠ  — t        | t        «      r| j                  d«      nt        | dd«      }|duryt        | t        «      r| j                  d«      nt        | dd«      }t        |t        t
        f«      rL|D ]G  }t        |t        «      r|j                  d«      nt        |dd«      }t        |t        «      sŒB|sŒE|c S  y)zúThe first text content of an ``isError=True`` tool result, or ``None``
    when the result is not an error.

    Accepts both ``mcp.types.CallToolResult`` objects and their dict
    equivalents, duck-typed so the ``mcp`` package is not required.
    ÚisErrorNTÚcontentÚtextz#MCP tool call returned isError=true)r‚   r   rB   r‹   ÚlistÚtupler&   )rÞ   Úis_errorrá   Úitemrâ   s        r   Ú%extract_mcp_tool_result_error_messagerç   û  s´   € ô 8BÀ&Ì'Ô7R˜fŸj™j¨Ô3ÔX_Ð`fÐhqÐswÓXx€HØ�tÑØÜ6@ÀÌÔ6Q˜VŸZ™Z¨	Ô2ÔW^Ð_eÐgpÐrvÓWw€GÜ�'œD¤%˜=Ô)Øò 	ˆDÜ/9¸$ÄÔ/H˜4Ÿ8™8 FÔ+ÌgÐVZÐ\bÐdhÓNiˆDÜ˜$¤Õ$ªØ’ð	ð 1r   c                 ó„   — t        | t        «      r| j                  d«      nt        | dd«      }t        |t        «      r|S y)að  The mutable content list of an MCP tool result, or ``None`` when it has none.

    Deliberately mutable: a guardrail masking the result rewrites entries in place,
    because the logging payload captured before the guardrail runs references this
    same list, so handing back a copy would leave the unmasked text in the spend log
    and the OTel span.

    Accepts both ``mcp.types.CallToolResult`` objects and their dict
    equivalents, duck-typed so the ``mcp`` package is not required.
    rá   N)r‚   r   rB   r‹   r   )rÞ   rá   s     r   Úmcp_tool_result_content_listré     s=   € ô 7AÀÌÔ6Q˜VŸZ™Z¨	Ô2ÔW^Ð_eÐgpÐrvÓWw€GÜ�'œ?Ô+ØˆØr   ræ   c                 ó¸   — t        | t        «      r| j                  d«      }n%t        t	        | dd«      «      rt	        | dd«      }nyt        |t
        «      r|S dS )a˜  The ``text`` of a rewritable MCP content item, or ``None``.

    Only mappings and Pydantic-style models report a text, because those are the
    only shapes ``with_mcp_content_item_text`` can rewrite; a caller therefore
    never reads text it would be unable to write back (e.g. masked by a
    guardrail). Non-text content (images, embedded resources) has no ``text``
    and is reported as ``None``.
    râ   Ú
model_copyN)r‚   r   rB   Úcallabler‹   r&   )ræ   râ   s     r   Úmcp_content_item_textrí     sR   € ô �$œÔ Ø�x‰x˜Ó‰Ü	”'˜$ ¨dÓ3Ô	4Ü�t˜V TÓ*‰àÜ˜d¤CÔ(ˆ4Ð2¨dÐ2r   râ   c                 ó|   — t        | t        «      ri | ¥d|i¥S t        | dd«      }t        |«      r |d|i¬«      S | S )zÀA copy of an MCP content item carrying ``text`` instead of its own.

    Only meaningful for items ``mcp_content_item_text`` returned a text for; any
    other item is returned unchanged.
    râ   rë   N)Úupdate)r‚   r   r‹   rì   )ræ   râ   rë   s      r   Úwith_mcp_content_item_textrð   2  sJ   € ô �$œÔ Ø%�$Ð%˜ Ñ%Ð%Ü  l°DÓ9€JÜ�
ÔÙ &¨$ Ô0Ð0Ø€Kr   z^[a-zA-Z0-9_-]+$ÚTOOL_DISPLAY_NAME_PATTERNr+   c           
      ó¼   — | sy| j                  «       D ]F  \  }}|sŒ	t        j                  |«      rŒddlm} ddlm}  ||j                  dd|› d|› d�i¬	«      ‚ y)
a·  
    Validate tool display name overrides against Bedrock's tool-name constraint.

    A display name replaces the tool name sent to the LLM provider, so it must
    satisfy the strictest provider requirement in use (Bedrock's
    ``[a-zA-Z0-9_-]+``); a name with spaces or other characters saves
    successfully but fails every subsequent Bedrock tool call.

    Raises:
        HTTPException: If any display name fails the pattern.
    Nr   rÑ   rÓ   rÕ   zInvalid display name 'z' for tool 'z¯'. Display names may only contain letters, digits, underscores, and hyphens (no spaces or other special characters), since they replace the tool name sent to the LLM provider.rÖ   )rv   rñ   ÚmatchrÙ   rÒ   rÚ   rÔ   rÛ   )r+   Úoriginal_nameÚdisplay_namerÒ   rÔ   s        r   r–   r–   C  sv   € ñ %Øà'@×'FÑ'FÓ'Hò Ñ#ˆ�|ÚÔ 9× ?Ñ ?ÀÕ MÝ-Ý(áØ"×7Ñ7àØ0°°¸lÈ=È/ð ZBð Bðô
ð 
ñr   c            
       óD   ‡ — e Zd ZdZdededz  dee   deddf
ˆ fd„Zˆ xZS )	ÚMCPMissingUserEnvVarsErrorzòRaised when an MCP request can't be built because the calling user has
    not supplied one or more required per-user environment variables.

    The error message is user-facing and includes a URL the user can visit
    to fill them in.
    r   r   NÚmissingÚ	setup_urlr   c                ó®   •— || _         || _        || _        || _        |xs |}dj	                  d„ |D «       «      }d|› d|› d|› �}t
        ‰| �  |«       y )Nú
c              3   ó&   K  — | ]	  }d |› �–— Œ y­w)z- Nr   ©rµ   r˜   s     r   r¸   z6MCPMissingUserEnvVarsError.__init__.<locals>.<genexpr>y  s   è ø€ Ò&G°t¨¨D¨6¤{Ñ&Gùs   ‚zCannot connect to MCP server "zt".

Your administrator configured this server to require per-user variables, but you haven't set the following yet:
z

Set your credentials here:
)r   r   rø   rù   rY   ÚsuperÚ__init__)	r   r   r   rø   rù   ÚlabelÚbullet_listÚmessageÚ	__class__s	           €r   rÿ   z#MCPMissingUserEnvVarsError.__init__l  sw   ø€ ð #ˆŒØ&ˆÔØˆŒØ"ˆŒØ"Ò/ iˆØ!ŸY™YÑ&G¸wÔ&GÓGˆà,¨U¨Gð 4Bð ˆmð +àˆkðð 	ô 	‰Ñ˜Õ!r   )r"   r#   r$   Ú__doc__r&   rã   rÿ   Ú__classcell__)r  s   @r   r÷   r÷   d  sL   ø„ ñð"ð ð"ð ˜4‘Zð	"ð
 �c‘ð"ð ð"ð 
÷"ñ "r   r÷   z\$\{([A-Za-z_][A-Za-z0-9_]*)\}Ú_ENV_VAR_PATTERNÚenv_varsc                 óž  — i }g }| s||fS | D ]»  }|€Œt        |d«      r|j                  «       }nt        |t        «      r|}nŒ7|j	                  d«      }t        |t
        «      r|sŒ[|j	                  d«      xs d}|dk(  r$|j                  ||j	                  d«      dœ«       Œ™|j	                  d«      }|€d	n
t        |«      ||<   Œ½ ||fS )
uÉ  Split admin-configured env var entries into globals and per-user specs.

    Accepts the raw value of ``MCPServer.env_vars`` (list of dicts or Pydantic
    models). Returns:

    - ``global_values``: ``{name: value}`` for entries with ``scope=="global"``.
    - ``user_specs``: list of ``{name, description}`` for entries with
      ``scope=="user"`` â€” these are the names the user must fill in.

    Unknown / malformed entries are skipped silently.
    Ú
model_dumpr˜   ÚscopeÚglobalÚuserÚdescription)r˜   r  r\   r;   )r”   r	  r‚   rƒ   rB   r&   rX   )r  Úglobal_valuesÚ
user_specsrE   Úentryr˜   r
  r\   s           r   Úparse_admin_env_varsr  Š  sß   € ð ,.€MØ.0€JÙØ˜jÐ(Ð(Øò FˆØˆ;ØÜ�3˜Ô%Ø*-¯.©.Ó*:‰EÜ˜œTÔ"Ø‰EàØ�y‰y˜Ó ˆÜ˜$¤Ô$©DØØ—	‘	˜'Ó"Ò. hˆØ�FŠ?Ø×Ñ t¸E¿I¹IÀmÓ<TÑUÕVà—I‘I˜gÓ&ˆEØ(-¨¡"¼3¸u»:ˆM˜$Òð#Fð$ ˜*Ð$Ð$r   r\   c                 óV   — | s
t        «       S t        t        j                  | «      «      S )zFReturn the set of ``${NAME}`` identifiers referenced inside ``value``.)r©   r  Úfindall)r\   s    r   Úfind_env_var_referencesr  ±  s#   € áÜ‹uˆÜÔ×'Ñ'¨Ó.Ó/Ð/r   Ústringsc                 óf   — t        «       }| D ]!  }t        |t        «      sŒ|t        |«      z  }Œ# |S )zDUnion of every ``${NAME}`` reference across a collection of strings.)r©   r‚   r&   r  )r  ÚrefsÚss      r   Úcollect_env_var_referencesr  ¸  s9   € ä“U€DØò /ˆÜ�aœÕØÔ+¨AÓ.Ñ.‰Dð/ð €Kr   Ú	variablesc                 óT   ‡— | s| S dddt         fˆfd„}t        j                  || «      S )zÎReplace ``${NAME}`` references in ``value`` with the matching mapping
    entry. Unknown names are left untouched so callers can detect them via
    ``find_env_var_references`` on the result if needed.
    ró   zre.Match[str]r   c                 óZ   •— | j                  d«      }|‰v r‰|   S | j                  d«      S )NrM   r   )Úgroup)ró   r˜   r  s     €r   Ú_subz"interpolate_env_vars.<locals>._subÉ  s0   ø€ Ø—k‘k !“nˆØ�9ÑØ˜T‘?Ð"Ø�{‰{˜1‹~Ðr   )r&   r  rp   )r\   r  r  s    ` r   Úinterpolate_env_varsr  Á  s5   ø€ ñ
 Øˆð�Oð ¬õ ô ×Ñ  eÓ,Ð,r   Úheadersc           	      ój   — | j                  «       D ��ci c]  \  }}|t        ||«      “Œ c}}S c c}}w )zVReturn a copy of ``headers`` with every value passed through ``interpolate_env_vars``.)rv   r  )r   r  rw   rx   s       r   Úinterpolate_headersr"  Ò  s-   € à>E¿m¹m»o×N±d°a¸ˆAÔ# A yÓ1Ñ1ÓNÐNùÓNs   ”/c                 ó”   — t         j                  j                  dd«      j                  d«      }dt	        | d¬«      › �}|r|› |› �S |S )zBThe frontend URL where a user can fill in their per-user env vars.ÚPROXY_BASE_URLr;   ú/z$/ui/?page=mcp-servers&fill_env_vars=)Úsafe)r@   rA   rB   Úrstripr   )r   ÚbaseÚpaths      r   Úbuild_env_var_setup_urlr*  ×  sK   € ä—*‘*—.‘.Ð!1°2Ó6×=Ñ=¸cÓB€DØ8¼¸yÈrÔ9RÐ8SÐT€DÙ"ˆdˆV�D�6ˆ?Ð,¨Ð,r   )Úextra_headersÚstatic_headersr+  r,  c           
      óF  — i }| rE|j                  | j                  «       D ��ci c]  \  }}t        |«      t        |«      “Œ c}}«       |rE|j                  |j                  «       D ��ci c]  \  }}t        |«      t        |«      “Œ c}}«       |xs dS c c}}w c c}}w )aå  Merge outbound HTTP headers for MCP calls.

    This is used when calling out to external MCP servers (or OpenAPI-based MCP tools).

    Merge rules:
    - Start with `extra_headers` (typically OAuth2-derived headers)
    - Overlay `static_headers` (user-configured per MCP server)

    If both contain the same key, `static_headers` wins. This matches the existing
    behavior in `MCPServerManager` where `server.static_headers` is applied after
    any caller-provided headers.
    N)rï   rv   r&   )r+  r,  Úmergedrw   rx   s        r   Úmerge_mcp_headersr/  Þ  s…   € ð" %'€FáØ�‰°-×2EÑ2EÓ2G×H©$¨!¨Q”s˜1“vœs 1›v‘~ÓHÔIáØ�‰°.×2FÑ2FÓ2H×I©$¨!¨Q”s˜1“vœs 1›v‘~ÓIÔJàŠ>�TÐùó Iùó Js   £B
Á*B
éd   Ú!MAX_STRUCTURED_CONTENT_SCAN_DEPTH.Úgroupsc                 ób   — t        | «      }t        d„ |D «       «      ryt        d„ |D «       «      S )zAConcatenate child leaf groups, propagating the too-deep sentinel.c              3   ó$   K  — | ]  }|d u –— Œ
 y ­wr   r   ©rµ   r  s     r   r¸   z'_flatten_leaf_groups.<locals>.<genexpr>  s   è ø€ Ò
3˜Uˆ5�DŒ=Ñ
3ùó   ‚Nc              3   ó4   K  — | ]  }|€Œ|D ]  }|–— Œ Œ y ­wr   r   )rµ   r  Úleafs      r   r¸   z'_flatten_leaf_groups.<locals>.<genexpr>	  s"   è ø€ ÒW˜%°5Ñ3DÐQVÒWÈ”ÐW�ÑWùó   ‚Š)rä   Úany)r2  Úmaterializeds     r   Ú_flatten_leaf_groupsr<    s/   € ô   ›-€LÜ
Ñ
3 lÔ
3Ô3ØÜÑW <ÔWÓWÐWr   r)  c                 ó  ‡— t        ‰«      t        kD  ryt        | t        «      r‰| ffS t        | t        «      r"t        ˆfd„| j                  «       D «       «      S t        | t        «      rt        ˆfd„t        | «      D «       «      S y)a   Depth-first, deterministically ordered string leaves of a JSON value.

    Returns ``None`` when the value is nested past ``MAX_STRUCTURED_CONTENT_SCAN_DEPTH``,
    so the caller blocks rather than letting deeper values through unscanned; an
    empty tuple means there was simply nothing to scan. A sentinel rather than an
    exception because this module is reloaded by tests (see the note above the
    environment-backed constants), which would give a custom exception class a new
    identity and let it escape a caller's ``except``.
    Nc              3   óF   •K  — | ]  \  }}t        |g ‰¢|‘­«      –— Œ y ­wr   ©Újson_string_leaves)rµ   r|   ræ   r)  s      €r   r¸   z%json_string_leaves.<locals>.<genexpr>  s&   øè ø€ Ò#jÉyÈsÐTXÔ$6°t¸\¸t¸\ÀS¹\×$JÑ#jùó   ƒ!c              3   óF   •K  — | ]  \  }}t        |g ‰¢|‘­«      –— Œ y ­wr   r?  )rµ   Úindexræ   r)  s      €r   r¸   z%json_string_leaves.<locals>.<genexpr>  s(   øè ø€ Ò#qÑQ\ÐQVÐX\Ô$6°t¸^¸t¸^ÀU¹^×$LÑ#qùrA  r   )	rW   r1  r‚   r&   rƒ   r<  rv   rã   Ú	enumerate)r\   r)  s    `r   r@  r@    sv   ø€ ô ˆ4ƒyÔ4Ò4ØÜ�%œÔØ�u�ÐÐÜ�%œÔÜ#Ó#jÐ\a×\gÑ\gÓ\iÔ#jÓjÐjÜ�%œÔÜ#Ó#qÔ`iÐjoÓ`pÔ#qÓqÐqØr   Úreplacementsc                 óf  — t        | t        «      r|j                  || «      S t        | t        «      r4| j	                  «       D ��ci c]  \  }}|t        ||g |¢|‘­«      “Œ c}}S t        | t        «      r.t        | «      D ��cg c]  \  }}t        ||g |¢|‘­«      ‘Œ c}}S | S c c}}w c c}}w )zBRebuild a JSON value with the guardrail's rewritten string leaves.)r‚   r&   rB   rƒ   rv   Úwith_json_string_leavesrã   rD  )r\   rE  r)  r|   ræ   rC  s         r   rG  rG  !  s«   € ô �%œÔØ×Ñ  eÓ,Ð,Ü�%œÔØ`e×`kÑ`kÓ`m×nÑS\ÐSVÐX\�Ô,¨T°<ÀÀ4ÀÈÁÓNÑNÓnÐnÜ�%œÔÜ_hÐinÓ_o×pÑP[ÐPUÐW[Ô'¨¨l¸N¸d¸NÀE¹NÕKÓpÐpØ€Lùó oùãps   ÁB'ÂB-Ú
path_depthc                 ó  ‡— ‰t         kD  ryt        | t        «      s| �t        | t        «      ryt        | t        t
        f«      rt        | «      fS t        | t        «      r\t        d„ | D «       «      }t        ˆfd„| j                  «       D «       «      }t        d„ |D «       «      ry|t        d„ |D «       «      z   S t        | t        «      r9t        ˆfd„| D «       «      }t        d„ |D «       «      ryt        d	„ |D «       «      S y)
aÄ  Strings in a JSON value that carry meaning but cannot be rewritten.

    Dictionary keys and non-string scalars: masking either would change the
    payload's contract rather than redact a value, so a caller scans these and
    blocks on a match instead of rewriting, matching what the content filter
    already does for MCP tool call arguments. ``None`` means the value is nested
    past the scan depth, same contract as ``json_string_leaves``.
    Nr   c              3   óB   K  — | ]  }t        |t        «      sŒ|–— Œ y ­wr   )r‚   r&   )rµ   r|   s     r   r¸   z+json_unrewritable_labels.<locals>.<genexpr>@  s   è ø€ ÒH 3´:¸cÄ3Õ3Gœ3ÑHùs   ‚˜c              3   ó<   •K  — | ]  }t        |‰d z   «      –— Œ y­w©rM   N©Újson_unrewritable_labels©rµ   ræ   rH  s     €r   r¸   z+json_unrewritable_labels.<locals>.<genexpr>A  s   øè ø€ ÒaÈ$Ô/°°jÀ1±n×EÑaùó   ƒc              3   ó$   K  — | ]  }|d u –— Œ
 y ­wr   r   r5  s     r   r¸   z+json_unrewritable_labels.<locals>.<genexpr>B  ó   è ø€ Ò1 ˆu˜Œ}Ñ1ùr6  c              3   ó4   K  — | ]  }|€Œ|D ]  }|–— Œ Œ y ­wr   r   ©rµ   r  r   s      r   r¸   z+json_unrewritable_labels.<locals>.<genexpr>D  s"   è ø€ Ò] U¸Ñ8IÐW\Ò]Èeœ5Ð]˜5Ñ]ùr9  c              3   ó<   •K  — | ]  }t        |‰d z   «      –— Œ y­wrL  rM  rO  s     €r   r¸   z+json_unrewritable_labels.<locals>.<genexpr>F  s   øè ø€ ÒXÈ$Ô/°°jÀ1±n×EÑXùrP  c              3   ó$   K  — | ]  }|d u –— Œ
 y ­wr   r   r5  s     r   r¸   z+json_unrewritable_labels.<locals>.<genexpr>G  rR  r6  c              3   ó4   K  — | ]  }|€Œ|D ]  }|–— Œ Œ y ­wr   r   rT  s      r   r¸   z+json_unrewritable_labels.<locals>.<genexpr>I  s"   è ø€ ÒW˜u°%Ñ2CÐQVÒWÈ”UÐW�UÑWùr9  )r1  r‚   rŒ   r&   rS   Úfloatrƒ   rä   Úvaluesr:  rã   )r\   rH  ÚownÚnesteds    `  r   rN  rN  0  sà   ø€ ð Ô5Ò5ØÜ�%œÔ % -´:¸eÄSÔ3IØÜ�%œ#œu˜Ô&Ü�E“
ˆ}ÐÜ�%œÔÜÑH¨%ÔHÓHˆÜÓaÐRW×R^ÑR^ÓR`ÔaÓaˆÜÑ1¨&Ô1Ô1ØØ”UÑ]¨fÔ]Ó]Ñ]Ð]Ü�%œÔÜÓXÐRWÔXÓXˆÜÑ1¨&Ô1Ô1ØÜÑW¨ÔWÓWÐWØr   c                 ó^   — t        | t        «      r| j                  d«      S t        | dd«      S )zNThe ``structuredContent`` of an MCP tool result, or ``None`` when it has none.ÚstructuredContentN)r‚   r   rB   r‹   )rÞ   s    r   Ú"mcp_tool_result_structured_contentr^  M  s,   € ä�&œ'Ô"Ø�z‰zÐ-Ó.Ð.Ü�6Ð.°Ó5Ð5r   c                 óš   — t        | t        «      r|| d<   yt        | d«      sy	 t        | d|«       y# t        t
        t        f$ r Y yw xY w)a2  Replace ``structuredContent`` in place; ``False`` when the shape does not carry it.

    In place for the same reason the content list is: the logging payload captured
    before the guardrail ran references this object, so a copy would leave the
    unmasked value in the spend log and the OTel span.
    r]  TF)r‚   r   r”   ÚsetattrÚAttributeErrorr†   rN   )rÞ   r\   s     r   Ú&set_mcp_tool_result_structured_contentrb  T  sX   € ô �&œ.Ô)Ø&+ˆÐ"Ñ#ØÜ�6Ð.Ô/ØðÜ�Ð+¨UÔ3ØøÜœI¤zÐ2ò Ùðús   ¥3 ³A
Á	A
>   ú
keep-aliveúcontent-lengthútransfer-encodingÚteÚtrailerÚupgradeÚ
connectionÚ_HOP_BY_HOP_HEADERS>   úcontent-typeúx-forwarded-forÚhostÚ#_SYNTHETIC_REQUEST_EXCLUDED_HEADERS)z	127.0.0.1i   Ú_SYNTHETIC_REQUEST_SERVERzx-mcp-Ú_MCP_SERVER_AUTH_HEADER_PREFIXc                  óX   — 	 ddl m}  | r| j                  d«      S dS # t        $ r Y yw xY w)z´``general_settings.litellm_key_header_name``, the deployment's custom header name for
    the proxy virtual key, so it is stripped from observability copies like the standard ones.r   ©Úgeneral_settingsNÚlitellm_key_header_name)Úlitellm.proxy.proxy_serverrs  rg   rB   rr  s    r   Ú_custom_litellm_key_header_namerv  |  s<   € ðÝ?ñ ?OÐ×ÑÐ 9Ó:ÐXÐTXÐXøô ò Ùðús   ‚ �	)¨)c                  óf   — ddl m}  	 | j                  «       S # t        $ r | j                  cY S w xY w)z®The header name the client passes the upstream MCP credential in, falling back to the
    default when ``general_settings`` is unavailable (the SDK, outside a running proxy).rM   ©ÚMCPRequestHandler)Úauth.user_api_key_auth_mcpry  Ú$get_mcp_client_side_auth_header_namerg   ÚLITELLM_MCP_AUTH_HEADER_NAMErx  s    r   Ú!_mcp_client_side_auth_header_namer}  †  s6   € õ >ð>Ø ×EÑEÓGÐGøÜò >Ø ×=Ñ=Ò=ð>ús   ˆ ˜0¯0c                  ó  — 	 ddl m}  | s
t        «       S | j	                  d«      }| j	                  d«      }t        |t        «      r|n|r|fnd}d„ |D «       }t        d„ |g|¢­D «       «      S # t        $ r t        «       cY S w xY w)a‰  Lowercased header names the deployment reads the caller's identity out of. A name here
    is a claim about who the caller is rather than a secret, and ``get_user_from_headers``
    resolves it off the request this module reconstructs, so dropping one would lose end user
    attribution on the MCP paths that leave ``end_user_id`` unset at connect time.

    ``user_header_mappings`` is accepted as a bare mapping as well as a list of them, matching
    ``get_internal_user_header_from_mapping`` and ``get_customer_user_header_from_mapping``.
    Iterating the bare form without normalizing yields its keys, which would silently exempt
    nothing.r   rr  Úuser_header_nameÚuser_header_mappingsr   c              3   ó`   K  — | ]&  }t        |t        «      sŒ|j                  d «      –— Œ( y­w)Úheader_nameN)r‚   r   rB   )rµ   Úmappings     r   r¸   z)_identity_header_names.<locals>.<genexpr>¤  s$   è ø€ Òh°GÌ:ÐV]Ô_fÕKg�W—[‘[ ×/Ñhùs   ‚.˜.c              3   ód   K  — | ](  }t        |t        «      sŒ|sŒ|j                  «       –— Œ* y ­wr   )r‚   r&   rD   rý   s     r   r¸   z)_identity_header_names.<locals>.<genexpr>¥  s$   è ø€ Òg dÌ
ÐSWÔY\ÕH]Òbf�T—Z‘Z—\Ñgùs   ‚0˜0›0)ru  rs  rg   Ú	frozensetrB   r‚   rã   )rs  Úuser_headerÚ
configuredÚmappingsÚmappeds        r   Ú_identity_header_namesrŠ  ‘  s�   € ðÝ?ñ Ü‹{ÐØ)×-Ñ-Ð.@ÓA€KØ(×,Ñ,Ð-CÓD€JÜ$.¨z¼4Ô$@‘jÑWaÀzÁmÐgi€HÙh¸xÔh€FÜÑg¨{Ð.D¸VÑ.DÔgÓgÐgøô ò Ü‹{Òðús   ‚A. Á.BÂBc                  óÒ   ‡— 	 ddl m}  t	        «       t        dh«      z  Št        ˆfd„| j                  «       j                  «       D «       «      S # t        $ r t        «       cY S w xY w)a¼  Lowercased header names that a configured MCP server forwards upstream through its
    ``extra_headers`` allowlist. The names are chosen by the admin, so no prefix rule can
    recognize them, and a caller supplied value under one of them is an upstream credential.

    ``authorization`` is left out because ``clean_headers`` already strips it, and claiming it
    here would change which header ``authenticated_with_header`` resolves to on the oauth
    passthrough config, which lists it in ``extra_headers`` by design. Identity headers are
    left out for the same reason: naming one in ``extra_headers`` forwards the caller's
    identity upstream, it does not turn that identity into a secret.rM   )Úglobal_mcp_server_managerÚauthorizationc              3   óŒ   •K  — | ];  }|j                   xs d D ]&  }|j                  «       ‰vr|j                  «       –— Œ( Œ= y­w)r   N)r+  rD   )rµ   rŸ   r˜   Úexempts      €r   r¸   z3_forwarded_upstream_header_names.<locals>.<genexpr>·  sM   øè ø€ ò àØ×)Ñ)Ò/¨Ròð Ø�:‰:‹<˜vÑ%ð 	�
‰
�ðØñùs   ƒAA)Úmcp_server_managerrŒ  rg   r…  rŠ  Úget_registryrY  )rŒ  r�  s    @r   Ú _forwarded_upstream_header_namesr’  ¨  sg   ø€ ðÝAô +Ó,¬y¸/Ð9JÓ/KÑK€FÜó à/×<Ñ<Ó>×EÑEÓGôó ð øô ò Ü‹{Òðús   ƒA ÁA&Á%A&Úheader_namesc                 ó  ‡‡‡— ddl m} t        |j                  j	                  «       |j
                  j	                  «       h«      Št        «       j	                  «       Št        «       Št        ˆˆˆfd„d„ | D «       D «       «      S )a«  Lowercased names of the headers in ``header_names`` that carry an upstream MCP
    credential rather than request context: the configured client side auth header, any
    header name a configured server forwards upstream via ``extra_headers``, and the
    per-server ``x-mcp-{alias}-{header}`` family. ``clean_headers`` only knows the
    credential headers of the chat completions path, so these are dropped on top of it.
    rM   rx  c              3   óf   •K  — | ](  }|‰k(  s|‰v s|j                  t        «      r|‰vr|–— Œ* y ­wr   )rÆ   rp  )rµ   r˜   Úclient_side_authÚforwarded_upstreamÚnon_credentials     €€€r   r¸   z/_upstream_credential_headers.<locals>.<genexpr>Ð  s?   øè ø€ ò àØÐ#Ò#ØÐ%Ñ%Ø�O‰OÔ:Ô;ÀÈNÑ@Zô	 	ñùs   ƒ.1c              3   ó<   K  — | ]  }|j                  «       –— Œ y ­wr   )rD   )rµ   Úraw_names     r   r¸   z/_upstream_credential_headers.<locals>.<genexpr>Ò  s   è ø€ ÒC¨(�X—^‘^×%ÑCùs   ‚)rz  ry  r…  ÚLITELLM_MCP_SERVERS_HEADER_NAMErD   Ú%LITELLM_MCP_ACCESS_GROUPS_HEADER_NAMEr}  r’  )r“  ry  r–  r—  r˜  s     @@@r   Ú_upstream_credential_headersr�  ¿  sx   ú€ õ >ä%à×=Ñ=×CÑCÓEØ×CÑC×IÑIÓKð	
ó€Nô @ÓA×GÑGÓIÐÜ @Ó BÐÜõ áC°lÔCôó ð r   )Úraw_headersÚ	client_iprž  rŸ  r   c                 óŽ  ‡— ddl m} t        «       }t        t	        |r|j                  «       nd«      z  |rt        |j                  «       h«      n	t        «       z  Št        ˆfd„|r|j                  «       ndD «       «      }|rd|j                  d«      ffnd} |dd| dt        d	d
dg|¢|¢­dœ|rd|dfi¥¬«      S i ¥¬«      S )a-  A synthetic FastAPI ``Request`` carrying the MCP connection's HTTP headers.

    The MCP protocol transports do not hand a per-call ``Request`` to the tool
    handlers, so one is reconstructed from the connection's ``raw_headers``. That
    lets ``add_litellm_data_to_request`` derive ``metadata.headers``,
    ``proxy_server_request``, header-based tags, guardrails and trace correlation
    exactly as on the chat completions path. Hop-by-hop headers describe the
    original HTTP framing rather than the logical request, so they are dropped, and
    ``x-forwarded-for`` comes from the resolved ``client_ip`` to avoid spoofing. ``host`` is
    dropped for the same reason: it is what ``Request.url`` is built from, so forwarding it
    would let a caller choose the URL every logging callback records. Upstream
    MCP credentials and the deployment's proxy key header, including a custom
    ``litellm_key_header_name``, are dropped so they cannot reach a callback or a guardrail
    through the derived metadata even when a caller omits ``general_settings``.
    r   r   r   c              3   ó°   •K  — | ]M  \  }}|j                  «       ‰vr6|j                  «       j                  d d¬«      |j                  dd¬«      f–— ŒO y­w)zlatin-1rl   )ÚerrorsrJ   N)rD   rQ   )rµ   r˜   r\   Úexcludeds      €r   r¸   z.build_synthetic_mcp_request.<locals>.<genexpr>ö  sX   øè ø€ ò ñ
 ˆD�%Ø�:‰:‹<˜xÑ'ð	 �J‰J‹L×Ñ 	°)ÐÓ<Ø�L‰L˜¨ˆLÓ3ô	
ñùs   ƒAAs   x-forwarded-forrJ   ÚhttpÚPOSTr   r;   )s   content-types   application/json)ÚtypeÚmethodr)  ÚschemerŸ   Úquery_stringÚ	root_pathr   Úclient)r
  )rÙ   r   rv  rn  r�  Úkeysr…  rD   rä   rv   rQ   ro  )r)  rž  rŸ  r   Úcustom_key_headerÚ	forwardedÚxffr£  s          @r   Úbuild_synthetic_mcp_requestr°  Ù  sÿ   ø€ õ*  ä>Ó@Ðä+Ü
&¹[ {×'7Ñ'7Ô'9ÈbÓ
Qñ	Rá5FŒ9Ð'×-Ñ-Ó/Ð0Ô1ÌIËKñ	Yð ô
 ó ñ
 4?˜K×-Ñ-Ô/ÀBôó €Iñ HQÐ% y×'7Ñ'7¸Ó'@ÐAÑCÐVX€CÙàØØØÜ/ØØØ>ÐQÀÐQÈSÑQñ

ñ .7�˜9 a˜.Ð)ð

ôð ð =?ð

ôð r   c                 ó2  — ddl m} ddlm}m}m} t        | r| j                  «       nd«      |z  t        dh«      z  } | || «      t        «       ¬«      } ||j                  «       D ��ci c]  \  }}|j                  «       |vsŒ||“Œ c}}«      S c c}}w )aS  The MCP request's client headers, sanitized the way the chat completions path
    sanitizes them before they reach a logging callback or a guardrail: proxy key
    headers stripped, including the custom key header name the deployment configured,
    upstream MCP credentials dropped, and credential-bearing values masked.

    Client-controlled behaviour flags (``litellm-disable-message-redaction``) are dropped
    too: these headers are read back out of the metadata to change proxy behaviour, so
    leaving one in place would let any MCP client turn off the redaction an admin
    configured. This path carries no key or team object to authorize an opt-out with, so
    it always strips them. ``host`` goes too, so that a caller cannot name the deployment in
    the guardrail payload and the spend row the way it could once name the request URL.r   )ÚHeaders)Ú'UNTRUSTED_REQUEST_HEADER_CONTROL_FIELDSÚclean_headersÚredact_credential_headersr   rm  )rt  )Ústarlette.datastructuresr²  Ú$litellm.proxy.litellm_pre_call_utilsr³  r´  rµ  r�  r¬  r…  rv  rv   rD   )	rž  r²  r³  r´  rµ  r£  Úcleanedr˜   r\   s	            r   Úlogging_safe_mcp_headersr¹    s™   € õ 1÷ñ ô 	%¹; [×%5Ñ%5Ô%7ÈBÓOØ
1ñ	2ä
�V�HÓ
ñ	ð ñ
 #Ù�ÓÜ ?Ó Aô€Gñ %ÀWÇ]Á]Ã_×%u±k°d¸EÐX\×XbÑXbÓXdÐltÒXt d¨E¡kÓ%uÓvÐvùÓ%us   Á+B
ÂB
)r   r   )F)r   )jr  rO   re   r„   r@   rq   ÚtypingÚcollections.abcr   r   r   r   r   r   ÚAbstractSetr	   r
   r   Úurllib.parser   Ú+litellm.types.mcp_server.mcp_server_managerr   ÚTYPE_CHECKINGrÙ   r   r   r(   rA   rB   r-   r,   r.   r/   r0   r2   r4   r6   r8   rŒ   rF   r&   rS   rb   rh   rm   Úcompilern   rs   rƒ   r}   r   Úobjectrˆ   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  r  r"  r*  r/  r1  ÚJSONLeafPathr<  r@  rG  rN  r^  rb  r…  rj  rn  ro  rp  rv  r}  rŠ  r’  r�  r°  r¹  r   r   r   ú<module>rÃ     sØ  ðòó Û Û Û 	Û 	Û ß XÕ XÝ .ß 'Ñ 'Ý å Aà	×ÒÝô.�Xô .ôI˜8ô Ið" "$§¡§¡Ð0IÐK_Ó!`Ð ˜Ó `Ø$+Ð ˜EÓ +Ø(*¯
©
¯©Ð7WÐYqÓ(rÐ  Ó rØ#%§:¡:§>¡>Ð2MÈsÓ#SÐ ˜5Ó SØ EÐ ˜Ó EðH '(Ð ˜eÓ 'ØZÐ �%Ó Zð !WÐ ˜Ó Vð=¨$ó =ñ$¨3ð $¸ð $ÀSó $ðB˜$ó ð) sð )¨só )ð '1 b§j¡j°Ó&?Ð ˜eÓ ?ð ¨ð  °ó  ð Ø"ò	Ø$ S¨#°°S¸#°X±Ñ*>Ð%>Ñ?ðð �‰:ðð �t‘ð	ð
 	ˆ4��S�‰>Ñ˜DÑ óð< *CÐ  Ó Bð 6ð ¨g°c¸6°kÑ.BÀTÑ.Ió ð?°&ð ?¸Tó ?ðP¨fð P¸ó Pð(Ð7Kð (ÐPTó (ðV Cð °cð ¸có ð˜fð ¨ó ð@  ~ð  ¸(À3¹-ó  ð<;¨cð ;¸9ð ;ÈÐRUÉó ;ð2 Cð 2¨Có 2ðP Sð P°)ð PÀHÈSÁMð PÐVYÐ\`ÑV`ó Pð*°ð ¸¸sÀC¸x¹ó ð" Cð ¸À#¹ð È5ÐQTÐVYÐQYÉ?Ð]aÑKaó ð*3 Cð 3°À$Ñ1Fð 3È3ó 3ð. 6:ñ Øð à& sÑ+¨dÑ2ð ð 
ó ñF+¨#ð +ÀTð +ÐVZó +ð,1°&ð 1¸SÀ4¹Zó 1ð&¨ð °OÀFÑ4KÈdÑ4Ró ð"3 ð 3¨3°©:ó 3ð& Vð °3ð ¸6ó ð $. 2§:¡:Ð.AÓ#BÐ ˜5Ó Bð¸7À3ÈÀ8Ñ;LÈtÑ;Sð ÐX\ó ôB" ô "ðF %˜"Ÿ*™*Ð%FÓGÐ �%Ó Gð$%Ø�s‰m˜dÑ"ð$%à
ˆ4��S�‰>˜4  S¨# X¡Ñ/Ð/Ñ0ó$%ðN0 3ð 0¨3¨s©8ó 0ð¨8°C©=ð ¸SÀ¹Xó ð- ð -°¸¸S¸Ñ0Að -Àcó -ð"O ¨¨c¨Ñ!2ð O¸wÀsÈCÀxÑ?Pð OÐUYÐZ]Ð_bÐZbÑUcó Oð
- sð -¨só -ð /3Ø/3òà˜3 ˜8Ñ$ tÑ+ðð ˜C ˜HÑ%¨Ñ,ðð 
ˆ#ˆsˆ(�^�dÑó	ð< ,/Ð ! 5Ó .ð �S˜3‘Y �^Ñ$€ðXØ�U˜5 ¨sÐ!2Ñ3°SÐ8Ñ9¸DÑ@ÑAðXà
ˆ5�˜sÐ"Ñ# SÐ(Ñ)¨DÑ0óXñ˜fð ¨Lð À%ÈÈlÐ\_ÐN_ÑH`ÐbeÐHeÑBfÐimÑBmó ð0 ñØðà˜,¨Ð+Ñ,ðð ðð ó	ñ Fð ¸ð ÀEÈ#ÈsÈ(ÁOÐVZÑDZó ð:6¨vð 6¸&ó 6ð°6ð À&ð ÈTó ñ& 'òó
Ð �Uó 
ð .AÁ9Ú/óDñ .Ð # Uó ð $7Ð ˜5Ó 6à(0Ð  Ó 0ðY¨¨t©ó Yð>¨3ó >ðh 	¨#¡ó hð.¨)°C©.ó ð.¨x¸©}ð ÀÈ3Áó ð: -1Ø ò	2à
ð2ð ˜˜c˜Ñ" TÑ)ð2ð �T‰zð	2ð
 ó2ðjw¨'°#°s°(Ñ*;¸dÑ*Bð wÀwÈsÐTWÈxÑGXô wr   