
     kh`                        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 d dl	m
Z
 d dlmZ d dlmZmZ d dlmZ d dlmZ d dlZd dlmZ d dlmZ d d	lmZ d d
lmZ d dlmZ d dl m!Z! d dl"m#Z# d dl$m%Z%m&Z& d dl'm(Z(m)Z) d dl*m+Z+m,Z,m-Z- d dl.m/Z/m0Z0mZ d dl1m2Z2 d dl3m4Z4 d dl5m6Z6 erd dl1m7Z7m8Z8 d dl3m9Z9 ejt                  jw                  d      xs   e< e ejz                               dz        Z> G d d      Z?y)    N)AsyncIteratorSequence)BytesIO)Path)TYPE_CHECKINGAny)unquote)types)Server)SseServerTransport)StreamableHTTPSessionManager)Image)	Starlette)Request)JSONResponseResponse)MountRoute)ReceiveScopeSend)processing_utilsroute_utilsutils)BlockFunction)State)FileData)BlockContextBlocks)	ComponentGRADIO_TEMP_DIRgradioc                   P   e Zd ZdZd(dZdedefdZdeeef   fdZ	d)dZ
defd	Zd
edededdfdZdeddfdZeded   dedefd       Zeded   dedefd       Z	 d*dedeeef   dz  deeeef   eeeez        f   fdZdefdZdeeef   deeeef   eeeez        f   fdZdedeeeez        defdZededej:                  dz  fd        Zed!ededz  fd"       Z ed#ej:                  d$edefd%       Z!ded&edee"jF                  e"jH                  z     fd'Z%y)+GradioMCPServerz
    A class for creating an MCP server around a Gradio app.

    Args:
        blocks: The Blocks app to create the MCP server for.
    c                 T   || _         | j                   j                         | _        | j                         | _        d| _        t        j                         }|r4|j                  d      d   dz   }t        j                  dd|      | _        nd| _        | j                         | _        | j                          t        | j                  dd      d	t         d
t"        dt$        dd ffd}t&        j(                  dt*        dt,        d    ffd       }|| _        | _        || _        y )N /_z[^a-zA-Z0-9]FT)appjson_response	statelessscopereceivesendreturnc                 H   K   j                  | ||       d {    y 7 wN)handle_request)r-   r.   r/   managers      F/opt/mcp/mcp-sentiment/venv/lib/python3.12/site-packages/gradio/mcp.pyhandle_streamable_httpz8GradioMCPServer.__init__.<locals>.handle_streamable_httpC   s!      ((>>>s   " "r*   c                   K   j                         4 d{    	 d 	 ddd      d{    y7 # w xY w7 # 1 d{  7  sw Y   yxY ww)z7Context manager for managing session manager lifecycle.N)run)r*   r4   s    r5   lifespanz*GradioMCPServer.__init__.<locals>.lifespanH   sR      {{}  	   	   sL   A5A>7>A<A9>AAAAA)blocksget_api_infoapi_infocreate_mcp_server
mcp_server	root_pathr   	get_spacesplitresubtool_prefixget_tool_to_endpointtool_to_endpointwarn_about_state_inputsr   r   r   r   
contextlibasynccontextmanagerr   r   r9   r4   r6   )selfr:   rD   r6   r9   r4   s        @r5   __init__zGradioMCPServer.__init__1   s   002002oo'%++C04s:K!vvosKHD!D $ 9 9 ;$$&.u
	?	?#*	?26	?	?
 
	'	'		 	mD.A 	 
(	 !&<#    requestr0   c                     t        j                  t        |j                              }|j	                  d      }t        |      j                  d      }|j                  d      ryy)z
        Gets the route path of the MCP server based on the incoming request.
        Can be different depending on whether the request is coming from the MCP SSE transport or the HTTP transport.
        N)queryr'   z/gradio_api/mcp/messagesz/gradio_api/mcp/http)httpxURLstrurl	copy_withrstripendswith)rJ   rM   rS   s      r5   get_route_pathzGradioMCPServer.get_route_pathU   sP    
 iiGKK()mm$m'#hooc"<<23-)rL   c                    i }| j                   d   j                         D ]  \  }}|d   s| j                  |      }||j                  ,t	        |j                  dd      xsL t        |j                  d      xr! t	        |j                  j                  dd      xs |j                  d      }| j                  |z   }||v r
|dz   }||v r
|||<    |S )z
        Gets all of the tools that are exposed by the Gradio app and also
        creates a mapping from the tool names to the endpoint names in the API docs.
        named_endpointsshow_apiN__name__	__class__r'   r)   )	r<   itemsget_block_fn_from_endpoint_namefngetattrhasattrr\   lstriprD   )rJ   rF   endpoint_nameendpoint_infoblock_fnfn_name	tool_names          r5   rE   z$GradioMCPServer.get_tool_to_endpointb   s    
 ,0MM:K,L,R,R,T 	<(M=Z(??N#x{{':HKKT: 1[9 M#HKK$9$9:tL1
 %++C0  !,,w6	#33 )CI  #33.; +!	<"  rL   Nc                     | j                   j                         D ]K  \  }}| j                  |      }|st        d |j                  D              s7t        j                  d       M y)z=
        Warn about tools that have gr.State inputs.
        c              3   <   K   | ]  }t        |t                y wr2   )
isinstancer   ).0inputs     r5   	<genexpr>z:GradioMCPServer.warn_about_state_inputs.<locals>.<genexpr>   s     VU
5% 8Vs   zThis MCP server includes a tool that has a gr.State input, which will not be updated between tool calls. The original, default value of the State will be used each time.N)rF   r]   r^   anyinputswarningswarn)rJ   r)   rc   re   s       r5   rG   z'GradioMCPServer.warn_about_state_inputs{   s\     !% 5 5 ; ; = 	A};;MJHCVhooVV&	rL   c           	      t    t        t         j                  j                  xs d            }|j	                         dt        dt
        t        t        f   dt        t        j                  t        j                  z     f fd       }|j                         dt        t        j                     f fd       }|S )z
        Create an MCP server for the given Gradio Blocks app.

        Parameters:
            blocks: The Blocks app to create the MCP server for.

        Returns:
            The MCP server.
        z
Gradio Appname	argumentsr0   c                   K   j                   j                  j                  }|t        d      j	                  |      }t        j                  ||j                        }j                  |       \  }}j                  ||      }j                  j                  |       }|t        d|        j                  |      }	|	J |j                  d   v r-j                  d   |   d   }
t        j                  |
d|      }ng }j!                  |	j"                  |      }j$                  j'                  |	||       d{   }j)                  |	j"                  |      }j+                  |d	   |      S 7 5w)
z
            Call a tool on the Gradio app.

            Args:
                name: The name of the tool to call.
                arguments: The arguments to pass to the tool.
            NzCould not find the request object in the MCP server context. This is not expected to happen. Please raise an issue: https://github.com/gradio-app/gradio.)rM   
route_pathr?   "Unknown tool for this Gradio app: rY   
parameters )re   ro   rM   data)r>   request_contextrM   
ValueErrorrW   r   get_root_urlr?   get_input_schemaconvert_strings_to_filedatarF   getr^   r<   client_utilsconstruct_argsinsert_empty_statero   r:   process_apipop_returned_statepostprocess_output_data)rs   rt   context_requestrv   root_urlr)   filedata_positionsprocessed_kwargsrc   re   parameters_infoprocessed_argsoutputrJ   s                r5   	call_toolz4GradioMCPServer.create_mcp_server.<locals>.call_tool   s     #oo==EEO&  p  ,,_=J"//'%..H
 %)$9$9$$?!A!#??-  !1155d;M$ #EdV!LMM;;MJH'''.? @@"&--0A"B="Q # ".!<!<#$" "$!44X__nUN;;22!%' 3  F
 "44X__nUN//vIIs   EE?E=6E?c                    K   g } 	j                   j                         D ]  \  }}	j                  |      }||j                  J t	        j
                  |j                        \  }}}|r-||j                  d      rdnddz   dj                  |      z   z  }	j                  ||      \  }}| j                  t        j                  |||              | S w)z;
            List all tools on the Gradio app.
            .r&   
 Returns: , rs   descriptioninputSchema)rF   r]   r^   r_   r   get_function_descriptionrV   joinr~   appendr
   Tool)
toolsrg   rc   re   r   rx   returnsschemar)   rJ   s
            r5   
list_toolsz5GradioMCPServer.create_mcp_server.<locals>.list_tools   s     
 E,0,A,A,G,G,I (	=??N+0GGG383Q3QKK40Z *33C8c&'))G,-K
 !11)ZH	JJ&$/$*( Ls   CC)r   rR   r:   titler   dictr   listr
   TextContentImageContentr   r   )rJ   serverr   r   s   `   r5   r=   z!GradioMCPServer.create_mcp_server   s     DKK--=>?				2	J2	J"&sCx.2	J%##e&8&8892	J 
