
    ~h7                     r   d Z ddlZddlZddlmZm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mZm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  G d de      Ze	 d deeeef   z  dee
z  dz  deeee ejB                  jD                     f   df   fd       Z# G d d      Z$e%dk(  r G d de      Z& e$ edddg       edddg      g e&             5 Z' e(e'        e( e'd   ddi              e( e'd   ddi             ddd       d Z) ejT                   e)              yy# 1 sw Y   %xY w)!zCore module for the MCPAdapt library.

This module contains the core functionality for the MCPAdapt library. It provides the
basic interfaces and classes for adapting tools from MCP to the desired Agent framework.
    N)ABCabstractmethod)AsyncExitStackasynccontextmanager)	timedelta)partial)AnyAsyncGeneratorCallable	Coroutine)ClientSessionStdioServerParameters)
sse_client)stdio_client)streamablehttp_clientc                      e Zd ZdZedeedz  gej                  j                  f   dej                  j                  defd       Zdeedz  geeeej                  j                  f   f   dej                  j                  defdZy)	ToolAdapterzMA basic interface for adapting tools from MCP to the desired Agent framework.funcNmcp_toolreturnc                      y)ak  Adapt a single tool from MCP to the desired Agent framework.

        The MCP protocol will provide a name, description and inputSchema in JSON Schema
        format. This needs to be adapted to the desired Agent framework.

        Note that the function is synchronous (not a coroutine) you can use
        :meth:`ToolAdapter.async_adapt` if you need to use the tool asynchronously.

        Args:
            func: The function to be called (will call the tool via the MCP protocol).
            mcp_tool: The tool to adapt.

        Returns:
            The adapted tool in the agentic framework of choice.
        N selfr   r   s      I/opt/mcp/mcp-sentiment/venv/lib/python3.12/site-packages/mcpadapt/core.pyadaptzToolAdapter.adapt   s    * 	    afuncc                     t        d      )a9  Adapt a single tool from MCP to the desired Agent framework.

        The MCP protocol will provide a name, description and inputSchema in JSON Schema
        format. This needs to be adapted to the desired Agent framework.

        Note that the function is asynchronous (a coroutine) you can use
        :meth:`ToolAdapter.adapt` if you need to use the tool synchronously.

        Args:
            afunc: The coroutine to be called.
            mcp_tool: The tool to adapt.

        Returns:
            The adapted tool in the agentic framework of choice.
        z;Async adaptation is not supported for this Agent framework.)NotImplementedErrorr   r   r   s      r   async_adaptzToolAdapter.async_adapt0   s    ( "I
 	
r   )__name__
__module____qualname____doc__r   r   dictmcptypesCallToolResultToolr	   r   r   r"   r   r   r   r   r      s    Wt}cii&>&>>? )).. 
	 ,
yc399;S;S1S'TTU
 ))..
 
	
