Skip to content

Commit 07f4e3a

Browse files
bhosmer-antpraboud-ant
authored andcommitted
Fix stream resource leaks and upgrade Starlette
- Add docstring for custom_route method in FastMCP server - Fix stream resource leaks in SSE transport and streaming ASGI response - Upgrade Starlette to 0.46.0+ to remove multipart deprecation warning - Remove python-multipart dependency which is now included in Starlette
1 parent d3725cf commit 07f4e3a

File tree

4 files changed

+22
-22
lines changed

4 files changed

+22
-22
lines changed

pyproject.toml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ dependencies = [
2626
"httpx>=0.27",
2727
"httpx-sse>=0.4",
2828
"pydantic>=2.7.2,<3.0.0",
29-
"starlette>=0.27",
29+
"starlette>=0.46",
3030
"sse-starlette>=1.6.1",
3131
"pydantic-settings>=2.5.2",
3232
"uvicorn>=0.23.1; sys_platform != 'emscripten'",
@@ -110,12 +110,8 @@ mcp = { workspace = true }
110110
xfail_strict = true
111111
filterwarnings = [
112112
"error",
113-
# this is a long-standing issue with fastmcp, which is just now being exercised by tests
114-
"ignore:Unclosed:ResourceWarning",
115113
# This should be fixed on Uvicorn's side.
116114
"ignore::DeprecationWarning:websockets",
117115
"ignore:websockets.server.WebSocketServerProtocol is deprecated:DeprecationWarning",
118116
"ignore:Returning str or bytes.*:DeprecationWarning:mcp.server.lowlevel",
119-
# this is a problem in starlette
120-
"ignore:Please use `import python_multipart` instead.:PendingDeprecationWarning",
121117
]

src/mcp/server/sse.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,17 @@ async def sse_writer():
120120
}
121121
)
122122

123-
async with anyio.create_task_group() as tg:
124-
response = EventSourceResponse(
125-
content=sse_stream_reader, data_sender_callable=sse_writer
126-
)
127-
logger.debug("Starting SSE response task")
128-
tg.start_soon(response, scope, receive, send)
129-
130-
logger.debug("Yielding read and write streams")
131-
yield (read_stream, write_stream, response)
123+
# Ensure all streams are properly closed
124+
async with read_stream, write_stream, read_stream_writer, sse_stream_reader:
125+
async with anyio.create_task_group() as tg:
126+
response = EventSourceResponse(
127+
content=sse_stream_reader, data_sender_callable=sse_writer
128+
)
129+
logger.debug("Starting SSE response task")
130+
tg.start_soon(response, scope, receive, send)
131+
132+
logger.debug("Yielding read and write streams")
133+
yield (read_stream, write_stream, response)
132134

133135
async def handle_post_message(
134136
self, scope: Scope, receive: Receive, send: Send

src/mcp/server/streaming_asgi_transport.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ async def process_messages() -> None:
173173
# Ensure events are set even if there's an error
174174
initial_response_ready.set()
175175
response_complete.set()
176+
await content_send_channel.aclose()
176177

177178
# Create tasks for running the app and processing messages
178179
self.task_group.start_soon(run_app)
@@ -205,5 +206,8 @@ def __init__(
205206
self.receive_channel = receive_channel
206207

207208
async def __aiter__(self) -> typing.AsyncIterator[bytes]:
208-
async for chunk in self.receive_channel:
209-
yield chunk
209+
try:
210+
async for chunk in self.receive_channel:
211+
yield chunk
212+
finally:
213+
await self.receive_channel.aclose()

uv.lock

Lines changed: 4 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)