Default capabilities advertise listChanged, prompts, resources and logging on a tools-only stateless server
維護者通常 1 天內回覆
評估
- 難度
- 3/5
- 預估耗時
- 1-2 天
- 新手友好度
- 56/100
- Issue 類型
- 缺陷
- 描述清晰度
- 描述清楚
- 活躍度
- 活躍
- 技術堆疊
- ruby
研究方向
Start by reading MCP::Server#default_capabilities and #discover_capabilities, then trace how the initialize response obtains its capabilities and how stateless transport handles notifications and logging. Compare the initialize and server/discover results for the supplied reproduction. Done means both lifecycles advertise capabilities the server and transport can provide, with tests covering the tools-only stateless case.
由索引模型根據 Issue 內容生成。
描述
Describe the bug
When capabilities: is not passed, MCP::Server advertises Server#default_capabilities regardless of what the server has or what its transport can deliver:
{ tools: { listChanged: true }, prompts: { listChanged: true }, resources: { listChanged: true }, logging: {} }
On a server with one tool, no prompts, no resources, behind StreamableHTTPTransport.new(server, stateless: true, serve_subscriptions_listen: false), the initialize result still promises:
listChangedon tools, prompts and resources, although a stateless transport cannot sendnotifications/*/list_changed(the transports page says notifications are not supported in stateless mode);promptsandresources, which the server does not have;logging, although request-scoped log messages are skipped in stateless mode.
server/discover already strips listChanged/subscribe when the transport does not serve subscriptions/listen (Server#discover_capabilities, from #533), but initialize does not, so the same server describes itself differently on the two lifecycles. prompts, resources and logging are advertised on both.
To Reproduce
require "bundler/inline"
gemfile do
source "https://rubygems.org"
gem "mcp", "1.8.0"
gem "rack"
end
require "json"
class EchoTool < MCP::Tool
description "Echoes a message"
input_schema(properties: { message: { type: "string" } }, required: ["message"])
def self.call(message:, server_context:)
MCP::Tool::Response.new([{ type: "text", text: message }])
end
end
def post(body, headers = {})
env = Rack::MockRequest.env_for(
"http://localhost/mcp",
method: "POST",
input: JSON.generate(body),
"CONTENT_TYPE" => "application/json",
"HTTP_ACCEPT" => "application/json, text/event-stream",
)
headers.each { |k, v| env[k] = v }
Rack::Request.new(env)
end
server = MCP::Server.new(name: "echo", version: "1.0.0", tools: [EchoTool])
transport = MCP::Server::Transports::StreamableHTTPTransport.new(
server, stateless: true, serve_subscriptions_listen: false
)
_, _, body = transport.handle_request(post(
jsonrpc: "2.0", id: 1, method: "initialize",
params: { protocolVersion: "2025-11-25", capabilities: {}, clientInfo: { name: "c", version: "1" } },
))
puts "initialize: #{JSON.parse(body.first).dig("result", "capabilities").to_json}"
_, _, body = transport.handle_request(post(
{ jsonrpc: "2.0", id: 2, method: "server/discover",
params: { _meta: { "io.modelcontextprotocol/protocolVersion" => "2026-07-28",
"io.modelcontextprotocol/clientInfo" => { name: "c", version: "1" },
"io.modelcontextprotocol/clientCapabilities" => {} } } },
{ "HTTP_MCP_PROTOCOL_VERSION" => "2026-07-28", "HTTP_MCP_METHOD" => "server/discover" },
))
puts "server/discover: #{JSON.parse(body.first).dig("result", "capabilities").to_json}"
Output, the same on 1.8.0 and on main at 56af0c7:
initialize: {"tools":{"listChanged":true},"prompts":{"listChanged":true},"resources":{"listChanged":true},"logging":{}}
server/discover: {"tools":{},"prompts":{},"resources":{},"logging":{}}
Expected behavior
Capabilities that describe what this server can do. The spec (lifecycle, Capability Negotiation) says capabilities "establish which optional protocol features will be available during the session", and tools defines listChanged as "whether the server will emit notifications when the list of available tools changes" (prompts and resources say the same). For the repro, that is {"tools":{}} on both lifecycles.
A client that takes the default at its word waits for a list_changed that cannot arrive and asks for prompts and resources that do not exist. Passing capabilities: { tools: {} } works around it, but the default is what most servers ship with.
Two possible shapes, either would fix it:
- Derive the default from the server:
tools/prompts/resourcesonly when some are registered (or a list handler is set),loggingonly when it can be delivered. - At minimum, apply the same stripping
discover_capabilitiesdoes to theinitializeresult, and droplistChangedinstateless: true.
I'm happy to send a PR for whichever shape you prefer.
Additional context
mcp1.8.0 (latest release) andmain56af0c7- Ruby 3.4.10, rack 3.2.7, macOS
- Related: #531 / #533 (the
serve_subscriptions_listen: falseflag and the discover-side stripping), #602 (methods of undeclared capabilities).
- 主要語言
- Ruby
- 星號
- 924
- 分支
- 136
- 平均合併
- 1 天 9 小時
- 30 天內合併 PR
- 38
環境準備
- 沒有 Dockerfile 或 Docker Compose 檔案
- 沒有 Pull Request 範本
- 閱讀貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
modelcontextprotocol/ruby-sdk 的其他 Issue
-
Empty members in the Accept header cause a 500可能已有人在做 @koic 於 3 天前認領。 未關閉bug
難度 2/5 1-3 小時 新手友好度 82/100
modelcontextprotocol/ruby-sdk#609 ·
維護者通常 1 天內回覆
-
Error responses carry "id": null, which the MCP schema does not allow可能已有人在做 @koic 於 3 天前認領。 未關閉bug
難度 3/5 半天 新手友好度 45/100
modelcontextprotocol/ruby-sdk#606 ·
維護者通常 1 天內回覆
-
String request ids are limited to letters, digits, "_" and "-"可能已有人在做 @koic 於 4 天前認領。 未關閉bug
modelcontextprotocol/ruby-sdk#603 · 已指派 1 人 ·
維護者通常 1 天內回覆
-
Support modelcontextprotocol/experimental-ext-triggers-events可能已有人在做 @koic 於 7 天前認領。 未關閉enhancement
modelcontextprotocol/ruby-sdk#594 · 已指派 1 人 ·
維護者通常 1 天內回覆
-
SEP-2640: Skills Extension可能已有人在做 @koic 於 26 天前認領。 未關閉enhancement
modelcontextprotocol/ruby-sdk#568 · 1 則留言 · 2 個 reaction · 已指派 1 人 ·
維護者通常 1 天內回覆
查看 modelcontextprotocol/ruby-sdk 的全部 Issue
相似的 Issue
-
Windows: packaged_agent_ruby? never matches 'Puppet Labs\Puppet', so the openvox gem is not found未關閉
難度 2/5 1-3 小時 新手友好度 85/100
-
難度 2/5 1-3 小時 新手友好度 65/100
-
難度 2/5 1-3 小時 新手友好度 85/100
temporalio/sdk-ruby#625 ·
維護者通常 1 天內回覆
-
難度 1/5 1-3 小時 新手友好度 94/100
zerocracy/judges-action#2803 ·
維護者通常 5 天內回覆
-
empty tone.rb未關閉
難度 1/5 1 小時以內 新手友好度 78/100