|
10 | 10 | import httpx2 |
11 | 11 | import pytest |
12 | 12 | from mcp_types import INVALID_REQUEST, ListToolsResult, PaginatedRequestParams |
13 | | -from starlette.types import Message, Receive, Scope, Send |
| 13 | +from starlette.types import Message, Scope |
14 | 14 |
|
15 | 15 | from mcp import Client |
16 | 16 | from mcp.client.streamable_http import streamable_http_client |
17 | 17 | from mcp.server import Server, ServerRequestContext, streamable_http_manager |
18 | 18 | from mcp.server.auth.middleware.bearer_auth import AuthenticatedUser |
19 | 19 | from mcp.server.auth.provider import AccessToken |
20 | 20 | from mcp.server.streamable_http import MCP_SESSION_ID_HEADER, StreamableHTTPServerTransport |
21 | | -from mcp.server.streamable_http_manager import ( |
22 | | - DEFAULT_MAX_REQUEST_BODY_SIZE, |
23 | | - RequestBodyLimitMiddleware, |
24 | | - StreamableHTTPSessionManager, |
25 | | -) |
| 21 | +from mcp.server.streamable_http_manager import DEFAULT_MAX_REQUEST_BODY_SIZE, StreamableHTTPSessionManager |
26 | 22 |
|
27 | 23 |
|
28 | 24 | @pytest.mark.anyio |
@@ -146,114 +142,6 @@ async def send(message: Message) -> None: |
146 | 142 | assert response_start["status"] == 413 |
147 | 143 |
|
148 | 144 |
|
149 | | -@pytest.mark.anyio |
150 | | -async def test_client_disconnect_while_streaming_request_body_is_replayed() -> None: |
151 | | - """SDK-defined: raw ASGI is required to prove a disconnect before body completion reaches the transport.""" |
152 | | - disconnect: Message = {"type": "http.disconnect"} |
153 | | - request_messages: Iterator[Message] = iter( |
154 | | - [{"type": "http.request", "body": b"1234", "more_body": True}, disconnect] |
155 | | - ) |
156 | | - received_messages: list[Message] = [] |
157 | | - |
158 | | - async def receive() -> Message: |
159 | | - return next(request_messages) |
160 | | - |
161 | | - async def app(scope: Scope, receive: Receive, send: Send) -> None: |
162 | | - received_messages.append(await receive()) |
163 | | - received_messages.append(await receive()) |
164 | | - |
165 | | - scope: Scope = {"type": "http", "method": "POST", "path": "/mcp", "headers": []} |
166 | | - middleware = RequestBodyLimitMiddleware(app, max_body_size=8) |
167 | | - |
168 | | - await middleware(scope, receive, AsyncMock()) |
169 | | - |
170 | | - assert received_messages == [ |
171 | | - {"type": "http.request", "body": b"1234", "more_body": True}, |
172 | | - disconnect, |
173 | | - ] |
174 | | - |
175 | | - |
176 | | -@pytest.mark.anyio |
177 | | -async def test_client_disconnect_before_request_body_is_replayed() -> None: |
178 | | - """SDK-defined: raw ASGI proves a disconnect before the first body message reaches the transport.""" |
179 | | - disconnect: Message = {"type": "http.disconnect"} |
180 | | - received_messages: list[Message] = [] |
181 | | - |
182 | | - async def receive() -> Message: |
183 | | - return disconnect |
184 | | - |
185 | | - async def app(scope: Scope, receive: Receive, send: Send) -> None: |
186 | | - received_messages.append(await receive()) |
187 | | - |
188 | | - scope: Scope = {"type": "http", "method": "POST", "path": "/mcp", "headers": []} |
189 | | - middleware = RequestBodyLimitMiddleware(app, max_body_size=8) |
190 | | - |
191 | | - await middleware(scope, receive, AsyncMock()) |
192 | | - |
193 | | - assert received_messages == [disconnect] |
194 | | - |
195 | | - |
196 | | -@pytest.mark.anyio |
197 | | -async def test_request_body_chunks_are_replayed_as_one_message() -> None: |
198 | | - """SDK-defined: raw ASGI proves chunk overhead is discarded before the body reaches the transport.""" |
199 | | - request_messages: Iterator[Message] = iter( |
200 | | - [ |
201 | | - {"type": "http.request", "body": b"12", "more_body": True}, |
202 | | - {"type": "http.request", "body": b"34", "more_body": True}, |
203 | | - {"type": "http.request", "body": b"56", "more_body": False}, |
204 | | - ] |
205 | | - ) |
206 | | - received_messages: list[Message] = [] |
207 | | - |
208 | | - async def receive() -> Message: |
209 | | - return next(request_messages) |
210 | | - |
211 | | - async def app(scope: Scope, receive: Receive, send: Send) -> None: |
212 | | - received_messages.append(await receive()) |
213 | | - |
214 | | - scope: Scope = {"type": "http", "method": "POST", "path": "/mcp", "headers": []} |
215 | | - middleware = RequestBodyLimitMiddleware(app, max_body_size=8) |
216 | | - |
217 | | - await middleware(scope, receive, AsyncMock()) |
218 | | - |
219 | | - assert received_messages == [{"type": "http.request", "body": b"123456", "more_body": False}] |
220 | | - |
221 | | - |
222 | | -@pytest.mark.anyio |
223 | | -@pytest.mark.parametrize("method", ["GET", "PUT", "OPTIONS", "HEAD", "DELETE"]) |
224 | | -async def test_request_body_limit_applies_to_every_method(method: str) -> None: |
225 | | - """SDK-defined: the limit is a property of the request body, not of the method that carries it.""" |
226 | | - app = AsyncMock() |
227 | | - sent_messages: list[Message] = [] |
228 | | - receive = AsyncMock(return_value={"type": "http.request", "body": b"123456789", "more_body": False}) |
229 | | - |
230 | | - async def send(message: Message) -> None: |
231 | | - sent_messages.append(message) |
232 | | - |
233 | | - scope: Scope = {"type": "http", "method": method, "path": "/mcp", "headers": []} |
234 | | - middleware = RequestBodyLimitMiddleware(app, max_body_size=8) |
235 | | - |
236 | | - await middleware(scope, receive, send) |
237 | | - |
238 | | - assert [message["status"] for message in sent_messages if message["type"] == "http.response.start"] == [413] |
239 | | - app.assert_not_awaited() |
240 | | - |
241 | | - |
242 | | -@pytest.mark.anyio |
243 | | -async def test_request_body_limit_leaves_non_http_scopes_alone() -> None: |
244 | | - """SDK-defined: only HTTP requests carry a body to limit; other ASGI scopes go straight to the app.""" |
245 | | - app = AsyncMock() |
246 | | - receive = AsyncMock() |
247 | | - send = AsyncMock() |
248 | | - scope: Scope = {"type": "lifespan"} |
249 | | - middleware = RequestBodyLimitMiddleware(app, max_body_size=8) |
250 | | - |
251 | | - await middleware(scope, receive, send) |
252 | | - |
253 | | - app.assert_awaited_once_with(scope, receive, send) |
254 | | - receive.assert_not_awaited() |
255 | | - |
256 | | - |
257 | 145 | def test_request_body_limit_defaults_to_four_mib() -> None: |
258 | 146 | """SDK-defined: Streamable HTTP request bodies are limited to 4 MiB by default.""" |
259 | 147 | manager = StreamableHTTPSessionManager(app=Server("test-default-size-limit")) |
|
0 commit comments