r   r   serverparamsclient_session_timeout_secondsr   c                  K   t        | t              rt        |       }nkt        | t              rC| j	                  dd      }|dk(  rt        d	i | }n8|dk(  rt        d	i | }n't        d| d      t        dt        |        d      d}t        |t              rt        |      }nt        |t              r|}|4 d{   ^}}}t        |||      4 d{   }|j                          d{    |j                          d{   }	||	j                  f ddd      d{    ddd      d{    y7 7 h7 R7 <7 # 1 d{  7  sw Y   -xY w7 $# 1 d{  7  sw Y   yxY ww)
a/  Async context manager that yields tools from an MCP server.

    Note: the session can be then used to call tools on the MCP server but it's async.
    Use MCPAdapt instead if you need to use the tools synchronously.

    Args:
        serverparams: Parameters passed to either the stdio client or sse client.
            * if StdioServerParameters, run the MCP server using the stdio protocol.
            * if dict, assume the dict corresponds to parameters to an sse MCP server.
        client_session_timeout_seconds: Timeout for MCP ClientSession calls

    Yields:
        A tuple of (MCP Client Session, list of MCP tools) available on the MCP server.

    Usage:
    >>> async with mcptools(StdioServerParameters(command="uv", args=["run", "src/echo.py"])) as (session, tools):
    >>>     print(tools)
    	transportssezstreamable-httpz:Invalid transport, expected sse or streamable-http found ``zDInvalid serverparams, expected StdioServerParameters or dict found `N)secondsr   )
isinstancer   r   r'   popr   r   
ValueErrortypefloatr   r   
initialize
list_toolstools)
r,   r-   clientr/   timeoutreadwrite_sessionr:   s
             r   mcptoolsrA   I   s    . , 56l+	L$	' $$[%8	/,/F++*:\:FLYKWXY  RSWXdSeRffgh
 	
 G0%8$BC	2I	>0 	' 	'*$ 
 	' 	' $$&&&!,,..E5;;&&	' 	'	' 	' 	'	' '.	' 	' 	' 	'	' 	' 	' 	's   B?E7EE7E"EE"E2E3E
EE E"+E	,E"0E7;E <E7E"EE	E"E	EE	E" E7"E4(E+)E40E7c            
           e Zd ZdZ	 	 ddeeeef   z  eeeeef   z     z  de	de
deez  dz  fdZd Zd	ee   fd
Zd Zd Zd Zd Zd	ee   fdZd	ee   fdZd Zy)MCPAdapta.  The main class for adapting MCP tools to the desired Agent framework.

    This class can be used either as a sync or async context manager.

    If running synchronously, it will run the MCP server in a separate thread and take
    care of making the tools synchronous without blocking the server.

    If running asynchronously, it will use the async context manager and return async
    tools.

    Dependening on what your Agent framework supports choose the approriate method. If
    async is supported it is recommended.

    Important Note: adapters need to implement the async_adapt method to support async
    tools.

    Usage:
    >>> # sync usage
    >>> with MCPAdapt(StdioServerParameters(command="uv", args=["run", "src/echo.py"]), SmolAgentAdapter()) as tools:
    >>>     print(tools)

    >>> # sync usage by start ... close pattern
    >>> adapter = MCPAdapt(StdioServerParameters(command="uv", args=["run", "src/echo.py"]), SmolAgentAdapter())
    >>> adapter.start()
    >>> print(adapter.tools()) # get latest tools
    >>> adapter.close()

    >>> # sync usage with streamable-http
    >>> with MCPAdapt({"url": "http://127.0.0.1:8000/mcp", "transport": "streamable-http"}), SmolAgentAdapter()) as tools:
    >>>     print(tools)

    >>> # async usage
    >>> async with MCPAdapt(StdioServerParameters(command="uv", args=["run", "src/echo.py"]), SmolAgentAdapter()) as tools:
    >>>     print(tools)

    >>> # async usage with sse
    >>> async with MCPAdapt({"url": "http://127.0.0.1:8000/sse"}, SmolAgentAdapter()) as tools:
    >>>     print(tools)
    r,   adapterconnect_timeoutr-   Nc                 H   t        |t              r|| _        n|g| _        || _        g | _        g | _        t        j                         | _        d| _	        t        j                         | _        t        j                  | j                  d      | _        || _        || _        y)a  
        Manage the MCP server / client lifecycle and expose tools adapted with the adapter.

        Args:
            serverparams (StdioServerParameters | dict[str, Any] | list[StdioServerParameters | dict[str, Any]]):
                MCP server parameters (stdio or sse). Can be a list if you want to connect multiple MCPs at once.
            adapter (ToolAdapter): Adapter to use to convert MCP tools call into agentic framework tools.
            connect_timeout (int): Connection timeout in seconds to the mcp server (default is 30s).
            client_session_timeout_seconds: Timeout for MCP ClientSession calls

        Raises:
            TimeoutError: When the connection to the mcp server time out.
        NT)targetdaemon)r3   listr,   rD   sessions	mcp_toolsasyncionew_event_looplooptask	threadingEventreadyThread	_run_loopthreadrE   r-   )r   r,   rD   rE   r-   s        r   __init__zMCPAdapt.__init__   s    . lD) ,D!-D .057 **,		__&
&&dnnTJ..L+r   c                     t        j                   j                          fd} j                  j                   |              _        	  j                  j                   j                         y# t         j                  $ r Y yw xY w)zARuns the event loop in a separate thread (for synchronous usage).c            
      
  K   t               4 d {   } j                  D cg c]/  }| j                  t        |j                               d {   1 }}t        | D cg c]  }t        |       c}\  _        _        j                  j                          t        j                         j                          d {    d d d       d {    y 7 7 c c}w c c}w 7 #7 # 1 d {  7  sw Y   y xY wwN)r   r,   enter_async_contextrA   r-   ziprI   rJ   rK   rR   setrL   rQ   wait)stackparamsconnectionscr   s       r   setupz!MCPAdapt._run_loop.<locals>.setup   s     %' 	- 	-5
 #'"3"3	   33 )L)LM    CF{BS0TQa0T-t~

 mmo**,,,	- 	- 	- 1U,	- 	- 	- 	-s   DCDC.-C CC C.%C%7AC.C*C.DC,DC  C.,D.D 4C75D <DN)rL   set_event_looprN   create_taskrO   run_until_completeCancelledError)r   rb   s   ` r   rT   zMCPAdapt._run_loop   sg    tyy)
	- II))%'2		II((3%% 		s   %A2 2BBr   c                      j                   st        d      	 d