2	Jh 
				$uzz"2 	 
	6 rL   r*   subpathr?   c                     d}t        |      | _         fd}|j                  |t        t	        d j
                        t	        d|      t        dj                        t        d j                        g             y	)
a!  
        Launch the MCP server on the SSE transport.

        Parameters:
            app: The Gradio app to mount the MCP server on.
            subpath: The subpath to mount the MCP server on. E.g. "/gradio_api/mcp"
            root_path: The root path of the Gradio Blocks app.
        z
/messages/c                   K   	 j                  | j                  | j                  | j                        4 d {   }j                  j                  |d   |d   j                  j                                d {    d d d       d {    t               S 7 a7  7 # 1 d {  7  sw Y   t               S xY w# t        $ r}t        dt        |               d }~ww xY ww)Nr      zMCP SSE connection error: )connect_sser-   r.   _sendr>   r8   create_initialization_optionsr   	ExceptionprintrR   )rM   streamserJ   sses      r5   
handle_ssez5GradioMCPServer.launch_mcp_on_sse.<locals>.handle_sse   s     ??MM7??GMM  //--

EEG     z!     z! 23q6(;<s   C)5C  BC  ?B"=B>B"C  B C  C)C  B" C  "B=(B+)B=0C  <C)=C   	C&	C!!C&&C)z/schema)endpointz/sse)r*   z/http/)routesN)	r   r?   mountr   r   get_complete_schemar   handle_post_messager6   )rJ   r*   r   r?   messages_pathr   r   s   `     @r5   launch_mcp_on_ssez!GradioMCPServer.launch_mcp_on_sse   s~     % /"	 			!!%!9!9 &:6,C,C,CD((C(CD
	
rL   rc   zBlockFunction | Nonec                 v    t        fd| j                  j                  j                         D        d      }|S )a$  
        Get the BlockFunction for a given endpoint name (e.g. "/predict").

        Parameters:
            endpoint_name: The name of the endpoint to get the BlockFunction for.

        Returns:
            The BlockFunction for the given endpoint name, or None if it is not found.
        c              3   ^   K   | ]$  }|j                   j                  d       k(  r| & yw)r'   N)api_namerb   )rk   r_   rc   s     r5   rm   zBGradioMCPServer.get_block_fn_from_endpoint_name.<locals>.<genexpr>  s0      ;;-"6"6s";; s   *-N)nextr:   fnsvalues)rJ   rc   re   s    ` r5   r^   z/GradioMCPServer.get_block_fn_from_endpoint_name  s9     ++//002
 
 rL   ro   zComponent | BlockContextrz   c                 r    t        |       D ](  \  }}t        |t              s|j                  |d       * |S )z
        Insert None placeholder values for any State input components, as State inputs
        are not included in the endpoint schema.
        N)	enumeraterj   r   insertro   rz   iinput_component_types       r5   r   z"GradioMCPServer.insert_empty_state(  s>     (1'8 	%#A#.6At$	% rL   c                 p    t        |       D ]'  \  }}t        |t              s|j                  |       ) |S )z
        Remove any values corresponding to State output components from the data
        as State outputs are not included in the endpoint schema.
        )r   rj   r   popr   s       r5   r   z"GradioMCPServer.pop_returned_state5  s;     (1'8 	#A#.6	 rL   rg   rx   c           
      X   | j                   j                  |      }|t        d|       | j                  d   }|j                  |      }|J d|d   D ci c]4  }|d   i |d   |r|d   |v r
