monoai.mcp

MCP (Model Context Protocol) is a protocol for connecting AI Agents to external tools and services.

1"""
2MCP (Model Context Protocol) is a protocol for connecting AI Agents to external tools and services.
3"""
4
5from .mcp_server import McpServer
6
7__all__ = ["McpServer"]
class McpServer:
 12class McpServer:
 13    """MCP (Model Context Protocol) Server client for connecting to external tools.
 14    
 15    This class provides a unified interface for connecting to MCP servers using
 16    different transport protocols (HTTP/SSE, stdio). It handles connection
 17    management, tool discovery, and tool execution in both synchronous and
 18    asynchronous contexts.
 19    
 20    The MCP server allows AI agents to access external tools and services
 21    through a standardized protocol, enabling dynamic tool discovery and
 22    execution without hardcoded integrations.
 23        
 24    Examples
 25    --------
 26    >>> # HTTP/SSE server
 27    >>> server = McpServer(
 28    ...     name="coingecko",
 29    ...     server_type="http",
 30    ...     server_url="https://mcp.api.coingecko.com/sse"
 31    ... )
 32    >>> tools = server.get_tools()
 33    >>> result = server.call_tool("get_price", {"coin": "bitcoin"})
 34    
 35    >>> # Stdio server
 36    >>> server = McpServer(
 37    ...     name="local_tools",
 38    ...     server_type="python",
 39    ...     server_args=["python", "tools_server.py"]
 40    ... )
 41    >>> tools = server.get_tools()
 42    """
 43    
 44    def __init__(
 45        self,
 46        name: str,
 47        server_type: str,               # "http" | "node" | "python"
 48        server_args: list | None = None,
 49        server_url: str | None = None,
 50        env: dict | None = None,
 51        headers: dict | None = None,    # <-- per HTTP/SSE
 52        connect_timeout: float = 15.0,  # opzionale
 53    ):
 54        """Initialize the MCP server client.
 55        
 56        Parameters
 57        ----------
 58        name : str
 59            Unique identifier for this MCP server instance
 60        server_type : str
 61            Type of server connection ("http", "node", "python")
 62        server_args : list, optional
 63            Command line arguments for stdio-based servers
 64        server_url : str, optional
 65            URL for HTTP/SSE based servers
 66        env : dict, optional
 67            Environment variables for stdio-based servers
 68        headers : dict, optional
 69            HTTP headers for HTTP/SSE based servers
 70        connect_timeout : float, optional
 71            Connection timeout in seconds (default: 15.0)
 72        """
 73        self.name = name
 74        self._server_type = server_type
 75        self._server_args = server_args or []
 76        self._server_url = server_url
 77        self._env = env or {}
 78        self._headers = headers or {}
 79
 80    def _get_client(self):
 81        """Get the appropriate MCP client for the server type.
 82        
 83        Returns
 84        -------
 85        async generator
 86            MCP client context manager for the specified server type
 87            
 88        Raises
 89        ------
 90        ValueError
 91            If server_type is "http" but server_url is not provided
 92        """
 93        if self._server_type == "http":
 94            if not self._server_url:
 95                raise ValueError("URL mancante per server_type='http'")
 96            
 97            if self._server_url.endswith("/mcp"):
 98                return streamablehttp_client(self._server_url, headers=self._headers)
 99            else:
