modelcontextprotocol / modelcontextprotocol/java-sdk

ServerCapabilities.logging is added unconditionally, overriding the caller's explicit capabilities

Open Beginner friendly
#1,086 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug P2 ready for work
Dominant language
Java
Stars
3.7k
Forks
1.1k
Avg merge
1d 15h
Merged PRs (30d)
9

Description

What happens

McpAsyncServer adds the logging capability to every server it builds, overriding whatever the
caller passed to .capabilities(...). There is no flag and no branch, so a server cannot decline to
advertise it.

Both constructors — the McpServerTransportProvider one and the McpStreamableServerTransportProvider
one — run:

this.serverCapabilities = features.serverCapabilities().mutate().logging().build();
Reproduction

Build a server that asks for tools only:

McpServer.sync(transport)
        .serverInfo("example", "1.0.0")
        .capabilities(ServerCapabilities.builder().tools(true).build())
        .tools(theTools)
        .build();

initialize answers:

"capabilities": { "logging": {}, "tools": { "listChanged": true } }
Evidence

Verified in the bytecode of mcp-core-2.0.0.jar rather than from source, in case the mutation was
conditional at runtime. It is not — javap -c io/modelcontextprotocol/server/McpAsyncServer.class
shows the same three calls at identical offsets in both constructors:

101: invokevirtual  // McpServerFeatures$Async.serverCapabilities:()...ServerCapabilities;
104: invokevirtual  // ServerCapabilities.mutate:()...ServerCapabilities$Builder;
107: invokevirtual  // ServerCapabilities$Builder.logging:()...ServerCapabilities$Builder;
113: putfield       // serverCapabilities
Why this is worth changing

MCP's in-protocol logging utility is deprecated as of protocol revision 2026-07-28
(SEP-2577); new
implementations SHOULD NOT adopt it. The current behaviour means every server built with this SDK
advertises the deprecated capability
, whether or not it sends notifications/message — which is
close to the opposite of what the deprecation is trying to achieve, and it makes the advertisement
useless to clients as a signal, since it is true of everyone.

It also leaves implementors with only two honest options, neither of them "follow the spec's advice":

  1. implement a deprecated feature they did not want, purely so the advertisement is not empty; or
  2. advertise a capability that does nothing, and hope no client acts on it.

Worth noting the gap is narrower than it first appears, which is why this is a small fix rather than
a design change: the SDK does implement logging/setLevel and filters on
McpServerSession.minLoggingLevel (default INFO) inside loggingNotification. So a server that
never calls loggingNotification is offering an always-empty stream rather than a broken method.
The problem is only that it cannot say so.

Suggested fix

Honour what the caller passed:

this.serverCapabilities = features.serverCapabilities();

If the unconditional .logging() is load-bearing for existing users, an opt-out on the builder would
also do — anything that lets a server say "I do not implement this". Happy to open a PR if you have a
preference between the two.

Related but not the same ask: #872 (constructors package-private, so behaviour cannot be customised
by subclassing).

Version

io.modelcontextprotocol.sdk:mcp:2.0.0, Java 21.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in McpAsyncServer's constructors for McpServerTransportProvider and McpStreamableServerTransportProvider, where serverCapabilities is built from features. Reproduce the tools-only server from the issue and inspect its initialize response; done means the response reflects the caller's capabilities without unrequested logging.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
api, backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.