Skip to content

SetLoggingLevel omits SEP-2575 _meta while sending the 2026-07-28 header #1116

Description

@JAORMX

Describe the bug

On a session negotiated at 2026-07-28, ClientSession.SetLoggingLevel sends logging/setLevel with the MCP-Protocol-Version: 2026-07-28 header but without the SEP-2575 per-request _meta fields. It is the only Modern-eligible client call that does not inject them.

A server that validates header/_meta consistency — as the Streamable HTTP "Server Validation" rules require — sees a Modern protocol header alongside an empty _meta protocol version and must reject the request. So a Modern client cannot call logging/setLevel against a conformant server at all.

Cause

Every other Modern-eligible client method guards on cs.usesNewProtocol() and calls injectRequestMeta, e.g. ListResources (mcp/client.go:1309):

func (cs *ClientSession) ListResources(ctx context.Context, params *ListResourcesParams) (*ListResourcesResult, error) {
	if cs.usesNewProtocol() {
		if result, ok := cachedListResult(&cs.resourcesCache, params); ok {
			return result, nil
		}
		params = injectRequestMeta(cs, params)
	}
	result, err := handleSend[*ListResourcesResult](ctx, methodListResources, newClientRequest(cs, orZero[Params](params)))
	...

SetLoggingLevel (mcp/client.go:1303-1306) has no such guard:

func (cs *ClientSession) SetLoggingLevel(ctx context.Context, params *SetLoggingLevelParams) error {
	_, err := handleSend[*emptyResult](ctx, methodSetLevel, newClientRequest(cs, orZero[Params](params)))
	return err
}

The injectRequestMeta call sites in mcp/client.go are lines 1236, 1251, 1262, 1290, 1314, 1332, 1354, 1368, 1452, and 1676. SetLoggingLevel is absent from that list.

The header itself is still set by the streamable transport, so the two disagree on the wire: header says 2026-07-28, _meta[io.modelcontextprotocol/protocolVersion] (mcp/protocol.go:2363) is missing.

To Reproduce

  1. Serve a Streamable HTTP server that validates header/_meta protocol-version consistency.
  2. Connect a Client so it negotiates 2026-07-28 via server/discover.
  3. Call cs.SetLoggingLevel(ctx, &SetLoggingLevelParams{Level: "debug"}).

Observed:

calling "logging/setLevel": sending "logging/setLevel":
MCP-Protocol-Version header "2026-07-28" does not match _meta protocol version "": Bad Request

Expected behavior

Either the request carries the SEP-2575 _meta triple like every other Modern-eligible call, or the client declines to send it with a clear error — but not a request that cannot validate.

Suggested fix

Smallest change, matching the sibling idiom:

func (cs *ClientSession) SetLoggingLevel(ctx context.Context, params *SetLoggingLevelParams) error {
	if cs.usesNewProtocol() {
		params = injectRequestMeta(cs, params)
	}
	_, err := handleSend[*emptyResult](ctx, methodSetLevel, newClientRequest(cs, orZero[Params](params)))
	return err
}

Refusing client-side is also a legitimate resolution rather than a cop-out, and worth considering on its merits: SetLoggingLevel is already marked Deprecated for exactly this revision — its doc comment cites SEP-2577's deprecation of logging and points at consuming stderr or OpenTelemetry instead. If the intent is that logging/setLevel is Legacy-only, then returning a descriptive error on a Modern session is more honest than emitting a request the peer must reject, and it gives callers something actionable. What is hard to justify is the current middle state, where the call is advertised as functional during the deprecation window but cannot succeed against a conformant Modern server.

Note that SEP-2577 says the feature "remains functional during the deprecation window (at least twelve months)", which reads as favouring the injection fix — the deprecation is about discouraging new use, not about breaking the call.

Additional context

Found while implementing the MCP 2026-07-28 client-facing surface in a stateless proxy built on this SDK (a virtual MCP server that aggregates several backends and serves both revisions). Our revision classifier rejects the header/_meta mismatch with -32020, which is what surfaced it; the same request would be rejected by any server enforcing that consistency rule.

Related to #1112 and #1113, both from adopting v1.7.0-pre.3 in the same shim. Happy to send a PR for either resolution, and to supply a standalone repro (small server enforcing the consistency check, plus a client that negotiates Modern) if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions