mcp: list_changed can only be broadcast; a server cannot notify one session (2026-07-28 subscriptions/listen)
Maintainer thường phản hồi trong vòng 1 ngày
@huynle-dev đang làm issue này rồi.
Từ ngày 2/11/2026.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 1-2 ngày
- Mức phù hợp với người mới
- 32/100
Hướng nghiên cứu
Start with Server.changeAndNotify and notifySessions in mcp/server.go, then notifySubscribedSessions and injectMetaSubscriptionID, to see how list-changed reaches the subscription tables today. Read TestSubscriptionsListen_MultipleSessions to learn the fan-out test pattern. Done means a ServerSession method sends a list-changed notification only to that session's open listen stream, with the listen's ID in _meta, and does nothing when no listen is open. The open questions in the issue need a maintainer decision before implementation.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
#666 asked for a way to send notifications/tools/list_changed to a specific client and was closed as obsolete under 2026-07-28. The transport changed (subscriptions/listen replaced the standalone GET stream), but delivery did not: in v1.8.0 (and on main @ eafb671) a server still cannot send a list-changed notification to one session.
Today
- The only trigger is
Server.changeAndNotify(mcp/server.go:747, v1.8.0), run onAddTool/RemoveTool/AddPrompt/…. After debouncing it callsnotifySessions, which sends to every legacy session and every 2026-07-28 session intoolChangeSubscriptions/promptChangeSubscriptions/resourceChangeSubscriptions. - The per-session sender
notifySubscribedSessions(:810) and the listen-request ID stamped underMetaKeySubscriptionID(injectMetaSubscriptionID,:832) are unexported.ServerSessionexportsNotifyProgress,NotifyElicitationComplete,Log, … but no list-changed sender. TestSubscriptionsListen_MultipleSessionsasserts the fan-out.
Use case
We run a multi-tenant MCP gateway on go-sdk: one stateless *mcp.Server behind several replicas. Each principal sees a different tool/prompt/resource list (registry + policy engine + OAuth scopes). A receiving middleware serves tools/list and tools/call; the SDK tool table stays empty, because per-principal servers with ~1000 tools each are not viable (#1265 has the same constraint).
When a principal's visible set changes, only that principal's open subscriptions/listen streams should get list_changed. Clients cache catalogs, so without it they keep calling tools that are gone and never see new ones.
The SDK already accepts the listen, acks it, records req.Session → requestID, forces SSE and retires the subscription. The missing piece is the last step: send this notification to this session's listen.
Workarounds, each with a problem:
AddTool/RemoveToola dummy tool plus a sending middleware that drops the notification for other principals (the #666 hack): fans out N notifications to drop N−1, and the routing key has to be smuggled through_meta(no ctx propagation, #1265).- One
*mcp.Serverper principal fromgetServer, diffing its tool table: duplicates the catalog per principal just to reach the notifier. - Writing the frame ourselves is impossible: the listen request ID is unreachable (
idContextKeyand the subscription tables are unexported).
Proposal
// NotifyToolListChanged sends notifications/tools/list_changed to this session.
// For a 2026-07-28 session it is delivered on the session's active
// subscriptions/listen stream that opted into toolsListChanged, with that
// listen's request ID under _meta[MetaKeySubscriptionID]. If no such listen is
// open, it does nothing and returns nil. Legacy sessions receive it on the
// standalone stream, as changeAndNotify does today.
func (ss *ServerSession) NotifyToolListChanged(ctx context.Context, params *ToolListChangedParams) error
func (ss *ServerSession) NotifyPromptListChanged(ctx context.Context, params *PromptListChangedParams) error
func (ss *ServerSession) NotifyResourceListChanged(ctx context.Context, params *ResourceListChangedParams) error
Implementation: lock s.mu, look up s.toolChangeSubscriptions[ss], unlock, copy params, injectMetaSubscriptionID, then handleNotify with the caller's ctx. Server.notifySessions can become a loop over these (one delivery path; composes with #1265).
If three methods is too much surface (the concern raised in #666), a single func (ss *ServerSession) NotifyListChanged(ctx context.Context, kind string) error would also work.
Usage:
server.AddReceivingMiddleware(func(next mcp.MethodHandler) mcp.MethodHandler {
return func(ctx context.Context, method string, req mcp.Request) (mcp.Result, error) {
if method == "subscriptions/listen" {
ss := req.GetSession().(*mcp.ServerSession)
stop := watchPrincipal(ctx, principalFrom(ctx), func() {
_ = ss.NotifyToolListChanged(ctx, &mcp.ToolListChangedParams{})
})
defer stop()
}
return next(ctx, method, req) // SDK registers, acks, blocks until the stream ends
}
})
Open questions: (a) "no open listen" → nil (our preference) or a sentinel error? (b) keep the shouldSendListChangedNotification capability gate (we'd say yes).
Happy to send a PR with tests.
- Ngôn ngữ chính
- Go
- Star
- 5.3k
- Fork
- 607
- Merge trung bình
- 1 ngày 17 giờ
- Pull request đã merge (30 ngày)
- 80
Chuẩn bị môi trường
Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của modelcontextprotocol/go-sdk
-
Expose generic `SendNotification` on `ServerSession` for custom protocol extensionsCó thể làm lại được Pull request cho issue này đã bị đóng mà không được merge. Đang mởneeds investigation
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
modelcontextprotocol/go-sdk#745 · 13 bình luận · 1 reaction ·
Maintainer thường phản hồi trong vòng 1 ngày
-
mcp: a second subscriptions/listen for the same URI silences the firstCó thể đã có người làm @jmrplens đã nhận hôm nay. Đang mở
modelcontextprotocol/go-sdk#1489 · 3 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
mcp: ClientSession.CallTool omits Mcp-Param-* headers unless the tool is in the ListTools cache, so a call without a prior tools/list is rejectedCó thể đã có người làm @huynle-dev đã nhận hôm nay. Đang mở
Độ khó 4/5 1-2 ngày Mức phù hợp với người mới 35/100
modelcontextprotocol/go-sdk#1481 · 2 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
mcp: Client.Connect falls back to initialize after a 401 on server/discover, running the authorization flow twiceCó thể đã có người làm @kerthans đã nhận 12 ngày trước. Đang mở
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 35/100
modelcontextprotocol/go-sdk#1423 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
jsonrpc: decimal and exponent integer IDs still lose precision after #1401Có thể đã có người làm @skosovsky đã nhận 17 ngày trước. Đang mở
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 25/100
modelcontextprotocol/go-sdk#1404 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của modelcontextprotocol/go-sdk
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
router-for-me/CLIProxyAPI#7209 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
Azure/azure-storage-azcopy#3674 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
date_time, url_encode, html_escape, to_unix_time, contains_all and contains_any panic when called without argumentsCó thể đã có người làm @antcybersec đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
projectdiscovery/dsl#372 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
blocked-needs-validation
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
projectcapsule/capsule#2265 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Remove "Model catalog refresh unavailable" warning from OpenRouter agent runsCó thể đã có người làm @superplanehq-integration đã nhận hôm nay. Đang mởsuperplane
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
superplanehq/superplane#9164 ·
Maintainer thường phản hồi trong vòng 1 ngày