Source code for agentscope.mcp._stateful_client_base
# -*- coding: utf-8 -*-"""The base MCP stateful client class in AgentScope, that provides basic functionality for stateful MCP clients."""fromabcimportABCfromcontextlibimportAsyncExitStackfromtypingimportListimportmcpfrommcpimportClientSessionfrom._client_baseimportMCPClientBasefrom._mcp_functionimportMCPToolFunctionfrom.._loggingimportlogger
[docs]classStatefulClientBase(MCPClientBase,ABC):"""The base class for stateful MCP clients in AgentScope, which maintains the session state across multiple tool calls. The developers should use `connect()` and `close()` methods to manage the client lifecycle. """is_connected:bool"""If connected to the MCP server"""
[docs]def__init__(self,name:str)->None:"""Initialize the stateful MCP client. Args: name (`str`): The name to identify the MCP server, which should be unique across the MCP servers. """super().__init__(name=name)self.client=Noneself.stack=Noneself.session=Noneself.is_connected=False# Cache the tools to avoid fetching them multiple timesself._cached_tools=None
[docs]asyncdefconnect(self)->None:"""Connect to MCP server."""ifself.is_connected:raiseRuntimeError("The MCP server is already connected. Call close() ""before connecting again.",)self.stack=AsyncExitStack()try:context=awaitself.stack.enter_async_context(self.client,)read_stream,write_stream=context[0],context[1]self.session=ClientSession(read_stream,write_stream)awaitself.stack.enter_async_context(self.session)awaitself.session.initialize()self.is_connected=Truelogger.info("MCP client connected.")exceptException:awaitself.stack.aclose()self.stack=Noneraise
[docs]asyncdefclose(self)->None:"""Clean up the MCP client resources. You must call this method when your application is done."""ifnotself.is_connected:raiseRuntimeError("The MCP server is not connected. Call connect() before ""closing.",)try:ifself.stack:awaitself.stack.aclose()logger.info("MCP client closed.")finally:self.stack=Noneself.session=Noneself.is_connected=False
[docs]asyncdeflist_tools(self)->List[mcp.types.Tool]:"""Get all available tools from the server. Returns: `mcp.types.ListToolsResult`: A list of available MCP tools. """self._validate_connection()res=awaitself.session.list_tools()# Cache the tools for later useself._cached_tools=res.toolsreturnres.tools
[docs]asyncdefget_callable_function(self,func_name:str,wrap_tool_result:bool=True,)->MCPToolFunction:"""Get an async tool function from the MCP server by its name, so that you can call it directly, wrap it into your own function, or anyway you like. .. note:: Currently, only the text, image, and audio results are supported in this function. Args: func_name (`str`): The name of the tool function to get. wrap_tool_result (`bool`): Whether to wrap the tool result into agentscope's `ToolResponse` object. If `False`, the raw result type `mcp.types.CallToolResult` will be returned. Returns: `MCPToolFunction`: A callable async function that returns either `mcp.types.CallToolResult` or `ToolResponse` when called. """self._validate_connection()ifself._cached_toolsisNone:awaitself.list_tools()target_tool=Nonefortoolinself._cached_tools:iftool.name==func_name:target_tool=toolbreakiftarget_toolisNone:raiseValueError(f"Tool '{func_name}' not found in the MCP server",)returnMCPToolFunction(mcp_name=self.name,tool=target_tool,wrap_tool_result=wrap_tool_result,session=self.session,)
def_validate_connection(self)->None:"""Validate the connection to the MCP server."""ifnotself.is_connected:raiseRuntimeError("The connection is not established. Call connect() ""before using the client.",)ifnotself.session:raiseRuntimeError("The session is not initialized. Call connect() ""before using the client.",)