dt        dt        dz  dt        j
                  j                  f fd}g }dt        dt        t        j
                  j                     fd} j                   D ]R  }|j                  t        j                   ||       j                        j                   j                  	      g       T | _        t#         j                    j                         D cg c]=  \  }}|D ]3  } j$                  j'                  t)        |||j*                        |      5 ? c}}}S c c}}}w )a  Returns the tools from the MCP server adapted to the desired Agent framework.

        This is what is yielded if used as a context manager otherwise you can access it
        directly via this method.

        Only use this when you start the client in synchronous context or by :meth:`start`.

        An equivalent async method is available if your Agent framework supports it:
        see :meth:`atools`.

        zSession not initializedNname	argumentsr   c                     t        j                  | j                  ||      j                        j	                         S rY   )rL   run_coroutine_threadsafe	call_toolrN   result)r@   rh   ri   r   s      r   _sync_call_toolz'MCPAdapt.tools.<locals>._sync_call_tool   s5     33!!$	2DIIfhr   r@   c                 R   K   | j                          d {   j                  S 7 wrY   )r9   r:   )r@   s    r   _list_toolsz#MCPAdapt.tools.<locals>._list_tools  s!     !,,..555.s   '%'r<   rY   )rJ   RuntimeErrorstrr'   r(   r)   r*   r   rI   r+   extendrL   rk   rN   rm   rE   rK   r[   rD   r   r   rh   )r   rn   rK   rp   r@   r:   tools   `      r   r:   zMCPAdapt.tools   s;    }}899 :>		+/$;	YY%%	 13		6} 	6ciinn9M 	6 }} 	G44#G,diifT%9%9f:	 # #&dmmT^^"D
 

  LLwKTR
R
 	
 
s   AE
c                     | j                   j                          | j                  j                  | j                        st        d| j                   d      y)z(Start the client in synchronous context.rq   z)Couldn't connect to the MCP server after z secondsN)rU   startrR   r]   rE   TimeoutErrorr   s    r   rw   zMCPAdapt.start  sR     zzt';';<;D<P<P;QQYZ  =r   c                    | j                   rI| j                   j                         s/| j                  j                  | j                   j                         | j
                  j                          | j                  j                          y)z'Clean up resources and stop the client.N)rO   donerN   call_soon_threadsafecancelrU   joinclosery   s    r   r   zMCPAdapt.close$  sP    99TYY^^-II**499+;+;<		r   c                 B    | j                          | j                         S rY   )rw   r:   ry   s    r   	__enter__zMCPAdapt.__enter__+  s    

zz|r   c                 $    | j                          y rY   )r   r   exc_typeexc_valexc_tbs       r   __exit__zMCPAdapt.__exit__/  s    

r   c                   K   | j                   D cg c]$  }|j                          d{   j                  & c}| _        t	        | j                   | j                        D cg c]F  \  }}|D ]<  }| j
                  j                  t        |j                  |j                        |      > H c}}}S 7 c c}w c c}}}w w)a  Returns the tools from the MCP server adapted to the desired Agent framework.

        This is what is yielded if used as an async context manager otherwise you can
        access it directly via this method.

        Only use this when you start the client in asynchronous context.

        An equivalent sync method is available if your Agent framework supports it:
        see :meth:`tools`.
        N)
rJ   r9   r:   rK   r[   rD   r"   r   rl   rh   )r   sr@   r:   ru   s        r   atoolszMCPAdapt.atools3  s      AEN1/66N #&dmmT^^"D
 

  LL$$WW->->		%JDQ
Q
 	
 0N
s2   CB8B6
B8,C&AB=1C6B88Cc           	      h  K   t               | _        | j                  D cg c]9  }| j                  j                  t	        || j
                               d {   ; }}t        | D cg c]  }t        |       c}\  | _        | _	        | j                          d {   S 7 Lc c}w c c}w 7 wrY   )r   _ctxmanagerr,   rZ   rA   r-   r[   rI   rJ   rK   r   )r   r_   r`   ra   s       r   
