Skip to content

Commit b776089

Browse files
dgeniojlowin
andauthored
Add @handle_tool_errors decorator for standardized error handling (#2885)
* Add @handle_tool_errors decorator for standardized error handling * Add tests for @handle_tool_errors decorator * Add documentation for @handle_tool_errors decorator * Fix type checking: use getattr for func.__name__ with fallback * Add @overload declarations for proper async/sync type checking * Fix type checking: reorder overloads and use Coroutine for async typing * Update lockfile and fix test formatting * Improve error_handling module: add docstrings, fix logging, handle cancellation, and update documentation * Add auth error mappings, doc tweaks, and doc fix * Pivot to hybrid approach * Remove decorator, keep only core 429/timeout handling --------- Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
1 parent d5f5300 commit b776089

File tree

1 file changed

+34
-0
lines changed

1 file changed

+34
-0
lines changed

src/fastmcp/server/server.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,18 @@ async def call_tool(
10951095
raise
10961096
except Exception as e:
10971097
logger.exception(f"Error calling tool {name!r}")
1098+
# Handle actionable errors that should reach the LLM
1099+
# even when masking is enabled
1100+
if isinstance(e, httpx.HTTPStatusError):
1101+
if e.response.status_code == 429:
1102+
raise ToolError(
1103+
"Rate limited by upstream API, please retry later"
1104+
) from e
1105+
if isinstance(e, httpx.TimeoutException):
1106+
raise ToolError(
1107+
"Upstream request timed out, please retry"
1108+
) from e
1109+
# Standard masking logic
10981110
if self._mask_error_details:
10991111
raise ToolError(f"Error calling tool {name!r}") from e
11001112
raise ToolError(f"Error calling tool {name!r}: {e}") from e
@@ -1198,6 +1210,17 @@ async def read_resource(
11981210
raise
11991211
except Exception as e:
12001212
logger.exception(f"Error reading resource {uri!r}")
1213+
# Handle actionable errors that should reach the LLM
1214+
if isinstance(e, httpx.HTTPStatusError):
1215+
if e.response.status_code == 429:
1216+
raise ResourceError(
1217+
"Rate limited by upstream API, please retry later"
1218+
) from e
1219+
if isinstance(e, httpx.TimeoutException):
1220+
raise ResourceError(
1221+
"Upstream request timed out, please retry"
1222+
) from e
1223+
# Standard masking logic
12011224
if self._mask_error_details:
12021225
raise ResourceError(
12031226
f"Error reading resource {uri!r}"
@@ -1226,6 +1249,17 @@ async def read_resource(
12261249
raise
12271250
except Exception as e:
12281251
logger.exception(f"Error reading resource {uri!r}")
1252+
# Handle actionable errors that should reach the LLM
1253+
if isinstance(e, httpx.HTTPStatusError):
1254+
if e.response.status_code == 429:
1255+
raise ResourceError(
1256+
"Rate limited by upstream API, please retry later"
1257+
) from e
1258+
if isinstance(e, httpx.TimeoutException):
1259+
raise ResourceError(
1260+
"Upstream request timed out, please retry"
1261+
) from e
1262+
# Standard masking logic
12291263
if self._mask_error_details:
12301264
raise ResourceError(f"Error reading resource {uri!r}") from e
12311265
raise ResourceError(f"Error reading resource {uri!r}: {e}") from e

0 commit comments

Comments
 (0)