
    ~h                        d Z ddlZddlZddlZddlmZ ddlmZmZm	Z	 ddl
Z
ddlZddlZddlmZ ddlmZ  ej"                  e      Zddd	d
ddddZd Z	 	 d&dededeeef   dededefdZdedeegef   deedz  gej4                  j6                  f   eedz  ge	eeej4                  j6                  f   f   z  defdZ G d de      Zedk(  rnddlZddlmZ ddlm Z   e  ed d!d"g#       e             5 Z! e"e!        e"e!d   jG                  d$             ddd       d% Z$ ejJ                   e$              yy# 1 sw Y   %xY w)'a;  This module implements the LangChain adapter.

LangChain tools support both sync and async functions for their tools so we can
leverage both in our implementation.

Example Usage:
>>> with MCPAdapt(StdioServerParameters(command="uv", args=["run", "src/echo.py"]), LangChainAdapter()) as tools:
>>>     print(tools)
    N)partial)AnyCallable	Coroutine)BaseTool)ToolAdapterstrfloatintdictlistboolNone)stringnumberintegerobjectarraybooleannullc                     | j                  dd      } t        j                  dd|       } | d   j                         rd|  } t	        j
                  |       r|  d} | S )z
    A function to sanitize function names to be used as a tool name.
    Prevent the use of dashes or other python keywords as function names by tool.
    -_z[^\w_] r   )replaceresubisdigitkeyword	iskeyword)names    V/opt/mcp/mcp-sentiment/venv/lib/python3.12/site-packages/mcpadapt/langchain_adapter.py_sanitize_function_namer#   %   sc     <<S!D 66)R&D Aw4&z qzK    r!   descriptioninput_schema
async_funcparse_docstringreturnc                 ^   t        j                  |      }|j                  di       }g }|j                         D ]  \  }}	d|	v rtt	        |	d   t
              rTg }
|	d   D ]   }|dk7  s	|
j                  t        |          " t        |
      dkD  rdj                  |
      }n||
r|
d   nd}nrt        |	d      }ned|	v r_g }
|	d   D ]+  }d|v s|d   dk7  s|
j                  t        |d             - t        |
      dkD  rdj                  |
      }n|
r|
d   nd}nd}|j                  | d	|         d
j                  |      }dd
j                  d |j                         D              z   dz   }d}d| d}|rd}d| d}|rdnd}d| d| d|  d| d| d| dj                         }|S )aZ  Generate a tool BaseTool class for `langchain` from MCP tool information.

    Note we use the simpliest '@tool' decorator for now.

    Args:
        name: the name of the tool as used in the MCP protocol
        description: the description of the tool as used in the MCP protocol
        input_schema: the input schema of the tool as used in the MCP protocol
        async_func: whether the function is async or not
        parse_docstring: whether to parse the docstring as a Google-Style docstring

    Returns:
        the generated langchain tool class as a string to be executed with exec.
    
propertiestyper      z | r   r	   anyOfz: z, {c              3   ,   K   | ]  }d | d|   yw)'z': N ).0ks     r"   	<genexpr>z'_generate_tool_class.<locals>.<genexpr>y   s     H1#S}Hs   }defzreturn func(z).content[0].textz	async defzreturn (await func(z)).content[0].textz@tool(parse_docstring=True)z@tool
 (z) -> str:
    """z"""
    )jsonrefreplace_refsgetitems
isinstancer   appendJSON_SCHEMA_TO_PYTHON_TYPESlenjoinkeysstrip)r!   r%   r&   r'   r(   resolved_json_schemar+   tool_paramsr4   vtypestpython_typeoptionargumentdef_statementreturn_statement	decoratorclass_templates                      r"   _generate_tool_classrR   ;   s!   * #//=%)),;J K  " 21Q;!F)T*6 EAF{%@%CDE u:>"'**U"3K.3%(K9!F)D\EG* NV#v&(@LL!<VF^!LMN 5zA~#jj/*/eAhU  KaS;-01?2B ))K(KTYYHjoo6GHHH3NH M%hZ/@A#0
:LM 2A-gI
 qa} %}  	
 