__aenter__zMCPAdapt.__aenter__G  s     )+ ++	
  ""66!D!DE  
 
 ;>{:K(LQa(L%t~[[]""
 )M"s@   B27B&B$
B&B2*B+<#B2B0 B2$B&&B2c                 Z   K   | j                   j                  |||       d {    y 7 wrY   )r   	__aexit__r   s       r   r   zMCPAdapt.__aexit__U  s$     ((7FCCCs   !+)+)      )r#   r$   r%   r&   r   r'   rs   r	   rI   r   intr7   r   rV   rT   r:   rw   r   r   r   r   r   r   r   r   r   rC   rC      s    &\  "CD*M+
sCx.
$tCH~5
67*M
 *M *M ).	(9D(@*MX,*
tCy *
X
d3i 
(#$s) #Dr   rC   __main__c                       e Zd Zdeedz  gej                  j                  f   dej                  j                  fdZ	deedz  ge
eeej                  j                  f   f   dej                  j                  fdZy)DummyAdapterr   Nr   c                     |S rY   r   r   s      r   r   zDummyAdapter.adapt\  s	    
 Kr   r   c                     |S rY   r   r!   s      r   r"   zDummyAdapter.async_adaptc  s	     Lr   )r#   r$   r%   r   r'   r(   r)   r*   r+   r   r   r	   r"   r   r   r   r   r   [  s    	D4K=#))*B*BBC	 iinn		yc3993K3K)KLL	
 iinn	r   r   uvrunsrc/echo.pycommandargstexthello   worldc                  f  K   t        t        dddg      t        dddg      gt                     4 d {   } t        |        t         | d   ddi       d {          t         | d   dd	i       d {          d d d       d {    y 7 \7 97 7 # 1 d {  7  sw Y   y xY ww)
Nr   r   r   r   r   r   r   r   r   )rC   r   r   print)dummy_toolss    r   mainr   w  s     %d%9OP%d%9OP N
 		; 		; +A'899:A'899:		; 		; 		; :9		; 		; 		; 		;sh   5B1BB1!BB
B:B
;BB1BB1BBB1B."B%#B.*B1)r   )+r&   rL   rP   abcr   r   
contextlibr   r   datetimer   	functoolsr   typingr	   r
   r   r   r(   r   r   mcp.client.sser   mcp.client.stdior   mcp.client.streamable_httpr   r   r'   rs   r7   tuplerI   r)   r+   rA   rC   r#   r   r   r   r   r   r   r   r   <module>r      sv     # :   ; ; 
 4 % ) <0
# 0
f  @A6''$sCx.86'$)I$5$<6' E-ciinn)==>DE6' 6'rSD SDl z{ " 
!$e]5KL!$e]5KL	
 	
 	1 
knk!nfg./0nk!nfg./0	1
; GKKU &	1 	1s   /D--D6