Repository navigation
FastMCP server with SSE transport fails to shut down on a signal #514
Copy link
Copy link
Open
Labels
P3Nice to haves, rare edge casesNice to haves, rare edge casesbugSomething isn't workingSomething isn't workingready for workEnough information for someone to start working onEnough information for someone to start working on
Description
A very simple server fails to shut down on a signal if it processed at least one request:
❯ python test-server.py --port=8085
INFO: Started server process [216035]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8085 (Press CTRL+C to quit)
INFO: 127.0.0.1:55188 - "GET /sse HTTP/1.1" 200 OK
INFO: 127.0.0.1:55192 - "POST /messages/?session_id=e690de9733914d09aebf2b0e8c78191c HTTP/1.1" 202 Accepted
INFO: 127.0.0.1:55192 - "POST /messages/?session_id=e690de9733914d09aebf2b0e8c78191c HTTP/1.1" 202 Accepted
INFO: 127.0.0.1:55192 - "POST /messages/?session_id=e690de9733914d09aebf2b0e8c78191c HTTP/1.1" 202 Accepted
Processing request of type CallToolRequest
^CINFO: Shutting down
INFO: Waiting for background tasks to complete. (CTRL+C to force quit)
The expected behaviour would be:
❯ python test-server.py --port=8085
INFO: Started server process [216006]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8085 (Press CTRL+C to quit)
^CINFO: Shutting down
INFO: Waiting for application shutdown.
INFO: Application shutdown complete.
INFO: Finished server process [216006]
Here is the code for both the server and the client.
What do I miss?
the server
import click
import sys
from pydantic import Field
from mcp.server.fastmcp import FastMCP
@click.command()
@click.option("--port", default=8085, help="Port to listen", type=int)
def main(port: int):
mcp = FastMCP(
"mcp-echo-tool",
debug=True,
log_level="INFO",
port=port,
)
@mcp.tool(name="echo")
async def echo_tool(string: str = Field(description="A string to echo back")) -> str:
"""Echoes back the input string"""
return string
mcp.run(transport='sse')
return 0
if __name__ == "__main__":
sys.exit(main())
and the client
import asyncio
import click
import sys
from mcp.client.session import ClientSession
from mcp.client.sse import sse_client
async def client(url, string):
async with sse_client(url) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool("echo", {"string": string})
print(result.content)
@click.command()
@click.option('--url', default='http://localhost:8085', help='MCP Server URL', type=str)
@click.argument('string', type=str)
def main(url: str, string: str):
asyncio.run(client(url, string))
if __name__ == "__main__":
sys.exit(main())
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
P3Nice to haves, rare edge casesNice to haves, rare edge casesbugSomething isn't workingSomething isn't workingready for workEnough information for someone to start working onEnough information for someone to start working on
Activity
withwind8 commentedon Apr 15, 2025
I think uvicorn is waiting for close opened sse connection.
But I've no idea how to do it with fastmcp.
Can anyone help?
nadeemcite commentedon Apr 15, 2025
It’s unclear if there’s a manual way to disconnect from the session. The session gets created when you attempt to connect using an MCP client, and even the MCP Inspector doesn’t terminate the session when "Disconnect" is clicked.
evalstate commentedon Apr 15, 2025
deleted previous message as thought it was raised on my project! - i have been scratching my head on this one...! would be good to get fixed.
improved shutdown handling. review along with: modelcontextprotocol/p…
improve sse server shutdown handling - related to modelcontextprotoco…
growler commentedon Apr 16, 2025
... I would also like to add that if the MCP server has at least one real established connection, it fails to shut down even with forced Ctrl-C. The only way is to kill the process.
sealpp commentedon Apr 23, 2025
I'm experiencing the same issue. Initially, I suspected it might be caused by the Auto Forward Ports feature of the VS Code Remote plugin. However, after disabling automatic port forwarding, the problem persists.
AlfredYyong commentedon Apr 23, 2025
so this issue has been resolved?
PWZER commentedon Apr 25, 2025
Fixed in #586
pranftw commentedon May 2, 2025
Also an automatic timeout would be a useful feature. If the client has connected, but is no longer actively sending messages, then terminate the connection after specified time.
lanstonchu commentedon Aug 8, 2025
for @pranftw 's suggestion of adding automatic timeout, I think we should add
timeout_graceful_shutdown=3torun_streamable_http_async()?So it will become:-
voidwisp commentedon Sep 29, 2025
Still experiencing this issue with:
Name: mcp
Version: 1.14.0
Summary: Model Context Protocol SDK
Cursor is holding the connection and not letting go.. Repeated attempts to Ctrl+C does not help.
Shanek2k25k commentedon Nov 28, 2025
@lanstonchu Thanks!!!
ooo1208 commentedon Sep 4, 2026
Hi! I'd like to work on this issue.
I reviewed #612 and the earlier attempts in #518, #582, and #586. #612 handles a client disconnect, while #514 is about server shutdown while an SSE connection remains active. My plan is to reproduce that remaining case against the current codebase, add a focused regression test, and implement the smallest transport-scoped fix. I will avoid a global forced-shutdown timeout and won't require a dependency upgrade.
Could a maintainer assign #514 to me if this direction matches the expected scope?