{"record":{"id":"05895eb2193a714f","repo":"PrefectHQ/fastmcp","slug":"logging-setlevel-is-not-available-on-mcp-2026-07-2","errorCode":null,"errorMessage":"logging/setLevel is not available on MCP 2026-07-28 connections; the method requires per-session server state that the modern protocol does not have. Filter incoming log messages by level in your log_handler instead.","messagePattern":"logging/setLevel is not available on MCP 2026-07-28 connections; the method requires per-session server state that the modern protocol does not have\\. Filter incoming log messages by level in your log_handler instead\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"warning","filePath":"fastmcp_slim/fastmcp/client/client.py","lineNumber":1414,"sourceCode":"        \"\"\"Send a progress notification.\"\"\"\n        # Deprecated upstream in SDK v2 but deliberately kept per compat directive;\n        # removed with the multi-round-trip follow-up.\n        await self.session.send_progress_notification(  # ty: ignore[deprecated]\n            progress_token, progress, total, message\n        )\n\n    async def set_logging_level(self, level: mcp_types.LoggingLevel) -> None:\n        \"\"\"Send a logging/setLevel request.\n\n        Handshake-era servers only. `logging/setLevel` asks the server to\n        remember a level for the rest of the session, and the 2026-07-28\n        protocol has no session to remember it in — the method is absent from\n        that era's registry. Log *notifications* are unaffected: they ride the\n        request's own stream, so a server's `ctx.info()` still reaches you.\n        Filter by level on the receiving side instead, in your `log_handler`.\n        \"\"\"\n        if self.protocol_version in MODERN_PROTOCOL_VERSIONS:\n            raise RuntimeError(\n                \"logging/setLevel is not available on MCP 2026-07-28 \"\n                \"connections; the method requires per-session server state that \"\n                \"the modern protocol does not have. Filter incoming log \"\n                \"messages by level in your log_handler instead.\"\n            )\n        # Deprecated upstream in SDK v2 but deliberately kept per compat directive;\n        # removed with the multi-round-trip follow-up.\n        await self._await_with_session_monitoring(\n            self.session.set_logging_level(level)  # ty: ignore[deprecated]\n        )\n\n    async def send_roots_list_changed(self) -> None:\n        \"\"\"Send a roots/list_changed notification.\"\"\"\n        # Deprecated upstream in SDK v2 but deliberately kept per compat directive;\n        # removed with the multi-round-trip follow-up.\n        await self.session.send_roots_list_changed()  # ty: ignore[deprecated]\n\n    # --- Completion ---","sourceCodeStart":1396,"sourceCodeEnd":1432,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/client/client.py#L1396-L1432","documentation":"`logging/setLevel` exists in legacy MCP but is absent in the modern (MCP 2026-07-28) era, which has no per-session server state. FastMCP raises this RuntimeError from `client.set_logging_level()` when the connection's protocol version is modern and recommends filtering log messages client-side in your `log_handler`. Log notifications themselves still arrive.","triggerScenarios":"Calling `client.set_logging_level(...)` (or a session-level variant) on a client connected in modern mode / with a modern protocol version.","commonSituations":"Migrating client code from legacy to modern-era servers; shared logging helpers calling set_logging_level unconditionally; wanting dynamic verbosity control after a server protocol upgrade.","solutions":["Remove the set_logging_level call and filter by level inside your log_handler","Connect with mode='legacy' if you control the client and need server-side level state","Wrap in try/except RuntimeError and fall back to client-side filtering"],"exampleFix":"// before\nawait client.set_logging_level(\"debug\")\n\n// after\nasync def log_handler(msg) -> None:\n    if msg.level in (\"debug\", \"info\"):\n        print(msg.data)\n# pass log_handler=log_handler when constructing the Client","handlingStrategy":"fallback","validationCode":"def can_set_logging_level(client) -> bool:\n    return client.protocol_version not in MODERN_PROTOCOL_VERSIONS","typeGuard":null,"tryCatchPattern":"try:\n    await client.set_logging_level(\"debug\")\nexcept RuntimeError as e:\n    if \"logging/setLevel is not available\" in str(e):\n        pass  # rely on log_handler level filtering instead\n    else:\n        raise","preventionTips":["Filter log messages by level in log_handler rather than server-side","Check protocol_version before calling protocol-specific methods","Document protocol-era assumptions in shared client helpers"],"tags":["python","logging","protocol","mcp"],"backgroundTag":"method-unavailable-in-modern-protocol","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}