Skip to content

Commit c0f1ae7

Browse files
authored
fix: workaround mcp context manager suppressing exceptions + doc (#352)
1 parent be545d7 commit c0f1ae7

File tree

2 files changed

+23
-22
lines changed

2 files changed

+23
-22
lines changed

langchain_mcp_adapters/interceptors.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ class _MCPToolCallRequestOverrides(TypedDict, total=False):
3838
class MCPToolCallRequest:
3939
"""Tool execution request passed to MCP tool call interceptors.
4040
41-
Similar to LangChain's ToolCallRequest but adapted for MCP remote tools.
42-
MCP tools don't have local BaseTool instances, so this flattens the call
43-
data and context into a single object.
41+
This tool call request follows a similar pattern to LangChain's
42+
ToolCallRequest (flat namespace) rather than separating the call data
43+
and context into nested objects.
4444
4545
Modifiable fields (override these to change behavior):
4646
name: Tool name to invoke.

langchain_mcp_adapters/tools.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"""
66

77
from collections.abc import Awaitable, Callable
8-
from typing import Any, cast, get_args
8+
from typing import Any, get_args
99

1010
from langchain_core.tools import (
1111
BaseTool,
@@ -239,8 +239,7 @@ async def execute_tool(request: MCPToolCallRequest) -> MCPToolCallResult:
239239
}
240240
effective_connection = updated_connection
241241

242-
# Execute the tool call
243-
call_tool_result = None
242+
captured_exception = None
244243

245244
if session is None:
246245
# If a session is not provided, we will create one on the fly
@@ -252,29 +251,31 @@ async def execute_tool(request: MCPToolCallRequest) -> MCPToolCallResult:
252251
effective_connection, mcp_callbacks=mcp_callbacks
253252
) as tool_session:
254253
await tool_session.initialize()
255-
call_tool_result = await cast(
256-
"ClientSession", tool_session
257-
).call_tool(
258-
tool_name,
259-
tool_args,
260-
progress_callback=mcp_callbacks.progress_callback,
261-
)
254+
try:
255+
call_tool_result = await tool_session.call_tool(
256+
tool_name,
257+
tool_args,
258+
progress_callback=mcp_callbacks.progress_callback,
259+
)
260+
except Exception as e: # noqa: BLE001
261+
# Capture exception to re-raise outside context manager
262+
captured_exception = e
263+
264+
# Re-raise the exception outside the context manager
265+
# This is necessary because the context manager may suppress exceptions
266+
# This change was introduced to work-around an issue in MCP SDK
267+
# that may suppress exceptions when the client disconnects.
268+
# If this is causing an issue, with your use case, please file an issue
269+
# on the langchain-mcp-adapters GitHub repo.
270+
if captured_exception is not None:
271+
raise captured_exception
262272
else:
263273
call_tool_result = await session.call_tool(
264274
tool_name,
265275
tool_args,
266276
progress_callback=mcp_callbacks.progress_callback,
267277
)
268278

269-
if call_tool_result is None:
270-
msg = (
271-
"Tool call failed: no result returned from the underlying MCP SDK. "
272-
"This may indicate that an exception was handled or suppressed "
273-
"by the MCP SDK (e.g., client disconnection, network issue, "
274-
"or other execution error)."
275-
)
276-
raise RuntimeError(msg)
277-
278279
return call_tool_result
279280

280281
# Build and execute the interceptor chain

0 commit comments

Comments
 (0)