Skip to content

Commit bfb5a77

Browse files
committed
refactor: rename Outbound.send_request to send_raw_request
The dispatcher-layer raw channel is now `send_raw_request(method, params) -> dict`. This frees the `send_request` name for the typed surface (`send_request(req: Request) -> Result`) that Connection/Context/Client add in later PRs. Mechanical rename across Outbound, Dispatcher, DispatchContext, DirectDispatcher, _DirectDispatchContext, and all tests. `can_send_request` (the transport capability flag) is unchanged — it names the capability, not the method.
1 parent 1da25ec commit bfb5a77

5 files changed

Lines changed: 36 additions & 35 deletions

File tree

src/mcp/shared/direct_dispatcher.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class _DirectDispatchContext:
4040
"""`DispatchContext` for an inbound request on a `DirectDispatcher`.
4141
4242
The back-channel callables target the *originating* side, so a handler's
43-
`send_request` reaches the peer that made the inbound request.
43+
`send_raw_request` reaches the peer that made the inbound request.
4444
"""
4545

4646
transport: TransportContext
@@ -52,7 +52,7 @@ class _DirectDispatchContext:
5252
async def notify(self, method: str, params: Mapping[str, Any] | None) -> None:
5353
await self._back_notify(method, params)
5454

55-
async def send_request(
55+
async def send_raw_request(
5656
self,
5757
method: str,
5858
params: Mapping[str, Any] | None,
@@ -71,7 +71,7 @@ class DirectDispatcher:
7171
"""A `Dispatcher` that calls a peer's handlers directly, in-process.
7272
7373
Two instances are wired together with `create_direct_dispatcher_pair`; each
74-
holds a reference to the other. `send_request` on one awaits the peer's
74+
holds a reference to the other. `send_raw_request` on one awaits the peer's
7575
`on_request`. `run` parks until `close` is called.
7676
"""
7777

@@ -86,7 +86,7 @@ def __init__(self, transport_ctx: TransportContext):
8686
def connect_to(self, peer: DirectDispatcher) -> None:
8787
self._peer = peer
8888

89-
async def send_request(
89+
async def send_raw_request(
9090
self,
9191
method: str,
9292
params: Mapping[str, Any] | None,

src/mcp/shared/dispatcher.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
A Dispatcher turns a duplex message channel into two things:
44
5-
* an outbound API: ``send_request(method, params)`` and ``notify(method, params)``
5+
* an outbound API: ``send_raw_request(method, params)`` and ``notify(method, params)``
66
* an inbound pump: ``run(on_request, on_notify)`` that drives the receive loop
77
and invokes the supplied handlers for each incoming request/notification
88
@@ -44,7 +44,7 @@ async def __call__(self, progress: float, total: float | None, message: str | No
4444

4545

4646
class CallOptions(TypedDict, total=False):
47-
"""Per-call options for `Outbound.send_request`.
47+
"""Per-call options for `Outbound.send_raw_request`.
4848
4949
All keys are optional. Dispatchers ignore keys they do not understand.
5050
"""
@@ -67,17 +67,18 @@ class Outbound(Protocol):
6767
"""Anything that can send requests and notifications to the peer.
6868
6969
Both `Dispatcher` (top-level outbound) and `DispatchContext` (back-channel
70-
during an inbound request) extend this. `PeerMixin` wraps an `Outbound` to
71-
provide typed MCP request/notification methods.
70+
during an inbound request) extend this. The MCP type layer (`PeerMixin`,
71+
`Connection`, `Context`) builds typed ``send_request`` / convenience methods
72+
on top of this raw channel.
7273
"""
7374

74-
async def send_request(
75+
async def send_raw_request(
7576
self,
7677
method: str,
7778
params: Mapping[str, Any] | None,
7879
opts: CallOptions | None = None,
7980
) -> dict[str, Any]:
80-
"""Send a request and await its result.
81+
"""Send a request and await its raw result dict.
8182
8283
Raises:
8384
MCPError: If the peer responded with an error, or the handler
@@ -96,7 +97,7 @@ class DispatchContext(Outbound, Protocol[TransportT_co]):
9697
9798
Carries the transport metadata for the inbound message and provides the
9899
back-channel for sending requests/notifications to the peer while handling
99-
it. `send_request` raises `NoBackChannelError` if
100+
it. `send_raw_request` raises `NoBackChannelError` if
100101
``transport.can_send_request`` is ``False``.
101102
"""
102103

src/mcp/shared/exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class NoBackChannelError(MCPError):
4646
4747
Stateless HTTP and JSON-response-mode HTTP have no channel for the server to
4848
push requests (sampling, elicitation, roots/list) to the client. This is
49-
raised by `DispatchContext.send_request` when `transport.can_send_request`
49+
raised by `DispatchContext.send_raw_request` when `transport.can_send_request`
5050
is ``False``, and serializes to an ``INVALID_REQUEST`` error response.
5151
"""
5252

src/mcp/shared/transport_context.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,5 @@ class TransportContext:
2626
2727
``False`` for stateless HTTP and HTTP with JSON response mode; ``True`` for
2828
stdio, SSE, and stateful streamable HTTP. When ``False``,
29-
`DispatchContext.send_request` raises `NoBackChannelError`.
29+
`DispatchContext.send_raw_request` raises `NoBackChannelError`.
3030
"""

tests/shared/test_dispatcher.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -67,44 +67,44 @@ async def running_pair(
6767

6868

6969
@pytest.mark.anyio
70-
async def test_send_request_returns_result_from_peer_on_request():
70+
async def test_send_raw_request_returns_result_from_peer_on_request():
7171
async with running_pair() as (client, _server, _crec, srec):
7272
with anyio.fail_after(5):
73-
result = await client.send_request("tools/list", {"cursor": "abc"})
73+
result = await client.send_raw_request("tools/list", {"cursor": "abc"})
7474
assert result == {"echoed": "tools/list", "params": {"cursor": "abc"}}
7575
assert srec.requests == [("tools/list", {"cursor": "abc"})]
7676

7777

7878
@pytest.mark.anyio
79-
async def test_send_request_reraises_mcperror_from_handler_unchanged():
79+
async def test_send_raw_request_reraises_mcperror_from_handler_unchanged():
8080
async def on_request(
8181
ctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None
8282
) -> dict[str, Any]:
8383
raise MCPError(code=INVALID_PARAMS, message="bad cursor")
8484

8585
async with running_pair(server_on_request=on_request) as (client, *_):
8686
with anyio.fail_after(5), pytest.raises(MCPError) as exc:
87-
await client.send_request("tools/list", {})
87+
await client.send_raw_request("tools/list", {})
8888
assert exc.value.error.code == INVALID_PARAMS
8989
assert exc.value.error.message == "bad cursor"
9090

9191

9292
@pytest.mark.anyio
93-
async def test_send_request_wraps_non_mcperror_exception_as_internal_error():
93+
async def test_send_raw_request_wraps_non_mcperror_exception_as_internal_error():
9494
async def on_request(
9595
ctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None
9696
) -> dict[str, Any]:
9797
raise ValueError("oops")
9898

9999
async with running_pair(server_on_request=on_request) as (client, *_):
100100
with anyio.fail_after(5), pytest.raises(MCPError) as exc:
101-
await client.send_request("tools/list", {})
101+
await client.send_raw_request("tools/list", {})
102102
assert exc.value.error.code == INTERNAL_ERROR
103103
assert isinstance(exc.value.__cause__, ValueError)
104104

105105

106106
@pytest.mark.anyio
107-
async def test_send_request_with_timeout_raises_mcperror_request_timeout():
107+
async def test_send_raw_request_with_timeout_raises_mcperror_request_timeout():
108108
async def on_request(
109109
ctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None
110110
) -> dict[str, Any]:
@@ -113,7 +113,7 @@ async def on_request(
113113

114114
async with running_pair(server_on_request=on_request) as (client, *_):
115115
with anyio.fail_after(5), pytest.raises(MCPError) as exc:
116-
await client.send_request("slow", None, {"timeout": 0})
116+
await client.send_raw_request("slow", None, {"timeout": 0})
117117
assert exc.value.error.code == REQUEST_TIMEOUT
118118

119119

@@ -127,32 +127,32 @@ async def test_notify_invokes_peer_on_notify():
127127

128128

129129
@pytest.mark.anyio
130-
async def test_ctx_send_request_round_trips_to_calling_side():
131-
"""A handler's ctx.send_request reaches the side that made the inbound request."""
130+
async def test_ctx_send_raw_request_round_trips_to_calling_side():
131+
"""A handler's ctx.send_raw_request reaches the side that made the inbound request."""
132132

133133
async def server_on_request(
134134
ctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None
135135
) -> dict[str, Any]:
136-
sample = await ctx.send_request("sampling/createMessage", {"prompt": "hi"})
136+
sample = await ctx.send_raw_request("sampling/createMessage", {"prompt": "hi"})
137137
return {"sampled": sample}
138138

139139
async with running_pair(server_on_request=server_on_request) as (client, _server, crec, _srec):
140140
with anyio.fail_after(5):
141-
result = await client.send_request("tools/call", None)
141+
result = await client.send_raw_request("tools/call", None)
142142
assert crec.requests == [("sampling/createMessage", {"prompt": "hi"})]
143143
assert result == {"sampled": {"echoed": "sampling/createMessage", "params": {"prompt": "hi"}}}
144144

145145

146146
@pytest.mark.anyio
147-
async def test_ctx_send_request_raises_nobackchannelerror_when_transport_disallows():
147+
async def test_ctx_send_raw_request_raises_nobackchannelerror_when_transport_disallows():
148148
async def server_on_request(
149149
ctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None
150150
) -> dict[str, Any]:
151-
return await ctx.send_request("sampling/createMessage", None)
151+
return await ctx.send_raw_request("sampling/createMessage", None)
152152

153153
async with running_pair(server_on_request=server_on_request, can_send_request=False) as (client, *_):
154154
with anyio.fail_after(5), pytest.raises(NoBackChannelError) as exc:
155-
await client.send_request("tools/call", None)
155+
await client.send_raw_request("tools/call", None)
156156
assert exc.value.method == "sampling/createMessage"
157157
assert exc.value.error.code == INVALID_REQUEST
158158

@@ -167,7 +167,7 @@ async def server_on_request(
167167

168168
async with running_pair(server_on_request=server_on_request) as (client, _server, crec, _srec):
169169
with anyio.fail_after(5):
170-
await client.send_request("tools/call", None)
170+
await client.send_raw_request("tools/call", None)
171171
await crec.notified.wait()
172172
assert crec.notifications == [("notifications/message", {"level": "info"})]
173173

@@ -187,12 +187,12 @@ async def on_progress(progress: float, total: float | None, message: str | None)
187187

188188
async with running_pair(server_on_request=server_on_request) as (client, *_):
189189
with anyio.fail_after(5):
190-
await client.send_request("tools/call", None, {"on_progress": on_progress})
190+
await client.send_raw_request("tools/call", None, {"on_progress": on_progress})
191191
assert received == [(0.5, 1.0, "halfway")]
192192

193193

194194
@pytest.mark.anyio
195-
async def test_send_request_issued_before_peer_run_blocks_until_peer_ready():
195+
async def test_send_raw_request_issued_before_peer_run_blocks_until_peer_ready():
196196
client, server = create_direct_dispatcher_pair()
197197
s_req, s_notify = echo_handlers(Recorder())
198198
c_req, c_notify = echo_handlers(Recorder())
@@ -205,7 +205,7 @@ async def late_start():
205205
tg.start_soon(client.run, c_req, c_notify)
206206
tg.start_soon(late_start)
207207
with anyio.fail_after(5):
208-
result = await client.send_request("ping", None)
208+
result = await client.send_raw_request("ping", None)
209209
assert result == {"echoed": "ping", "params": {}}
210210
client.close()
211211
server.close()
@@ -221,15 +221,15 @@ async def server_on_request(
221221

222222
async with running_pair(server_on_request=server_on_request) as (client, *_):
223223
with anyio.fail_after(5):
224-
result = await client.send_request("tools/call", None)
224+
result = await client.send_raw_request("tools/call", None)
225225
assert result == {"ok": True}
226226

227227

228228
@pytest.mark.anyio
229-
async def test_send_request_and_notify_raise_runtimeerror_when_no_peer_connected():
229+
async def test_send_raw_request_and_notify_raise_runtimeerror_when_no_peer_connected():
230230
d = DirectDispatcher(TransportContext(kind="direct", can_send_request=True))
231231
with pytest.raises(RuntimeError, match="no peer"):
232-
await d.send_request("ping", None)
232+
await d.send_raw_request("ping", None)
233233
with pytest.raises(RuntimeError, match="no peer"):
234234
await d.notify("ping", None)
235235

0 commit comments

Comments
 (0)