Skip to content

Commit dd2d770

Browse files
committed
docs: document ValidationError from typed request result validation
A peer result that fails model validation raises pydantic.ValidationError out of send_request and the peer convenience methods; list it in the Raises sections and pin the behavior with a test. ServerSession.send_request was also missing MCPError in its Raises section.
1 parent bde30c1 commit dd2d770

4 files changed

Lines changed: 15 additions & 0 deletions

File tree

src/mcp/server/_typed_request.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ async def send_request(
7979
Raises:
8080
MCPError: The peer responded with an error.
8181
NoBackChannelError: No back-channel for server-initiated requests.
82+
pydantic.ValidationError: The peer's result does not match the expected result type.
8283
KeyError: `result_type` omitted for a non-spec request type.
8384
"""
8485
raw = await self.send_raw_request(req.method, dump_params(req.params), opts)

src/mcp/server/session.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,11 @@ async def send_request(
6565
streamable HTTP; it is the only metadata field honored here.
6666
6767
Raises:
68+
MCPError: The peer responded with an error.
6869
NoBackChannelError: If there is no related request to ride on and
6970
the connection has no standalone channel (stateless HTTP), so
7071
a response could never arrive.
72+
pydantic.ValidationError: The peer's result does not match `result_type`.
7173
"""
7274
data = request.model_dump(by_alias=True, mode="json", exclude_none=True)
7375
opts: CallOptions = {}

src/mcp/shared/peer.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ async def sample(
125125
MCPError: The peer responded with an error.
126126
NoBackChannelError: The host's transport context has no
127127
back-channel for server-initiated requests.
128+
pydantic.ValidationError: The peer's result does not match the expected result type.
128129
"""
129130
params = CreateMessageRequestParams(
130131
messages=messages,
@@ -156,6 +157,7 @@ async def elicit_form(
156157
Raises:
157158
MCPError: The peer responded with an error.
158159
NoBackChannelError: No back-channel for server-initiated requests.
160+
pydantic.ValidationError: The peer's result does not match the expected result type.
159161
"""
160162
params = ElicitRequestFormParams(message=message, requested_schema=requested_schema)
161163
result = await self.send_raw_request("elicitation/create", dump_params(params, meta), opts)
@@ -175,6 +177,7 @@ async def elicit_url(
175177
Raises:
176178
MCPError: The peer responded with an error.
177179
NoBackChannelError: No back-channel for server-initiated requests.
180+
pydantic.ValidationError: The peer's result does not match the expected result type.
178181
"""
179182
params = ElicitRequestURLParams(message=message, url=url, elicitation_id=elicitation_id)
180183
result = await self.send_raw_request("elicitation/create", dump_params(params, meta), opts)
@@ -188,6 +191,7 @@ async def list_roots(
188191
Raises:
189192
MCPError: The peer responded with an error.
190193
NoBackChannelError: No back-channel for server-initiated requests.
194+
pydantic.ValidationError: The peer's result does not match the expected result type.
191195
"""
192196
result = await self.send_raw_request("roots/list", dump_params(None, meta), opts)
193197
return ListRootsResult.model_validate(result, by_name=False)

tests/server/test_connection.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import anyio
1414
import pytest
15+
from pydantic import ValidationError
1516

1617
from mcp.server.connection import Connection
1718
from mcp.shared.dispatcher import CallOptions
@@ -136,6 +137,13 @@ async def test_connection_send_request_with_result_type_kwarg_validates_custom_t
136137
assert isinstance(result, EmptyResult)
137138

138139

140+
@pytest.mark.anyio
141+
async def test_connection_send_request_nonconforming_result_raises_validation_error():
142+
conn = Connection(StubOutbound(result={"bogus": 1}), has_standalone_channel=True)
143+
with pytest.raises(ValidationError):
144+
await conn.send_request(ListRootsRequest())
145+
146+
139147
@pytest.mark.anyio
140148
async def test_connection_ping_sends_ping_on_standalone():
141149
out = StubOutbound()

0 commit comments

Comments
 (0)