Skip to content

Commit 74f8da6

Browse files
committed
(fix): fix mcp_client code.
1 parent 94258f8 commit 74f8da6

3 files changed

Lines changed: 400 additions & 0 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
from .mcp_client import MCPClient
3+
from .sokoban_mcp_env import SokobanMCPEnv
4+
5+
__all__ = ["MCPClient", "SokobanMCPEnv"]
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
from typing import Optional
2+
from mcp import ClientSession
3+
from mcp.client.sse import sse_client
4+
from roll.utils.logging import get_logger
5+
6+
logger = get_logger()
7+
8+
class MCPClient:
9+
def __init__(self, server_url: str):
10+
"""
11+
Initialize MCPClient with the server URL.
12+
Args:
13+
server_url (str): The URL of the MCP SSE server to connect to.
14+
"""
15+
self.server_url = server_url
16+
self._streams_context = None
17+
self._session_context = None
18+
self.session: Optional[ClientSession] = None
19+
20+
async def __aenter__(self):
21+
"""
22+
Enter the async context manager: connect to the MCP SSE server,
23+
initialize the session and prepare for tool calls.
24+
25+
Returns:
26+
MCPClient: The connected client instance itself.
27+
"""
28+
self._streams_context = sse_client(url=self.server_url)
29+
self._streams = await self._streams_context.__aenter__()
30+
self._session_context = ClientSession(*self._streams)
31+
self.session = await self._session_context.__aenter__()
32+
33+
initialize = await self.session.initialize()
34+
logger.debug(f"Session initialize: {initialize}")
35+
36+
response = await self.session.list_tools()
37+
tools = getattr(response, "tools", [])
38+
logger.debug(f"Connected to server with tools: {[tool.name for tool in tools]}")
39+
40+
return self
41+
42+
async def __aexit__(self, exc_type, exc_val, exc_tb):
43+
"""
44+
Exit the async context: cleanly close session and streams,
45+
ensuring resources are properly released.
46+
Args:
47+
exc_type, exc_val, exc_tb: Exception information if exiting because of an exception.
48+
"""
49+
if self._session_context:
50+
await self._session_context.__aexit__(exc_type, exc_val, exc_tb)
51+
self._session_context = None
52+
if self._streams_context:
53+
await self._streams_context.__aexit__(exc_type, exc_val, exc_tb)
54+
self._streams_context = None
55+
56+
async def tools(self):
57+
"""
58+
List available tools on the MCP server.
59+
Returns:
60+
List of tools info retrieved from the server.
61+
"""
62+
try:
63+
# Call the server to get tools list
64+
response = await self.session.list_tools()
65+
if not hasattr(response, 'tools'):
66+
logger.error(f"Invalid tools response: 'tools' attribute not found in {response}")
67+
return []
68+
tools_list = response.tools
69+
logger.debug(f"Retrieved {len(tools_list)} tools")
70+
71+
tool_names = [tool.name for tool in tools_list]
72+
logger.debug(f"Tools available: {tool_names}")
73+
74+
return tools_list
75+
76+
except Exception as e:
77+
logger.exception(f"Error retrieving tools: {e}")
78+
return []
79+
80+
async def call_tool(self, tool_name: str, tool_params: Optional[dict] = None):
81+
"""
82+
Call a specific tool on the MCP server with optional parameters.
83+
Args:
84+
tool_name (str): The name of the tool to call.
85+
tool_params (Optional[dict]): Parameters to pass to the tool call.
86+
Returns:
87+
The result object returned from the MCP server's tool call.
88+
"""
89+
if tool_params is None:
90+
tool_params = {}
91+
result = await self.session.call_tool(tool_name, tool_params)
92+
logger.debug(f"Call tool '{tool_name}' with params {tool_params} received: {result}" )
93+
94+
return result

0 commit comments

Comments
 (0)