Skip to content

Commit 54367dc

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: support to customize timeout for mcpstdio connections
Fixes #643 PiperOrigin-RevId: 767788472
1 parent fe1de7b commit 54367dc

File tree

4 files changed

+106
-47
lines changed

4 files changed

+106
-47
lines changed

contributing/samples/mcp_stdio_server_agent/agent.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
import os
1717

1818
from google.adk.agents.llm_agent import LlmAgent
19+
from google.adk.tools.mcp_tool import StdioConnectionParams
1920
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
20-
from google.adk.tools.mcp_tool.mcp_toolset import StdioServerParameters
21+
from mcp import StdioServerParameters
2122

2223
_allowed_path = os.path.dirname(os.path.abspath(__file__))
2324

@@ -31,13 +32,16 @@
3132
""",
3233
tools=[
3334
MCPToolset(
34-
connection_params=StdioServerParameters(
35-
command='npx',
36-
args=[
37-
'-y', # Arguments for the command
38-
'@modelcontextprotocol/server-filesystem',
39-
_allowed_path,
40-
],
35+
connection_params=StdioConnectionParams(
36+
server_params=StdioServerParameters(
37+
command='npx',
38+
args=[
39+
'-y', # Arguments for the command
40+
'@modelcontextprotocol/server-filesystem',
41+
_allowed_path,
42+
],
43+
),
44+
timeout=5,
4145
),
4246
# don't want agent to do write operation
4347
# you can also do below

src/google/adk/tools/mcp_tool/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
try:
1818
from .conversion_utils import adk_to_mcp_tool_type
1919
from .conversion_utils import gemini_to_json_schema
20+
from .mcp_session_manager import SseConnectionParams
21+
from .mcp_session_manager import StdioConnectionParams
22+
from .mcp_session_manager import StreamableHTTPConnectionParams
2023
from .mcp_tool import MCPTool
2124
from .mcp_toolset import MCPToolset
2225

@@ -25,6 +28,9 @@
2528
'gemini_to_json_schema',
2629
'MCPTool',
2730
'MCPToolset',
31+
'StdioConnectionParams',
32+
'SseConnectionParams',
33+
'StreamableHTTPConnectionParams',
2834
])
2935

3036
except ImportError as e:

src/google/adk/tools/mcp_tool/mcp_session_manager.py

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -47,30 +47,61 @@
4747
logger = logging.getLogger('google_adk.' + __name__)
4848

4949

50-
class SseServerParams(BaseModel):
50+
class StdioConnectionParams(BaseModel):
51+
"""Parameters for the MCP Stdio connection.
52+
53+
Attributes:
54+
server_params: Parameters for the MCP Stdio server.
55+
timeout: Timeout in seconds for establishing the connection to the MCP
56+
stdio server.
57+
"""
58+
59+
server_params: StdioServerParameters
60+
timeout: float = 5.0
61+
62+
63+
class SseConnectionParams(BaseModel):
5164
"""Parameters for the MCP SSE connection.
5265
5366
See MCP SSE Client documentation for more details.
5467
https://github.com/modelcontextprotocol/python-sdk/blob/main/src/mcp/client/sse.py
68+
69+
Attributes:
70+
url: URL for the MCP SSE server.
71+
headers: Headers for the MCP SSE connection.
72+
timeout: Timeout in seconds for establishing the connection to the MCP SSE
73+
server.
74+
sse_read_timeout: Timeout in seconds for reading data from the MCP SSE
75+
server.
5576
"""
5677

5778
url: str
5879
headers: dict[str, Any] | None = None
59-
timeout: float = 5
60-
sse_read_timeout: float = 60 * 5
80+
timeout: float = 5.0
81+
sse_read_timeout: float = 60 * 5.0
6182

6283

63-
class StreamableHTTPServerParams(BaseModel):
84+
class StreamableHTTPConnectionParams(BaseModel):
6485
"""Parameters for the MCP SSE connection.
6586
6687
See MCP SSE Client documentation for more details.
6788
https://github.com/modelcontextprotocol/python-sdk/blob/main/src/mcp/client/streamable_http.py
89+
90+
Attributes:
91+
url: URL for the MCP Streamable HTTP server.
92+
headers: Headers for the MCP Streamable HTTP connection.
93+
timeout: Timeout in seconds for establishing the connection to the MCP
94+
Streamable HTTP server.
95+
sse_read_timeout: Timeout in seconds for reading data from the MCP
96+
Streamable HTTP server.
97+
terminate_on_close: Whether to terminate the MCP Streamable HTTP server
98+
when the connection is closed.
6899
"""
69100

70101
url: str
71102
headers: dict[str, Any] | None = None
72-
timeout: float = 5
73-
sse_read_timeout: float = 60 * 5
103+
timeout: float = 5.0
104+
sse_read_timeout: float = 60 * 5.0
74105
terminate_on_close: bool = True
75106

76107

@@ -142,7 +173,10 @@ class MCPSessionManager:
142173
def __init__(
143174
self,
144175
connection_params: Union[
145-
StdioServerParameters, SseServerParams, StreamableHTTPServerParams
176+
StdioServerParameters,
177+
StdioConnectionParams,
178+
SseConnectionParams,
179+
StreamableHTTPConnectionParams,
146180
],
147181
errlog: TextIO = sys.stderr,
148182
):
@@ -155,7 +189,20 @@ def __init__(
155189
errlog: (Optional) TextIO stream for error logging. Use only for
156190
initializing a local stdio MCP session.
157191
"""
158-
self._connection_params = connection_params
192+
if isinstance(connection_params, StdioServerParameters):
193+
# So far timeout is not configurable. Given MCP is still evolving, we
194+
# would expect stdio_client to evolve to accept timeout parameter like
195+
# other client.
196+
logger.warning(
197+
'StdioServerParameters is not recommended. Please use'
198+
' StdioConnectionParams.'
199+
)
200+
self._connection_params = StdioConnectionParams(
201+
server_params=connection_params,
202+
timeout=5,
203+
)
204+
else:
205+
self._connection_params = connection_params
159206
self._errlog = errlog
160207
# Each session manager maintains its own exit stack for proper cleanup
161208
self._exit_stack: Optional[AsyncExitStack] = None
@@ -174,21 +221,19 @@ async def create_session(self) -> ClientSession:
174221
self._exit_stack = AsyncExitStack()
175222