d||d      ini d|v r|d   rd	|d   ini 6 c}d
}| j	                  |      S c c}w )a  
        Get the input schema of the Gradio app API, appropriately formatted for MCP.

        Parameters:
            tool_name: The name of the tool to get the schema for, e.g. "predict"
            parameters: The description and parameters of the tool to get the schema for.
        Returns:
            - The input schema of the Gradio app API.
            - A list of positions of FileData objects in the input schema.
        rw   rY   objectrx   parameter_nametyper   parameter_defaultdefault)r   
properties)rF   r   r|   r<   simplify_filedata_schema)rJ   rg   rx   rc   rY   rd   pr   s           r5   r~   z GradioMCPServer.get_input_schemaB  s    --11)< A)MNN--(9:'++M:(((  '|4  "# &i& &!,<*=*K '
15E3F(GH& /!3:M8N #A&9$:;& 
& ,,V44#s   9B'c                   K   | j                   st        i       S g }| j                  j                         D ]  \  }}| j	                  |      }||j
                  J t        j                  |j
                        \  }}}|r-||j                  d      rdnddz   dj                  |      z   z  }| j                  ||      \  }	}
|||	d}|j                  |        t        |      S w)a  
        Get the complete schema of the Gradio app API. For debugging purposes, also used by
        the Hugging Face MCP server to get the schema for MCP Spaces without needing to
        establish an SSE connection.

        Parameters:
            request: The Starlette request object.

        Returns:
            A JSONResponse containing a dictionary mapping tool names to their input schemas.
        r   r&   r   r   r   )r<   r   rF   r]   r^   r_   r   r   rV   r   r~   r   )rJ   rM   schemasrg   rc   re   r   rx   r   r   r)   infos               r5   r   z#GradioMCPServer.get_complete_schemam  s     }}##(,(=(=(C(C(E 	!$I};;MJH'HKK,CCC/4/M/M0,KW &//4R#"#ii()
 --iDIFA!*%D
 NN4 %	!( G$$s   C&C(r   c           	          dt         dt        t        t         f   dt        fd	 	 d	dt         dt        t        t
        z     dz  dt        t        t         f   dz  dt         ffdg  |      }|fS )
