{"record":{"id":"38addf73e371e067","repo":"PrefectHQ/fastmcp","slug":"stateful-proxy-requires-a-per-connection-server-se","errorCode":null,"errorMessage":"Stateful proxy requires a per-connection server session; no connection is available on the current context.","messagePattern":"Stateful proxy requires a per-connection server session; no connection is available on the current context\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/providers/proxy.py","lineNumber":1848,"sourceCode":"    async def clear(self):\n        \"\"\"Clear all cached clients and force disconnect them.\"\"\"\n        while self._caches:\n            _, cache = self._caches.popitem()\n            await cache._disconnect(force=True)\n\n    def new_stateful(self) -> Client[ClientTransportT]:\n        \"\"\"Create a new stateful proxy client instance with the same configuration.\n\n        Use this method as the client factory for stateful proxy server.\n        \"\"\"\n        session = get_context().session\n        # SDK v2: the ServerSession is per-request; the Connection is the stable\n        # per-connection object that owns the exit stack. Key the cache and the\n        # cleanup callback off it so one proxy client is reused for the whole\n        # connection instead of one per request.\n        connection = getattr(session, \"_connection\", None)\n        if connection is None:\n            raise RuntimeError(\n                \"Stateful proxy requires a per-connection server session; \"\n                \"no connection is available on the current context.\"\n            )\n        proxy_client = self._caches.get(connection, None)\n\n        if proxy_client is None:\n            proxy_client = self.new()\n            logger.debug(f\"{proxy_client} created for {connection}\")\n            self._caches[connection] = proxy_client\n\n            async def _on_connection_exit():\n                self._caches.pop(connection, None)\n                logger.debug(f\"{proxy_client} will be disconnect\")\n                # This callback runs while the connection's exit stack is\n                # unwinding, which usually happens because the owning task is\n                # being cancelled. Shield the disconnect so the forced cleanup\n                # actually runs to completion instead of aborting at the first\n                # cancellation checkpoint (e.g. acquiring the session lock).","sourceCodeStart":1830,"sourceCodeEnd":1866,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/providers/proxy.py#L1830-L1866","documentation":"new_stateful() creates a per-connection stateful proxy client keyed on the server Connection object, which it extracts from the current ServerSession via session._connection. If no Connection is attached to the session (e.g. the proxy client is being created outside a live MCP connection context), it raises RuntimeError because there is no stable per-connection object to key the cache and cleanup on.","triggerScenarios":"Calling new_stateful() (or a stateful proxy client factory) from a context where the ServerSession has no _connection attribute — e.g. in tests without a real transport, in in-memory/off-protocol invocation, or during startup before any client connects.","commonSituations":"Unit-testing stateful proxies without spinning up a real server session; calling the factory from background tasks or non-MCP entry points; SDK version changes where the session/connection wiring differs.","solutions":["Only create stateful proxy clients inside a live request/connection context where a ServerSession with its Connection exists","In tests, construct a real ClientSession/Connection pair (or a stub object exposing _connection) instead of a bare mock session","Fall back to a non-stateful proxy client when no connection context is available, if statefulness isn't required"],"exampleFix":"// before: fails outside a connection\nclient = provider.new_stateful(session)  # session is a bare mock\n// after: guard on connection presence\nconnection = getattr(session, '_connection', None)\nclient = provider.new_stateful(session) if connection else provider.new_client()","handlingStrategy":"try-catch","validationCode":"connection = getattr(session, '_connection', None)\nif connection is None:\n    raise RuntimeError('new_stateful() requires a live MCP connection context')","typeGuard":"def has_connection(session) -> bool:\n    return getattr(session, '_connection', None) is not None","tryCatchPattern":"try:\n    client = provider.new_stateful(session)\nexcept RuntimeError as e:\n    if 'no connection is available' in str(e):\n        client = provider.new_client()  # stateless fallback","preventionTips":["Only build stateful proxies inside request handlers with a real ServerSession","In tests, use a real client/server session pair or a stub exposing _connection","Keep the MCP SDK version in sync with what fastmcp expects for session wiring"],"tags":["proxy","stateful","session","lifecycle"],"backgroundTag":"missing-connection-context","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}