100                return sse_client(self._server_url, headers=self._headers)
101        else:
102            server_params = StdioServerParameters(
103                command="npx" if self._server_type == "node" else "python",
104                args=self._server_args,
105                env=self._env,
106                stderr="pipe",
107            )
108            
109            return stdio_client(server_params)
110
111    async def get_tools_async(self):
112        """Asynchronously retrieve available tools from the MCP server.
113        
114        Returns
115        -------
116        list
117            List of available tools with their schemas and descriptions
118            
119        Raises
120        ------
121        RuntimeError
122            If unable to connect to the server or retrieve tools
123        """
124        async with self._get_client() as (read, write):
125            async with ClientSession(read, write) as session:
126                resp = await session.list_tools()
127                return resp.tools
128
129    def get_tools(self):
130        """Synchronously retrieve available tools from the MCP server.
131        
132        This is a synchronous wrapper around the async method that runs
133        the operation in a new event loop.
134        
135        Returns
136        -------
137        list
138            List of available tools with their schemas and descriptions
139            
140        Raises
141        ------
142        RuntimeError
143            If unable to connect to the server or retrieve tools
144        """
145        return asyncio.run(self.get_tools_async())
146
147    async def call_tool_async(self, name: str, args: dict):
148        """Asynchronously execute a tool on the MCP server.
149        
150        Parameters
151        ----------
152        name : str
153            Name of the tool to execute. Can include MCP prefix (mcp_servername_toolname)
154            or just the tool name
155        args : dict
156            Arguments to pass to the tool
157            
158        Returns
159        -------
160        list
161            Tool execution result, formatted as a list of content items
162            
163        Raises
164        ------
165        RuntimeError
166            If unable to connect to the server or execute the tool
167        ValueError
168            If the tool name format is invalid
169        """
170        async with self._get_client() as (read, write):
171            async with ClientSession(read, write) as session:
172                prefix = f"mcp_{self.name}_"
173                tool_name = name[len(prefix):] if name.startswith(prefix) else name
174                result = await session.call_tool(tool_name, args)
175                                    
176                # Handle different response formats
177                if hasattr(result, 'content'):
178                    if isinstance(result.content, list) and len(result.content) > 0:
179                        if hasattr(result.content[0], 'text'):
180                            content = json.loads(result.content[0].text)
181                        else:
182                            content = str(result.content[0])
183                    else:
184                        content = str(result.content)
185                else:
186                    content = str(result)
187
188                if isinstance(content, dict):
189                    content = [content]
190                return content
191
192    def call_tool(self, name: str, args: dict):
193        """Synchronously execute a tool on the MCP server.
194        
195        This is a synchronous wrapper around the async method that runs
196        the operation in a new event loop.
197        
198        Parameters
199        ----------
200        name : str
201            Name of the tool to execute. Can include MCP prefix (mcp_servername_toolname)
202            or just the tool name
203        args : dict
204            Arguments to pass to the tool
205            
206        Returns
207        -------
208        list
209            Tool execution result, formatted as a list of content items
210            
211        Raises
212        ------
213        RuntimeError
214            If unable to connect to the server or execute the tool
215        ValueError
216            If the tool name format is invalid
217        """
218        return asyncio.run(self.call_tool_async(name, args))

MCP (Model Context Protocol) Server client for connecting to external tools.

This class provides a unified interface for connecting to MCP servers using different transport protocols (HTTP/SSE, stdio). It handles connection management, tool discovery, and tool execution in both synchronous and asynchronous contexts.

The MCP server allows AI agents to access external tools and services through a standardized protocol, enabling dynamic tool discovery and execution without hardcoded integrations.

Examples
>>> # HTTP/SSE server
>>> server = McpServer(
...     name="coingecko",
...     server_type="http",
...     server_url="https://mcp.api.coingecko.com/sse"
... )
>>> tools = server.get_tools()
>>> result = server.call_tool("get_price", {"coin": "bitcoin"})
>>> # Stdio server
>>> server = McpServer(
...     name="local_tools",
...     server_type="python",
...     server_args=["python", "tools_server.py"]
... )
>>> tools = server.get_tools()
McpServer( name: str, server_type: str, server_args: list | None = None, server_url: str | None = None, env: dict | None = None, headers: dict | None = None, connect_timeout: float = 15.0)
44    def __init__(
45        self,
46        name: str,
47        server_type: str,               # "http" | "node" | "python"
48        server_args: list | None = None,
49        server_url: str | None = None,
50        env: dict | None = None,
51        headers: dict | None = None,    # <-- per HTTP/SSE
52        connect_timeout: float = 15.0,  # opzionale
53    ):
54        """Initialize the MCP server client.
55        
56        Parameters
57        ----------
58        name : str
59            Unique identifier for this MCP server instance
60        server_type : str
61            Type of server connection ("http", "node", "python")
62        server_args : list, optional
63            Command line arguments for stdio-based servers
64        server_url : str, optional
65            URL for HTTP/SSE based servers
66        env : dict, optional
67            Environment variables for stdio-based servers
68        headers : dict, optional
69            HTTP headers for HTTP/SSE based servers
70        connect_timeout : float, optional
71            Connection timeout in seconds (default: 15.0)
72        """
73        self.name = name
74        self._server_type = server_type
75        self._server_args = server_args or []
76        self._server_url = server_url
77        self._env = env or {}
78        self._headers = headers or {}

Initialize the MCP server client.

Parameters
  • name (str): Unique identifier for this MCP server instance
  • server_type (str): Type of server connection ("http", "node", "python")
  • server_args (list, optional): Command line arguments for stdio-based servers
  • server_url (str, optional): URL for HTTP/SSE based servers
  • env (dict, optional): Environment variables for stdio-based servers
  • headers (dict, optional): HTTP headers for HTTP/SSE based servers
  • connect_timeout (float, optional): Connection timeout in seconds (default: 15.0)