a  
        Parses a schema of a Gradio app API to identify positions of FileData objects. Replaces them with base64
        strings while keeping track of their positions so that they can be converted back to FileData objects
        later.

        Parameters:
            schema: The original schema of the Gradio app API.

        Returns:
            A tuple containing the simplified schema and the positions of the FileData objects.
        objdefsr0   c                 0   t        | t              syd| v r>| d   }|j                  d      r'|j                  d      d   }|j	                  |i       } ny| j	                  di       }|j	                  di       }d|v r>|d   }|j                  d      r'|j                  d      d   }|j	                  |i       }ny|j	                  di       j	                  di       }|j	                  d	i       j	                  d      }|j	                  d
      dk(  xs |dk(  S )NFz$refz#/$defs/r'   r(   r   meta_typer   constzgradio.FileData)rj   r   
startswithrA   r   )r   r   refkeypropsr   
type_fielddefault_types           r5   is_gradio_filedatazDGradioMCPServer.simplify_filedata_schema.<locals>.is_gradio_filedata  s   c4(}&k>>*-))C.,C((3+C GGL"-E99VR(D~6l>>*-))C.,C88C,D ,377DJ88Ir266w?Lw'+<< 5#44rL   Nnodepathc                    |g }|i }t        | t              rd| v r|j                  | d           
| |      rB	j                  |j	                                dD ]  }| j                  |d         d| d<   d| d<   i }d| v xr d| v }| j                         D ]H  \  }}|r|dk(  r |||      ||<   |j                  |        |||      ||<   |j                          J |S t        | t              rQg }t        |       D ]?  \  }}|j                  |       |j                   |||             |j                          A |S | S )N$defs)r   additional_descriptionr   stringr   za http or https url to a fileformatr   )	rj   r   updater   copyr   r]   r   r   )r   r   r   r   resultis_schema_rootvaluer   itemr   r   traverses            r5   r   z:GradioMCPServer.simplify_filedata_schema.<locals>.traverse  s]   
 ||$%d?KKW.%dD1&--diik:P ,d+,#+DL%DDN!'4!HLD4H"&**, #JC%#*=&.udD&AsC(&.udD&As