176223
try:
177-
if isinstance(self._connection_params, StdioServerParameters):
178-
# So far timeout is not configurable. Given MCP is still evolving, we
179-
# would expect stdio_client to evolve to accept timeout parameter like
180-
# other client.
224+
if isinstance(self._connection_params, StdioConnectionParams):
181225
client = stdio_client(
182-
server=self._connection_params, errlog=self._errlog
226+
server=self._connection_params.server_params,
227+
errlog=self._errlog,
183228
)
184-
elif isinstance(self._connection_params, SseServerParams):
229+
elif isinstance(self._connection_params, SseConnectionParams):
185230
client = sse_client(
186231
url=self._connection_params.url,
187232
headers=self._connection_params.headers,
188233
timeout=self._connection_params.timeout,
189234
sse_read_timeout=self._connection_params.sse_read_timeout,
190235
)
191-
elif isinstance(self._connection_params, StreamableHTTPServerParams):
236+
elif isinstance(self._connection_params, StreamableHTTPConnectionParams):
192237
client = streamablehttp_client(
193238
url=self._connection_params.url,
194239
headers=self._connection_params.headers,
@@ -208,24 +253,13 @@ async def create_session(self) -> ClientSession:
208253
transports = await self._exit_stack.enter_async_context(client)
209254
# The streamable http client returns a GetSessionCallback in addition to the read/write MemoryObjectStreams
210255
# needed to build the ClientSession, we limit then to the two first values to be compatible with all clients.
211-
# The StdioServerParameters does not provide a timeout parameter for the
212-
# session, so we need to set a default timeout for it. Other clients
213-
# (SseServerParams and StreamableHTTPServerParams) already provide a
214-
# timeout parameter in their configuration.
215-
if isinstance(self._connection_params, StdioServerParameters):
216-
# Default timeout for MCP session is 5 seconds, same as SseServerParams
217-
# and StreamableHTTPServerParams.
218-
# TODO :
219-
# 1. make timeout configurable
220-
# 2. Add StdioConnectionParams to include StdioServerParameters as a
221-
# field and rename other two params to XXXXConnetionParams. Ohter
222-
# two params are actually connection params, while stdio is
223-
# special, stdio_client takes the resposibility of starting the
224-
# server and working as a client.
256+
if isinstance(self._connection_params, StdioConnectionParams):
225257
session = await self._exit_stack.enter_async_context(
226258
ClientSession(
227259
*transports[:2],
228-
read_timeout_seconds=timedelta(seconds=5),
260+
read_timeout_seconds=timedelta(
261+
seconds=self._connection_params.timeout
262+
),
229263
)
230264
)
231265
else:
@@ -257,3 +291,8 @@ async def close(self):
257291
finally:
258292
self._exit_stack = None
259293
self._session = None
294+
295+
296+
SseServerParams = SseConnectionParams
297+
298+
StreamableHTTPServerParams = StreamableHTTPConnectionParams

src/google/adk/tools/mcp_tool/mcp_toolset.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@
2727
from ..base_toolset import ToolPredicate
2828
from .mcp_session_manager import MCPSessionManager
2929
from .mcp_session_manager import retry_on_closed_resource
30+
from .mcp_session_manager import SseConnectionParams
3031
from .mcp_session_manager import SseServerParams
32+
from .mcp_session_manager import StdioConnectionParams
33+
from .mcp_session_manager import StreamableHTTPConnectionParams
3134
from .mcp_session_manager import StreamableHTTPServerParams
3235

3336
# Attempt to import MCP Tool from the MCP library, and hints user to upgrade
@@ -85,22 +88,29 @@ class MCPToolset(BaseToolset):
8588
def __init__(
8689
self,
8790
*,
88-
connection_params: (
89-
StdioServerParameters | SseServerParams | StreamableHTTPServerParams
90-
),
91+
connection_params: Union[
92+
StdioServerParameters,
93+
StdioConnectionParams,
94+
SseConnectionParams,
95+
StreamableHTTPConnectionParams,
96+
],
9197
tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
9298
errlog: TextIO = sys.stderr,
9399
):
94100
"""Initializes the MCPToolset.
95101
96102
Args:
97103
connection_params: The connection parameters to the MCP server. Can be:
98-
`StdioServerParameters` for using local mcp server (e.g. using `npx` or
99-
`python3`); or `SseServerParams` for a local/remote SSE server; or
100-
`StreamableHTTPServerParams` for local/remote Streamable http server.
101-
tool_filter: Optional filter to select specific tools. Can be either:
102-
- A list of tool names to include
103-
- A ToolPredicate function for custom filtering logic
104+
`StdioConnectionParams` for using local mcp server (e.g. using `npx` or
105+
`python3`); or `SseConnectionParams` for a local/remote SSE server; or
106+
`StreamableHTTPConnectionParams` for local/remote Streamable http
107+
server. Note, `StdioServerParameters` is also supported for using local
108+
mcp server (e.g. using `npx` or `python3` ), but it does not support
109+
timeout, and we recommend to use `StdioConnectionParams` instead when
110+
timeout is needed.
111+
tool_filter: Optional filter to select specific tools. Can be either: - A
112+
list of tool names to include - A ToolPredicate function for custom
113+
filtering logic
104114
errlog: TextIO stream for error logging.
105115
"""
106116
super().__init__(tool_filter=tool_filter)

0 commit comments

Comments
 (0)