EG  r$   mcp_tool_namegenerate_class_templatefuncc                     t         j                  j                  |d}	 t         |d      |       ||    }|S # t        $ r)}dt        |      v rt         |d      |       Y d}~4d}~ww xY w)a  Instanciate a tool from a class template and a function wrapping the mcp tool_call.

    Args:
        mcp_tool_name: the name of the tool as used in the MCP protocol
        generate_class_template: a function that generates the class template
            (with or without parsing the docstring)
        func: the function wrapping the mcp tool_call

    Returns:
        the instanciated langchain tool
    )toolrU   Tz%Found invalid Google-Style docstring.FN)langchain_coretoolsrW   exec
ValueErrorr	   )rS   rT   rU   	namespaceerW   s         r"   _instanciate_toolr^      so    & (--22DAI<$T*I6 ]#DK  <2c!f<(/;<s   8 	A*A%%A*c                      e Zd ZdZ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)	LangChainAdaptera.  Adapter for `langchain`.

    Note that `langchain` support both sync and async tools so we
    write adapt for both methods.

    Warning: if the mcp tool name is a python keyword, starts with digits or contains
    dashes, the tool name will be sanitized to become a valid python function name.

    rU   Nmcp_toolr)   c                     t        |j                        }t        t        ||j                  |j
                  d      }t        |||      S )zAdapt a MCP tool to a LangChain tool.

        Args:
            func: The function to adapt.
            mcp_tool: The MCP tool to adapt.

        Returns:
            A LangChain tool.
        F)r#   r!   r   rR   r%   inputSchemar^   )selfrU   ra   rS   rT   s        r"   adaptzLangChainAdapter.adapt   sJ     0>")     #
 !0GNNr$   afuncc                     t        |j                        }||j                  k7  r%t        j                  d|j                   d|        t	        t
        ||j                  |j                  d      }t        |||      S )zAdapt a MCP tool to a LangChain tool.

        Args:
            afunc: The function to adapt.
            mcp_tool: The MCP tool to adapt.

        Returns:
            A LangChain tool.
        zMCP tool name z sanitized to T)	r#   r!   logwarningr   rR   r%   rc   r^   )rd   rf   ra   rS   rT   s        r"   async_adaptzLangChainAdapter.async_adapt   ss     0>HMM)KK.~m_UV")     #
 !0GOOr$   )__name__
__module____qualname____doc__r   r   mcprI   CallToolResultToolr   re   r   r   rj   r2   r$   r"   r`   r`      s    Ot}cii&>&>>?O ))..O 
	O2Pyc399;S;S1S'TTUP ))..P 
	Pr$   r`   __main__)StdioServerParameters)MCPAdaptuvrunsrc/echo.pycommandargshelloc                    K   t        t        dddg      t                     4 d {   } t        |        t        | d   j	                  d       d {          d d d       d {    y 7 E7 7 	# 1 d {  7  sw Y   y xY ww)Nru   rv   rw   rx   r   r{   )rt   rs   r`   printainvoke)rY   s    r"   mainr      s~     !$e]5KL
 	3 	3 %La((112	3 	3 	3
 2	3 	3 	3 	3sV   &B	A.B	(A4A0
A4B	(A2)B	0A42B	4B:A=;BB	)FT)&rn   r   loggingr   	functoolsr   typingr   r   r   r;   rX   ro   langchain.toolsr   mcpadapt.corer   	getLoggerrk   rh   rA   r#   r	   r   r   rR   rI   rp   r^   r`   asynciors   rt   rY   r}   invoker   rv   r2   r$   r"   <module>r      s     	  + +   
 $ %g!  4  Q
QQ sCx.Q 	Q
 Q 	Qh%tfck2 D4K=#))":"::
;t}iS#))2J2J(JKKLM
 >=P{ =P@ z)&	d%1GH
 ( 
eeAhoog&'(3 GKK- ( (s   ;#EE