# D$'( GAtKKNMM(4t"<=HHJ KrL   )NN)r   r   rR   boolr   int)rJ   r   simplified_schemar   r   r   s      @@@r5   r   z(GradioMCPServer.simplify_filedata_schema  s    	C 	tCH~ 	$ 	@ ,0*.(	(	sSy/D((	 sCx.4'(	 	(	T 57$V, "444rL   r   r   c                 l    ddt         dt        t        t        z     dz  dt         ffd |      S )a'  
        Convert specific string values back to FileData objects based on their positions.
        This is used to convert string values (as base64 encoded strings) to FileData
        dictionaries so that they can be passed into .preprocess() logic of a Gradio app.

        Parameters:
            value: The input data to process, which can be an arbitrary nested data structure
                that may or may not contain strings that should be converted to FileData objects.
            filedata_positions: List of paths to positions in the input data that should be converted to FileData objects.

        Returns:
            The processed data with strings converted to FileData objects where appropriate. Base64
            encoded strings are first saved to a temporary file and then converted to a FileData object.

        Example:
            >>> convert_strings_to_filedata(
                {"image": "data:image/jpeg;base64,..."},
                [["image"]]
            )
            >>> {'image': FileData(path='<temporary file path>')},
        Nr   r   r0   c           
          |g }t        | t              r/| j                         D ci c]  \  }}| |||gz          c}}S t        | t              r)t	        |       D cg c]  \  }} |||gz          c}}S t        | t
              rd|v r`| j                  d      r$t        t        j                  | t                    S | j                  d      rt        |       S t        d|        | S c c}}w c c}}w )Nzdata:)r   )zhttp://zhttps://zSInvalid file data format, provide a url ('http://...' or 'https://...'). Received: )rj   r   r]   r   r   rR   r   r   r   save_base64_to_cacheDEFAULT_TEMP_DIRr|   )r   r   r   r   r   r   r   r   s         r5   r   z=GradioMCPServer.convert_strings_to_filedata.<locals>.traverse  s   |$%IM;E3C%66  D$'BKD/Rwq$tqcz2RRD#&43E+E??7+ $-BB "2 
 __%<=#..$mnrmst  K) Ss   C4#C:r2   )r   r   rR   r   )rJ   r   r   r   s     `@r5   r   z+GradioMCPServer.convert_strings_to_filedata  s6    2	3 	d39o&< 	 	6 rL   	file_pathc                    t         j                  j                  |       syt         j                  j                  | j	                               d   }|t        j                         vry	 t        j                  |       S # t        $ r Y yw xY w)ze
        If a filepath is a valid image, returns a PIL Image object. Otherwise returns None.
        Nr   )	osr   existssplitextlowerr   registered_extensionsopenr   )r   exts     r5   	get_imagezGradioMCPServer.get_image$  sq    
 ww~~i(ggy01!4e1133	::i(( 		s   )A> >	B
	B
	file_datac                     t        | t              ra| j                  d      x}rNt        |t              r=|j	                  d      r,t        |j                  dd      d         j                         S yy)z
        If a file_data is a valid FileDataDict with a url that is a data:image/svg+xml, returns bytes of the svg. Otherwise returns None.
        rS   zdata:image/svg,r   N)rj   r   r   rR   r   r	   rA   encode)r   rS   s     r5   get_svgzGradioMCPServer.get_svg3  sa    
 i&9==3G,GC,G#s#7G(Hsyya034;;==rL   imager   c                     t               }| j                  ||       t        j                  |j	                               j                  d      S )z?
        Returns a base64 encoded string of the image.
        )r   utf-8)r   savebase64	b64encodegetvaluedecode)r  r   buffers      r5   get_base64_datazGradioMCPServer.get_base64_data@  s>    
 

