Repository navigation
[v2] Allow MCPServer to disable subscriptions/listen #3357
Description
Initial Checks
- I confirm that I'm using the newest release of my line (the latest 2.x, or the latest 1.x if I'm still on v1)
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Release line
2.x (current stable)
Description
What happened?
MCPServer always installs a subscriptions/listen handler:
on_subscriptions_listen=ListenHandler(self._subscriptions)The constructor exposes subscriptions: SubscriptionBus | None = None, but None creates an InMemorySubscriptionBus; it does not disable subscriptions.
This causes server/discover to advertise:
{
"tools": {"listChanged": true},
"prompts": {"listChanged": true},
"resources": {"listChanged": true, "subscribe": true}
}Our MCP server has a static catalog and never publishes change notifications. It runs as a stateless Streamable HTTP server on AWS Lambda.
Claude Code 2.1.235 automatically opened four subscriptions/listen streams during discovery. Each request followed the long-lived SSE path and held a Lambda invocation until timeout.
We currently reject these requests in ASGI middleware. This prevents the timeout, but the server still advertises subscription support, so clients continue attempting the requests.
The low-level Server supports disabling this correctly through:
Server(..., on_subscriptions_listen=None)The high-level MCPServer has no equivalent public option. Removing the handler through _lowlevel_server._request_handlers works, but relies on private internals.
What did you expect?
MCPServer should provide a supported opt-out, for example:
MCPServer(..., subscriptions=False)or:
MCPServer(..., enable_subscriptions=False)When disabled:
subscriptions/listenshould not be registered.- server/discover should advertise the relevant change and subscription capabilities as false.
- An unsolicited subscriptions/listen call should return Method not found.
- Existing behavior should remain the default for compatibility.
This matches SEP-2575’s stateless-first, “pay as you go” design. Long-lived streams should only be enabled when the server uses them.
Area
Server
References
AI assistance disclosure: I used an AI coding assistant to inspect the SDK source and help draft this report. I reviewed and verified the contents.
Example Code
from mcp.server import MCPServer
server = MCPServer("static-server")
capabilities = server._lowlevel_server.get_capabilities(
protocol_version="2026-07-28"
)
print(capabilities.model_dump(by_alias=True, exclude_none=True))Python & MCP Python SDK
Python 3.11
mcp 2.0.0
AWS Lambda
Streamable HTTP
json_response=True
stateless_http=True
Activity
mikerva commentedon Aug 21, 2026
I had this exact issue yesterday as well. Bump! ^^
thijn-minerva commentedon Aug 21, 2026
Wow! Coincidentally I have the exact same issue! Would really love this resolved, cause leads to some confusing situations
owlofjaylan commentedon Aug 24, 2026
Might actually be better off mirroring the Go SDK here and passing
ServerCapabilitiesas a parameter instead ofenable_subscriptions=False.Serverdoesn't currently take capabilities as param, but this should be an easy fix.tomoki-takahashi-oisix commentedon Sep 14, 2026
Independent reproduction of the same problem, same stack (stateless Streamable HTTP on AWS Lambda, clients = Claude Code 2.1.2xx and a hosted connector). Details, a Lambda-free reproduction script and production numbers are in #3493 — filed before I found this issue, so treat #3357 as the primary and #3493 as supporting data:
subscriptions/listenPOSTs in a 30-minute sample never completed (ack event, then the stream stays open); every other method completed in milliseconds.server/discoveradvertisedlistChanged: true/subscribe: truefor a server that never publishes anything.Our workaround (works on 2.1.1 and 2.2.0, but relies on private attributes):
mcp._lowlevel_server._request_handlers.pop("subscriptions/listen"). With the handler gone,get_capabilities()flips tolistChanged: false / subscribe: falseand a listen POST gets the standard404+-32601; the TypeScript client treats that as a soft error and the connection keeps working.+1 to a public knob. Either an explicit flag or a
ServerCapabilitiesparameter (as suggested above) would do, as long asget_capabilities()follows it.1320800521 commentedon Sep 20, 2026
Independent verification from XBSTACK on 2026-09-20 confirms the same behavior on both
mcp==2.1.1andmcp==2.2.0with a minimal local Streamable HTTP harness (no Lambda required).Observed:
server/discoveradvertiseslistChanged/subscribe=trueby default.subscriptions/listenstays open beyond the test window when no disconnect is propagated.subscriptions/listenhandler flips the advertised subscription capability off and the same listen call returns-32601 Method not foundimmediately._lowlevel_server._request_handlers, so it is not a supported long-term solution.Repro, logs, and version matrix:
https://github.com/xbstack/mcp-subscriptions-listen-serverless-repro
Related deployment notes:
https://www.xbstack.com/ai/mcp-streamable-http-deployment/?utm_source=github&utm_medium=referral&utm_campaign=mcp_subscriptions_listen_serverless&utm_content=issue_reply&ref=github
This supports the request for a public capability/opt-out surface rather than relying on middleware rejection or private handler mutation.
mikerva commentedon Oct 1, 2026
I had this exact issue yesterday as well. Bump! ^^
maxisbey commentedon Oct 2, 2026
MCPServer(..., subscriptions=False)landed in #3626. Thanks for the clear report, and to everyone who added numbers and repros.It leaves
subscriptions/listenunregistered, soserver/discoverreports thelistChangedandsubscribeflags as false and a stray listen call gets Method not found. The default is unchanged. Turning it off has an example.Once it's released, you can drop the
_lowlevel_server._request_handlersworkaround.The
ServerCapabilities-style parameter suggested above isn't part of this. It fits better with the capabilities work in #2896.If it doesn't cover your setup, feel free to open a new issue.
AI Disclaimer
1320800521 commentedon Oct 8, 2026
XBSTACK follow-up to our September
mcp==2.1.1/2.2.0reproductions: the official Python SDK v2.3.0 release notes now explicitly includeMCPServer(subscriptions=False)from #3626. For a static/serverless MCP server, this is the supported opt-out to test instead of deleting_lowlevel_server._request_handlersprivately. After upgrading, please verifyserver/discoveradvertises the expected false capabilities, an unsolicitedsubscriptions/listenis rejected, and your real client and adapter behave correctly. Note the v2.3.0httpx2>=2.10.0requirement too.Our existing measurements cover 2.1.1 / 2.2.0 only; we have not yet rerun a production Lambda integration on 2.3.0. We've updated the bilingual deployment troubleshooting guide with that boundary: https://www.xbstack.com/en/ai/mcp-streamable-http-deployment/?utm_source=github&utm_medium=referral&utm_campaign=mcp_subscriptions_optout_v230&utm_content=issue_followup