name
async def get_tools_async(self):
111    async def get_tools_async(self):
112        """Asynchronously retrieve available tools from the MCP server.
113        
114        Returns
115        -------
116        list
117            List of available tools with their schemas and descriptions
118            
119        Raises
120        ------
121        RuntimeError
122            If unable to connect to the server or retrieve tools
123        """
124        async with self._get_client() as (read, write):
125            async with ClientSession(read, write) as session:
126                resp = await session.list_tools()
127                return resp.tools

Asynchronously retrieve available tools from the MCP server.

Returns
  • list: List of available tools with their schemas and descriptions
Raises
  • RuntimeError: If unable to connect to the server or retrieve tools
def get_tools(self):
129    def get_tools(self):
130        """Synchronously retrieve available tools from the MCP server.
131        
132        This is a synchronous wrapper around the async method that runs
133        the operation in a new event loop.
134        
135        Returns
136        -------
137        list
138            List of available tools with their schemas and descriptions
139            
140        Raises
141        ------
142        RuntimeError
143            If unable to connect to the server or retrieve tools
144        """
145        return asyncio.run(self.get_tools_async())

Synchronously retrieve available tools from the MCP server.

This is a synchronous wrapper around the async method that runs the operation in a new event loop.

Returns
  • list: List of available tools with their schemas and descriptions
Raises
  • RuntimeError: If unable to connect to the server or retrieve tools
async def call_tool_async(self, name: str, args: dict):
147    async def call_tool_async(self, name: str, args: dict):
148        """Asynchronously execute a tool on the MCP server.
149        
150        Parameters
151        ----------
152        name : str
153            Name of the tool to execute. Can include MCP prefix (mcp_servername_toolname)
154            or just the tool name
155        args : dict
156            Arguments to pass to the tool
157            
158        Returns
159        -------
160        list
161            Tool execution result, formatted as a list of content items
162            
163        Raises
164        ------
165        RuntimeError
166            If unable to connect to the server or execute the tool
167        ValueError
168            If the tool name format is invalid
169        """
170        async with self._get_client() as (read, write):
171            async with ClientSession(read, write) as session:
172                prefix = f"mcp_{self.name}_"
173                tool_name = name[len(prefix):] if name.startswith(prefix) else name
174                result = await session.call_tool(tool_name, args)
175                                    
176                # Handle different response formats
177                if hasattr(result, 'content'):
178                    if isinstance(result.content, list) and len(result.content) > 0:
179                        if hasattr(result.content[0], 'text'):
180                            content = json.loads(result.content[0].text)
181                        else:
182                            content = str(result.content[0])
183                    else:
184                        content = str(result.content)
185                else:
186                    content = str(result)
187
188                if isinstance(content, dict):
189                    content = [content]
190                return content

Asynchronously execute a tool on the MCP server.

Parameters
  • name (str): Name of the tool to execute. Can include MCP prefix (mcp_servername_toolname) or just the tool name
  • args (dict): Arguments to pass to the tool
Returns
  • list: Tool execution result, formatted as a list of content items
Raises
  • RuntimeError: If unable to connect to the server or execute the tool
  • ValueError: If the tool name format is invalid
def call_tool(self, name: str, args: dict):
192    def call_tool(self, name: str, args: dict):
193        """Synchronously execute a tool on the MCP server.
194        
195        This is a synchronous wrapper around the async method that runs
196        the operation in a new event loop.
197        
198        Parameters
199        ----------
200        name : str
201            Name of the tool to execute. Can include MCP prefix (mcp_servername_toolname)
202            or just the tool name
203        args : dict
204            Arguments to pass to the tool
205            
206        Returns
207        -------
208        list
209            Tool execution result, formatted as a list of content items
210            
211        Raises
212        ------
213        RuntimeError
214            If unable to connect to the server or execute the tool
215        ValueError
216            If the tool name format is invalid
217        """
218        return asyncio.run(self.call_tool_async(name, args))

Synchronously execute a tool on the MCP server.

This is a synchronous wrapper around the async method that runs the operation in a new event loop.

Parameters
  • name (str): Name of the tool to execute. Can include MCP prefix (mcp_servername_toolname) or just the tool name
  • args (dict): Arguments to pass to the tool
Returns
  • list: Tool execution result, formatted as a list of content items
Raises
  • RuntimeError: If unable to connect to the server or execute the tool
  • ValueError: If the tool name format is invalid