Skip to content

Commit a49a19e

Browse files
fix(mcp/): re-raise exception from server to client
allows user to debug why mcp server is not working as expected Related to #15180
1 parent b7ca138 commit a49a19e

File tree

4 files changed

+48
-56
lines changed

4 files changed

+48
-56
lines changed

litellm/experimental_mcp_client/client.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,10 +235,9 @@ async def list_tools(self) -> List[MCPTool]:
235235
await self.disconnect()
236236
raise
237237
except Exception as e:
238-
verbose_logger.warning(f"MCP client list_tools failed: {str(e)}")
238+
verbose_logger.debug(f"MCP client list_tools failed: {str(e)}")
239239
await self.disconnect()
240-
# Return empty list instead of raising to allow graceful degradation
241-
return []
240+
raise e
242241

243242
async def call_tool(
244243
self, call_tool_request_params: MCPCallToolRequestParams

litellm/proxy/_experimental/mcp_server/mcp_server_manager.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -490,10 +490,10 @@ async def _get_tools_from_server(
490490
return prefixed_or_original_tools
491491

492492
except Exception as e:
493-
verbose_logger.warning(
493+
verbose_logger.debug(
494494
f"Failed to get tools from server {server.name}: {str(e)}"
495495
)
496-
return []
496+
raise e
497497
finally:
498498
if client:
499499
try:
@@ -522,14 +522,14 @@ async def _list_tools_task():
522522
tools = await client.list_tools()
523523
verbose_logger.debug(f"Tools from {server_name}: {tools}")
524524
return tools
525-
except asyncio.CancelledError:
526-
verbose_logger.warning(f"Client operation cancelled for {server_name}")
527-
return []
525+
except asyncio.CancelledError as e:
526+
verbose_logger.debug(f"Client operation cancelled for {server_name}")
527+
raise e
528528
except Exception as e:
529-
verbose_logger.warning(
529+
verbose_logger.debug(
530530
f"Client operation failed for {server_name}: {str(e)}"
531531
)
532-
return []
532+
raise e
533533
finally:
534534
try:
535535
await client.disconnect()
@@ -538,22 +538,22 @@ async def _list_tools_task():
538538

539539
try:
540540
return await asyncio.wait_for(_list_tools_task(), timeout=30.0)
541-
except asyncio.TimeoutError:
542-
verbose_logger.warning(f"Timeout while listing tools from {server_name}")
543-
return []
544-
except asyncio.CancelledError:
545-
verbose_logger.warning(
541+
except asyncio.TimeoutError as e:
542+
verbose_logger.debug(f"Timeout while listing tools from {server_name}")
543+
raise e
544+
except asyncio.CancelledError as e:
545+
verbose_logger.debug(
546546
f"Task cancelled while listing tools from {server_name}"
547547
)
548-
return []
548+
raise e
549549
except ConnectionError as e:
550-
verbose_logger.warning(
550+
verbose_logger.debug(
551551
f"Connection error while listing tools from {server_name}: {str(e)}"
552552
)
553-
return []
553+
raise e
554554
except Exception as e:
555-
verbose_logger.warning(f"Error listing tools from {server_name}: {str(e)}")
556-
return []
555+
verbose_logger.debug(f"Error listing tools from {server_name}: {str(e)}")
556+
raise e
557557

558558
def _create_prefixed_tools(
559559
self, tools: List[MCPTool], server: MCPServer, add_prefix: bool = True

litellm/proxy/_experimental/mcp_server/server.py

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,8 @@ async def list_tools() -> List[MCPTool]:
206206
)
207207
return tools
208208
except Exception as e:
209-
verbose_logger.exception(f"Error in list_tools endpoint: {str(e)}")
210-
# Return empty list instead of failing completely
211-
# This prevents the HTTP stream from failing and allows the client to get a response
212-
return []
209+
verbose_logger.debug(f"Error in list_tools endpoint: {str(e)}")
210+
raise e
213211

214212
@server.call_tool()
215213
async def mcp_server_tool_call(
@@ -364,25 +362,25 @@ async def _get_allowed_mcp_servers_from_mcp_server_names(
364362
def _tool_name_matches(tool_name: str, filter_list: List[str]) -> bool:
365363
"""
366364
Check if a tool name matches any name in the filter list.
367-
365+
368366
Checks both the full tool name and unprefixed version (without server prefix).
369367
This allows users to configure simple tool names regardless of prefixing.
370-
368+
371369
Args:
372370
tool_name: The tool name to check (may be prefixed like "server-tool_name")
373371
filter_list: List of tool names to match against
374-
372+
375373
Returns:
376374
True if the tool name (prefixed or unprefixed) is in the filter list
377375
"""
378376
from litellm.proxy._experimental.mcp_server.utils import (
379377
get_server_name_prefix_tool_mcp,
380378
)
381-
379+
382380
# Check if the full name is in the list
383381
if tool_name in filter_list:
384382
return True
385-
383+
386384
# Check if the unprefixed name is in the list
387385
unprefixed_name, _ = get_server_name_prefix_tool_mcp(tool_name)
388386
return unprefixed_name in filter_list
@@ -393,34 +391,36 @@ def filter_tools_by_allowed_tools(
393391
) -> List[MCPTool]:
394392
"""
395393
Filter tools by allowed/disallowed tools configuration.
396-
394+
397395
If allowed_tools is set, only tools in that list are returned.
398396
If disallowed_tools is set, tools in that list are excluded.
399397
Tool names are matched with and without server prefixes for flexibility.
400-
398+
401399
Args:
402400
tools: List of tools to filter
403401
mcp_server: Server configuration with allowed_tools/disallowed_tools
404-
402+
405403
Returns:
406404
Filtered list of tools
407405
"""
408406
tools_to_return = tools
409-
407+
410408
# Filter by allowed_tools (whitelist)
411409
if mcp_server.allowed_tools:
412410
tools_to_return = [
413-
tool for tool in tools
411+
tool
412+
for tool in tools
414413
if _tool_name_matches(tool.name, mcp_server.allowed_tools)
415414
]
416-
415+
417416
# Filter by disallowed_tools (blacklist)
418417
if mcp_server.disallowed_tools:
419418
tools_to_return = [
420-
tool for tool in tools_to_return
419+
tool
420+
for tool in tools_to_return
421421
if not _tool_name_matches(tool.name, mcp_server.disallowed_tools)
422422
]
423-
423+
424424
return tools_to_return
425425

426426
async def _get_tools_from_mcp_servers(
@@ -497,18 +497,19 @@ async def _get_tools_from_mcp_servers(
497497
extra_headers=extra_headers,
498498
add_prefix=add_prefix,
499499
)
500-
500+
501501
filtered_tools = filter_tools_by_allowed_tools(tools, server)
502502
all_tools.extend(filtered_tools)
503-
503+
504504
verbose_logger.debug(
505505
f"Successfully fetched {len(tools)} tools from server {server.name}, {len(filtered_tools)} after filtering"
506506
)
507507
except Exception as e:
508-
verbose_logger.exception(
508+
verbose_logger.debug(
509509
f"Error getting tools from server {server.name}: {str(e)}"
510510
)
511511
# Continue with other servers instead of failing completely
512+
raise e
512513

513514
verbose_logger.info(
514515
f"Successfully fetched {len(all_tools)} tools total from all MCP servers"
@@ -556,7 +557,7 @@ async def _list_mcp_tools(
556557
f"Error getting tools from managed MCP servers: {str(e)}"
557558
)
558559
# Continue with empty managed tools list instead of failing completely
559-
560+
raise e
560561
# Get tools from local registry
561562
local_tools = []
562563
try:

litellm/proxy/_new_secret_config.yaml

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,10 @@ model_list:
1616
api_base: "https://webhook.site/2f385e05-00aa-402b-86d1-efc9261471a5"
1717
api_key: dummy
1818

19-
# mcp_servers:
20-
# github_mcp:
21-
# url: "https://api.githubcopilot.com/mcp"
22-
# auth_type: oauth2
23-
# authorization_url: https://github.com/login/oauth/authorize
24-
# token_url: https://github.com/login/oauth/access_token
25-
# client_id: os.environ/GITHUB_OAUTH_CLIENT_ID
26-
# client_secret: os.environ/GITHUB_OAUTH_CLIENT_SECRET
27-
# scopes: ["public_repo", "user:email"]
28-
# allowed_tools: ["list_tools"]
29-
# # disallowed_tools: ["repo_delete"]
30-
31-
litellm_settings:
32-
callbacks: ["prometheus"]
33-
custom_prometheus_metadata_labels: ["metadata.initiative", "metadata.business-unit"]
19+
mcp_servers:
20+
local_fake_mcp:
21+
url: "http://127.0.0.1:8001/mcp"
22+
transport: "http"
23+
description: "My custom MCP server"
24+
auth_type: "api_key"
25+
auth_value: "abc123"

0 commit comments

Comments
 (0)