Summary
server/discover advertises the deprecated logging capability unconditionally, while the logging/setLevel handler rejects 2026-07-28+ requests with MethodNotFound. Since server/discover only exists on 2026-07-28+, the server advertises a capability whose only method it refuses to serve on the very era doing the asking.
There is also no way for a server author to opt out.
Detail
McpServerImpl.ConfigureLogging is called unconditionally from the constructor and does:
ServerCapabilities.Logging = new();
with no guard — not on Handlers.SetLoggingLevelHandler being non-null, not on protocol version, not on any option.
The same method's handler then rejects the modern era:
if (IsJuly2026OrLaterProtocolRequest(jsonRpcRequest))
{
throw new McpProtocolException(
$"The method '{RequestMethods.LoggingSetLevel}' is not available on protocol version '...'. Use per-request _meta/{MetaKeys.LogLevel} instead.",
McpErrorCode.MethodNotFound);
}
So a 2026-07-28 client reading capabilities.logging from server/discover and calling logging/setLevel gets MethodNotFound.
Why a server author cannot work around it
McpServerOptions.Capabilities is not the source of the advertised set. McpServerImpl builds its own ServerCapabilities = new() and reads only two members from options (Experimental, Extensions); everything else is derived from registrations. Measured against 2.0.0 on a stateless HTTP server:
| Attempt |
Result |
caps.Logging = null in PostConfigure<McpServerOptions> |
MCP9005 compile error — the analyzer forbids touching the member even to clear it |
Same, with a scoped #pragma warning disable MCP9005 |
No effect; options.Capabilities is null at PostConfigure time |
options.Capabilities = new ServerCapabilities() |
No wire change — {"logging":{},"tools":{}} |
options.Capabilities = new ServerCapabilities { Tools = new ToolsCapability() } |
No wire change |
Legacy initialize advertises logging identically, and identically on 1.4.1 — so this is long-standing rather than a 2.0.0 regression.
The only remaining lever is overriding server/discover via the [Experimental] McpServerOptions.RequestHandlers, which requires re-deriving SupportedVersions from the private _perRequestMetadataProtocolVersions and taking ownership of a MUST endpoint.
Impact
For a server that deliberately does not adopt deprecated features, the advertised capability set is not something it can make true. A client may reasonably call a method the advertisement promised.
Possible resolutions
- Guard
ServerCapabilities.Logging on the negotiated/least-supported protocol version, so 2026-07-28-only servers do not advertise it.
- Set it only when
Handlers.SetLoggingLevelHandler is non-null — the level is still stored either way, and it makes the advertisement reflect a real handler.
- Honour an explicit
options.Capabilities.Logging = null as a suppression signal (needs a non-[Obsolete] opt-out so it does not trip MCP9005).
Happy to send a PR for whichever shape you prefer.
Environment
ModelContextProtocol 2.0.0, net10.0, stateless Streamable HTTP, MCP-Protocol-Version: 2026-07-28.
Summary
server/discoveradvertises the deprecatedloggingcapability unconditionally, while thelogging/setLevelhandler rejects2026-07-28+ requests withMethodNotFound. Sinceserver/discoveronly exists on2026-07-28+, the server advertises a capability whose only method it refuses to serve on the very era doing the asking.There is also no way for a server author to opt out.
Detail
McpServerImpl.ConfigureLoggingis called unconditionally from the constructor and does:with no guard — not on
Handlers.SetLoggingLevelHandlerbeing non-null, not on protocol version, not on any option.The same method's handler then rejects the modern era:
So a
2026-07-28client readingcapabilities.loggingfromserver/discoverand callinglogging/setLevelgetsMethodNotFound.Why a server author cannot work around it
McpServerOptions.Capabilitiesis not the source of the advertised set.McpServerImplbuilds its ownServerCapabilities = new()and reads only two members from options (Experimental,Extensions); everything else is derived from registrations. Measured against 2.0.0 on a stateless HTTP server:caps.Logging = nullinPostConfigure<McpServerOptions>MCP9005compile error — the analyzer forbids touching the member even to clear it#pragma warning disable MCP9005options.CapabilitiesisnullatPostConfiguretimeoptions.Capabilities = new ServerCapabilities(){"logging":{},"tools":{}}options.Capabilities = new ServerCapabilities { Tools = new ToolsCapability() }Legacy
initializeadvertisesloggingidentically, and identically on 1.4.1 — so this is long-standing rather than a 2.0.0 regression.The only remaining lever is overriding
server/discovervia the[Experimental]McpServerOptions.RequestHandlers, which requires re-derivingSupportedVersionsfrom the private_perRequestMetadataProtocolVersionsand taking ownership of a MUST endpoint.Impact
For a server that deliberately does not adopt deprecated features, the advertised capability set is not something it can make true. A client may reasonably call a method the advertisement promised.
Possible resolutions
ServerCapabilities.Loggingon the negotiated/least-supported protocol version, so2026-07-28-only servers do not advertise it.Handlers.SetLoggingLevelHandleris non-null — the level is still stored either way, and it makes the advertisement reflect a real handler.options.Capabilities.Logging = nullas a suppression signal (needs a non-[Obsolete]opt-out so it does not tripMCP9005).Happy to send a PR for whichever shape you prefer.
Environment
ModelContextProtocol2.0.0,net10.0, stateless Streamable HTTP,MCP-Protocol-Version: 2026-07-28.