modelcontextprotocol / modelcontextprotocol/java-sdk
ServerCapabilities.logging is added unconditionally, overriding the caller's explicit capabilities
还没有人认领这个 Issue。
- 主要语言
- Java
- 星标
- 3.7k
- 派生
- 1.1k
- 平均合并
- 1 天 15 小时
- 30 天内合并 PR
- 9
描述
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":
- implement a deprecated feature they did not want, purely so the advertisement is not empty; or
- 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.
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
从 McpServerTransportProvider 和 McpStreamableServerTransportProvider 的 McpAsyncServer 构造函数入手,其中 serverCapabilities 根据 features 构建。复现 issue 中的 tools-only 服务器并检查其 initialize 响应;完成的标准是,该响应反映调用方的 capabilities,且没有未请求的 logging。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- java
- 领域
- api, backend
- Issue 类型
- 缺陷
- 难度
- 2/5
- 预计耗时
- 1-3 小时
- 活跃度
- 冷清
- 描述清晰度
- 描述清楚
- 新手友好度
- 68/100