6&
) 1299'BBrL   r   c                 d   g }t        j                  ||d      }|D ]  }| j                  |      x}rt        j                  |      j                  d      }d}t        j                  ||d    t              }| d| }	t        j                  d||      t        j                  dd	|	 
      g}
nt        j                  |      r| j                  |d         x}rr|j                  xs d}| j                  ||      }d|j!                          }t        j                  d||      t        j                  dd|d   xs |d    
      g}
nMt        j                  dt#        |d   xs |d         
      g}
n!t        j                  dt#        |      
      g}
|j%                  |
        |S )z
        Postprocess the output data from the Gradio app to convert FileData objects back to base64 encoded strings.

        Parameters:
            data: The output data to postprocess.
        Nr  zimage/svg+xml	orig_namez/gradio_api/file=r  )r   rz   mimeTypetextzSVG Image URL: )r   r  r   pngzimage/zImage URL: rS   )r   add_root_urlr  r  r	  r  save_bytes_to_cacher   r
   r   r   r   is_file_obj_with_metar   r   r  r   rR   extend)rJ   rz   r   return_valuesr   	svg_bytesbase64_datamimetypesvg_pathsvg_urlreturn_valuer  image_formats                r5   r   z'GradioMCPServer.postprocess_output_dataI  s    ,,T8TB '	/F LL00y0$..y9@@I*+??&"5!69I &J&7zB&&$; %%#.wi8	  33F; NN6&>::5:#(<<#85L"&"6"6ul"KK!'(:(:(<'=>H**!({X ))!'#.ve}/Nv.O!P	$L ))!'c&-2Q6&>.R$L !& 1 1vCK PQ  .O'	/P rL   )r:   r   )r0   Nr2   )&r[   
__module____qualname____doc__rK   r   rR   rW   r   rE   rG   r   r=   r   r   r^   staticmethodr   r   r   r   tupler   r   r~   r   r   r   r   r   r   bytesr  r  r
   r   r   r   ry   rL   r5   r$   r$   )   se   "=H*g *# * d38n  2]6 ]~)
Y )
 )
 )
QU )
V 	, 
34
<@
	
 
 
34
<@
	
 
 -1)5)5 cNT))5 
tCH~tDsO44	5	)5V$%L $%LY538nY5	tCH~tDsO44	5Y5v44.24c	?.C4	4l S U[[4%7   
3 
54< 
 
 Cu{{ CC CC C C33#&3	e%"4"44	53rL   r$   )@r  rH   r   rB   tempfilerp   collections.abcr   r   ior   pathlibr   typingr   r   urllib.parser	   gradio_client.utilsr   r   rP   mcpr
   
mcp.serverr   mcp.server.sser   "mcp.server.streamable_http_managerr   PILr   starlette.applicationsr   starlette.requestsr   starlette.responsesr   r   starlette.routingr   r   starlette.typesr   r   r   r"   r   r   gradio.blocksr   gradio.componentsr   gradio.data_classesr   r   r   r    environr   rR   
gettempdirr   r$   ry   rL   r5   <module>r;     s      	 	   3   %   *    - K  , & 6 * 0 0 7 7 ' # (2+ ::>>"34 				(*9 
S	 S	rL   