|
31 | 31 | from mcp.server.lowlevel.server import LifespanResultT, Server |
32 | 32 | from mcp.server.lowlevel.server import lifespan as default_lifespan |
33 | 33 | from mcp.server.mcpserver.context import Context |
34 | | -from mcp.server.mcpserver.exceptions import ResourceError |
| 34 | +from mcp.server.mcpserver.exceptions import PromptError, ResourceError, ToolError |
35 | 35 | from mcp.server.mcpserver.prompts import Prompt, PromptManager |
36 | 36 | from mcp.server.mcpserver.resources import FunctionResource, Resource, ResourceManager |
37 | 37 | from mcp.server.mcpserver.tools import Tool, ToolManager |
|
44 | 44 | from mcp.server.transport_security import TransportSecuritySettings |
45 | 45 | from mcp.shared.exceptions import MCPError |
46 | 46 | from mcp.types import ( |
| 47 | + INTERNAL_ERROR, |
| 48 | + INVALID_PARAMS, |
47 | 49 | Annotations, |
48 | 50 | BlobResourceContents, |
49 | 51 | CallToolRequestParams, |
@@ -303,8 +305,14 @@ async def _handle_call_tool( |
303 | 305 | result = await self.call_tool(params.name, params.arguments or {}, context) |
304 | 306 | except MCPError: |
305 | 307 | raise |
306 | | - except Exception as e: |
| 308 | + except ToolError as e: |
307 | 309 | return CallToolResult(content=[TextContent(type="text", text=str(e))], is_error=True) |
| 310 | + except Exception: |
| 311 | + logger.exception(f"Unhandled error in tool {params.name}") |
| 312 | + return CallToolResult( |
| 313 | + content=[TextContent(type="text", text=f"Internal error executing tool {params.name}")], |
| 314 | + is_error=True, |
| 315 | + ) |
308 | 316 | if isinstance(result, CallToolResult): |
309 | 317 | return result |
310 | 318 | if isinstance(result, tuple) and len(result) == 2: |
@@ -332,7 +340,16 @@ async def _handle_read_resource( |
332 | 340 | self, ctx: ServerRequestContext[LifespanResultT], params: ReadResourceRequestParams |
333 | 341 | ) -> ReadResourceResult: |
334 | 342 | context = Context(request_context=ctx, mcp_server=self) |
335 | | - results = await self.read_resource(params.uri, context) |
| 343 | + try: |
| 344 | + results = await self.read_resource(params.uri, context) |
| 345 | + except MCPError: |
| 346 | + raise |
| 347 | + except ResourceError as e: |
| 348 | + raise MCPError(code=INVALID_PARAMS, message=str(e)) |
| 349 | + except Exception: |
| 350 | + logger.exception(f"Unhandled error reading resource {params.uri}") |
| 351 | + raise MCPError(code=INTERNAL_ERROR, message=f"Internal error reading resource {params.uri}") |
| 352 | + |
336 | 353 | contents: list[TextResourceContents | BlobResourceContents] = [] |
337 | 354 | for item in results: |
338 | 355 | if isinstance(item.content, bytes): |
@@ -369,7 +386,15 @@ async def _handle_get_prompt( |
369 | 386 | self, ctx: ServerRequestContext[LifespanResultT], params: GetPromptRequestParams |
370 | 387 | ) -> GetPromptResult: |
371 | 388 | context = Context(request_context=ctx, mcp_server=self) |
372 | | - return await self.get_prompt(params.name, params.arguments, context) |
| 389 | + try: |
| 390 | + return await self.get_prompt(params.name, params.arguments, context) |
| 391 | + except MCPError: |
| 392 | + raise |
| 393 | + except PromptError as e: |
| 394 | + raise MCPError(code=INVALID_PARAMS, message=str(e)) |
| 395 | + except Exception: |
| 396 | + logger.exception(f"Unhandled error in prompt {params.name}") |
| 397 | + raise MCPError(code=INTERNAL_ERROR, message=f"Internal error getting prompt {params.name}") |
373 | 398 |
|
374 | 399 | async def list_tools(self) -> list[MCPTool]: |
375 | 400 | """List all available tools.""" |
@@ -444,9 +469,10 @@ async def read_resource( |
444 | 469 | try: |
445 | 470 | content = await resource.read() |
446 | 471 | return [ReadResourceContents(content=content, mime_type=resource.mime_type, meta=resource.meta)] |
| 472 | + except (ResourceError, MCPError): |
| 473 | + raise |
447 | 474 | except Exception as exc: |
448 | | - logger.exception(f"Error getting resource {uri}") |
449 | | - # If an exception happens when reading the resource, we should not leak the exception to the client. |
| 475 | + logger.exception(f"Error reading resource {uri}") |
450 | 476 | raise ResourceError(f"Error reading resource {uri}") from exc |
451 | 477 |
|
452 | 478 | def add_tool( |
@@ -1090,14 +1116,16 @@ async def get_prompt( |
1090 | 1116 | try: |
1091 | 1117 | prompt = self._prompt_manager.get_prompt(name) |
1092 | 1118 | if not prompt: |
1093 | | - raise ValueError(f"Unknown prompt: {name}") |
| 1119 | + raise PromptError(f"Unknown prompt: {name}") |
1094 | 1120 |
|
1095 | 1121 | messages = await prompt.render(arguments, context) |
1096 | 1122 |
|
1097 | 1123 | return GetPromptResult( |
1098 | 1124 | description=prompt.description, |
1099 | 1125 | messages=pydantic_core.to_jsonable_python(messages), |
1100 | 1126 | ) |
| 1127 | + except (PromptError, MCPError): |
| 1128 | + raise |
1101 | 1129 | except Exception as e: |
1102 | 1130 | logger.exception(f"Error getting prompt {name}") |
1103 | | - raise ValueError(str(e)) |
| 1131 | + raise PromptError(f"Error getting prompt {name}") from e |
0 commit comments