Skip to content

Commit fe31e62

Browse files
committed
fix(shared): make UrlElicitationRequiredError pickle-safe
Signed-off-by: ulofiai <monsterking@tutamail.com>
1 parent a4f4ccd commit fe31e62

2 files changed

Lines changed: 41 additions & 0 deletions

File tree

src/mcp/shared/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ def elicitations(self) -> list[ElicitRequestURLParams]:
107107
"""The list of URL elicitations required before the request can proceed."""
108108
return self._elicitations
109109

110+
def __reduce__(self) -> tuple[type, tuple[list[ElicitRequestURLParams], str]]:
111+
"""Support pickling by reconstructing with the original constructor signature."""
112+
return (self.__class__, (self._elicitations, self.message))
113+
110114
@classmethod
111115
def from_error(cls, error: ErrorData) -> UrlElicitationRequiredError:
112116
"""Reconstruct from an ErrorData received over the wire."""

tests/shared/test_exceptions.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Tests for MCP exception classes."""
22

3+
import pickle
4+
35
import pytest
46
from mcp_types import URL_ELICITATION_REQUIRED, ElicitRequestURLParams, ErrorData, JSONRPCError
57

@@ -173,3 +175,38 @@ def test_from_jsonrpc_error_preserves_code_message_and_data() -> None:
173175
)
174176
error = MCPError.from_jsonrpc_error(wire)
175177
assert error.error == ErrorData(code=URL_ELICITATION_REQUIRED, message="go elsewhere", data={"hint": "y"})
178+
179+
180+
def test_mcp_error_pickle_roundtrip() -> None:
181+
"""MCPError preserves its structured payload across a pickle round-trip."""
182+
original = MCPError(code=-32600, message="Invalid request", data={"detail": "bad"})
183+
184+
restored = pickle.loads(pickle.dumps(original))
185+
186+
assert type(restored) is MCPError
187+
assert restored.error == original.error
188+
189+
190+
def test_url_elicitation_required_error_pickle_roundtrip() -> None:
191+
"""UrlElicitationRequiredError preserves its typed state when pickled."""
192+
elicitations = [
193+
ElicitRequestURLParams(
194+
mode="url",
195+
message="First authorization",
196+
url="https://example.com/auth/first",
197+
elicitation_id="auth-1",
198+
),
199+
ElicitRequestURLParams(
200+
mode="url",
201+
message="Second authorization",
202+
url="https://example.com/auth/second",
203+
elicitation_id="auth-2",
204+
),
205+
]
206+
original = UrlElicitationRequiredError(elicitations, message="Authorization required")
207+
208+
restored = pickle.loads(pickle.dumps(original))
209+
210+
assert type(restored) is UrlElicitationRequiredError
211+
assert restored.error == original.error
212+
assert restored.elicitations == original.elicitations

0 commit comments

Comments
 (0)