From d7bacdad1173aaa7727136b576f6fa0c9a261383 Mon Sep 17 00:00:00 2001 From: Kian Khooban Date: Wed, 3 Jun 2026 18:38:10 -0400 Subject: [PATCH 1/2] fix: add `raise ... from e` in all except handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preserves exception chaining so the original traceback is always visible when exceptions are re-raised inside except blocks. Fixes 11 sites across 6 files — follow-up to #2542. --- src/mcp/client/auth/utils.py | 4 ++-- src/mcp/client/session.py | 4 ++-- src/mcp/server/mcpserver/prompts/base.py | 2 +- src/mcp/server/mcpserver/resources/resource_manager.py | 2 +- src/mcp/server/mcpserver/resources/templates.py | 2 +- src/mcp/server/mcpserver/resources/types.py | 8 ++++---- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/mcp/client/auth/utils.py b/src/mcp/client/auth/utils.py index 780a24e859..12120dc095 100644 --- a/src/mcp/client/auth/utils.py +++ b/src/mcp/client/auth/utils.py @@ -241,7 +241,7 @@ async def handle_registration_response(response: Response) -> OAuthClientInforma client_info = OAuthClientInformationFull.model_validate_json(content) return client_info except ValidationError as e: # pragma: no cover - raise OAuthRegistrationError(f"Invalid registration response: {e}") + raise OAuthRegistrationError(f"Invalid registration response: {e}") from e def is_valid_client_metadata_url(url: str | None) -> bool: @@ -334,4 +334,4 @@ async def handle_token_response_scopes( token_response = OAuthToken.model_validate_json(content) return token_response except ValidationError as e: # pragma: no cover - raise OAuthTokenError(f"Invalid token response: {e}") + raise OAuthTokenError(f"Invalid token response: {e}") from e diff --git a/src/mcp/client/session.py b/src/mcp/client/session.py index 08f532eca5..8ee93b2789 100644 --- a/src/mcp/client/session.py +++ b/src/mcp/client/session.py @@ -316,9 +316,9 @@ async def _validate_tool_result(self, name: str, result: types.CallToolResult) - try: validate(result.structured_content, output_schema) except ValidationError as e: - raise RuntimeError(f"Invalid structured content returned by tool {name}: {e}") + raise RuntimeError(f"Invalid structured content returned by tool {name}: {e}") from e except SchemaError as e: # pragma: no cover - raise RuntimeError(f"Invalid schema for tool {name}: {e}") # pragma: no cover + raise RuntimeError(f"Invalid schema for tool {name}: {e}") from e # pragma: no cover async def list_prompts(self, *, params: types.PaginatedRequestParams | None = None) -> types.ListPromptsResult: """Send a prompts/list request. diff --git a/src/mcp/server/mcpserver/prompts/base.py b/src/mcp/server/mcpserver/prompts/base.py index 2f778eb514..b697988634 100644 --- a/src/mcp/server/mcpserver/prompts/base.py +++ b/src/mcp/server/mcpserver/prompts/base.py @@ -186,4 +186,4 @@ async def render( return messages except Exception as e: - raise ValueError(f"Error rendering prompt {self.name}: {e}") + raise ValueError(f"Error rendering prompt {self.name}: {e}") from e diff --git a/src/mcp/server/mcpserver/resources/resource_manager.py b/src/mcp/server/mcpserver/resources/resource_manager.py index 766cf51aea..e9d0ddfc22 100644 --- a/src/mcp/server/mcpserver/resources/resource_manager.py +++ b/src/mcp/server/mcpserver/resources/resource_manager.py @@ -93,7 +93,7 @@ async def get_resource(self, uri: AnyUrl | str, context: Context[LifespanContext try: return await template.create_resource(uri_str, params, context=context) except Exception as e: # pragma: no cover - raise ValueError(f"Error creating resource from template: {e}") + raise ValueError(f"Error creating resource from template: {e}") from e raise ValueError(f"Unknown resource: {uri}") diff --git a/src/mcp/server/mcpserver/resources/templates.py b/src/mcp/server/mcpserver/resources/templates.py index f1ee29a37f..edde0c4c4a 100644 --- a/src/mcp/server/mcpserver/resources/templates.py +++ b/src/mcp/server/mcpserver/resources/templates.py @@ -130,4 +130,4 @@ async def create_resource( fn=lambda: result, # Capture result in closure ) except Exception as e: - raise ValueError(f"Error creating resource from template: {e}") + raise ValueError(f"Error creating resource from template: {e}") from e diff --git a/src/mcp/server/mcpserver/resources/types.py b/src/mcp/server/mcpserver/resources/types.py index d9e472e362..bb3c2edf7a 100644 --- a/src/mcp/server/mcpserver/resources/types.py +++ b/src/mcp/server/mcpserver/resources/types.py @@ -72,7 +72,7 @@ async def read(self) -> str | bytes: else: return pydantic_core.to_json(result, fallback=str, indent=2).decode() except Exception as e: - raise ValueError(f"Error reading resource {self.uri}: {e}") + raise ValueError(f"Error reading resource {self.uri}: {e}") from e @classmethod def from_function( @@ -148,7 +148,7 @@ async def read(self) -> str | bytes: return await anyio.to_thread.run_sync(self.path.read_bytes) return await anyio.to_thread.run_sync(self.path.read_text) except Exception as e: - raise ValueError(f"Error reading file {self.path}: {e}") + raise ValueError(f"Error reading file {self.path}: {e}") from e class HttpResource(Resource): @@ -193,7 +193,7 @@ def list_files(self) -> list[Path]: # pragma: no cover return list(self.path.glob(self.pattern)) if not self.recursive else list(self.path.rglob(self.pattern)) return list(self.path.glob("*")) if not self.recursive else list(self.path.rglob("*")) except Exception as e: - raise ValueError(f"Error listing directory {self.path}: {e}") + raise ValueError(f"Error listing directory {self.path}: {e}") from e async def read(self) -> str: # Always returns JSON string # pragma: no cover """Read the directory listing.""" @@ -202,4 +202,4 @@ async def read(self) -> str: # Always returns JSON string # pragma: no cover file_list = [str(f.relative_to(self.path)) for f in files if f.is_file()] return json.dumps({"files": file_list}, indent=2) except Exception as e: - raise ValueError(f"Error reading directory {self.path}: {e}") + raise ValueError(f"Error reading directory {self.path}: {e}") from e From d41b0bedac899f870c821746f8c93d3eb4e777fc Mon Sep 17 00:00:00 2001 From: Kian Khooban Date: Wed, 3 Jun 2026 18:50:49 -0400 Subject: [PATCH 2/2] fixup: address Copilot review comments - Remove duplicate pragma: no cover from raise line in session.py - Strip {e} from OAuth error messages to avoid leaking token data; the chained exception already carries full details for debugging --- src/mcp/client/auth/utils.py | 4 ++-- src/mcp/client/session.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mcp/client/auth/utils.py b/src/mcp/client/auth/utils.py index 12120dc095..216f46aaab 100644 --- a/src/mcp/client/auth/utils.py +++ b/src/mcp/client/auth/utils.py @@ -241,7 +241,7 @@ async def handle_registration_response(response: Response) -> OAuthClientInforma client_info = OAuthClientInformationFull.model_validate_json(content) return client_info except ValidationError as e: # pragma: no cover - raise OAuthRegistrationError(f"Invalid registration response: {e}") from e + raise OAuthRegistrationError("Invalid registration response") from e def is_valid_client_metadata_url(url: str | None) -> bool: @@ -334,4 +334,4 @@ async def handle_token_response_scopes( token_response = OAuthToken.model_validate_json(content) return token_response except ValidationError as e: # pragma: no cover - raise OAuthTokenError(f"Invalid token response: {e}") from e + raise OAuthTokenError("Invalid token response") from e diff --git a/src/mcp/client/session.py b/src/mcp/client/session.py index 8ee93b2789..83eeec5073 100644 --- a/src/mcp/client/session.py +++ b/src/mcp/client/session.py @@ -318,7 +318,7 @@ async def _validate_tool_result(self, name: str, result: types.CallToolResult) - except ValidationError as e: raise RuntimeError(f"Invalid structured content returned by tool {name}: {e}") from e except SchemaError as e: # pragma: no cover - raise RuntimeError(f"Invalid schema for tool {name}: {e}") from e # pragma: no cover + raise RuntimeError(f"Invalid schema for tool {name}: {e}") from e async def list_prompts(self, *, params: types.PaginatedRequestParams | None = None) -> types.ListPromptsResult: """Send a prompts/list request.