mcp: list_changed can only be broadcast; a server cannot notify one session (2026-07-28 subscriptions/listen)
Los mantenedores suelen responder en 1 día
@huynle-dev ya está trabajando en esto.
Desde el 2/11/2026.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 32/100
Línea de trabajo
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.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
#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.
- Lenguaje dominante
- Go
- Estrellas
- 5.3k
- Forks
- 607
- Merge medio
- 1 d 17 h
- PR fusionados (30 d)
- 80
Preparar el entorno
Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de modelcontextprotocol/go-sdk
-
Expose generic `SendNotification` on `ServerSession` for custom protocol extensionsQuizá libre de nuevo Un pull request para esta issue se cerró sin fusionarse. Abiertoneeds investigation
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
modelcontextprotocol/go-sdk#745 · 13 comentarios · 1 reacción ·
Los mantenedores suelen responder en 1 día
-
mcp: a second subscriptions/listen for the same URI silences the firstPosiblemente ocupada @jmrplens la tomó hace 1 día. Abierto
modelcontextprotocol/go-sdk#1489 · 3 comentarios · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
mcp: ClientSession.CallTool omits Mcp-Param-* headers unless the tool is in the ListTools cache, so a call without a prior tools/list is rejectedPosiblemente ocupada @huynle-dev la tomó hace 1 día. Abierto
Dificultad 4/5 1-2 días Aptitud para principiantes 35/100
modelcontextprotocol/go-sdk#1481 · 2 comentarios · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
mcp: Client.Connect falls back to initialize after a 401 on server/discover, running the authorization flow twicePosiblemente ocupada @kerthans la tomó hace 13 días. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 35/100
modelcontextprotocol/go-sdk#1423 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
jsonrpc: decimal and exponent integer IDs still lose precision after #1401Posiblemente ocupada @skosovsky la tomó hace 18 días. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 25/100
modelcontextprotocol/go-sdk#1404 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
Todos los issues de modelcontextprotocol/go-sdk
Issues similares
-
agent-butler-finding chore
Dificultad 1/5 Menos de una hora Aptitud para principiantes 78/100
jordansmall/spindrift#6318 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Eyevinn/moqlivemock#166 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
JakeChampion/lang#14091 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
hyperledger/fabric-x-orderer#1409 ·
Los mantenedores suelen responder en 3 días
-
get actions doesn't show the approval flag, and describe action shows it unformattedPosiblemente ocupada @kenokech la tomó hoy. Abiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día