diff --git a/mcp/client.go b/mcp/client.go index 4c0a458..522f873 100644 --- a/mcp/client.go +++ b/mcp/client.go @@ -1443,7 +1443,7 @@ func (cs *ClientSession) cancelAllResourceSubscriptions() { // usual handlers registered in [ClientOptions]. func (cs *ClientSession) subscriptionsListen(ctx context.Context, params *SubscriptionsListenParams) error { params = injectRequestMeta(cs, params) - _, err := handleSend[*emptyResult](ctx, methodSubscriptionsListen, newClientRequest(cs, orZero[Params](params))) + _, err := handleSend[*SubscriptionsListenResult](ctx, methodSubscriptionsListen, newClientRequest(cs, orZero[Params](params))) return err } diff --git a/mcp/protocol.go b/mcp/protocol.go index dc760a5..4096786 100644 --- a/mcp/protocol.go +++ b/mcp/protocol.go @@ -2109,6 +2109,18 @@ type SubscriptionsAcknowledgedParams struct { func (x *SubscriptionsAcknowledgedParams) isParams() {} func (x *SubscriptionsAcknowledgedParams) isNil() bool { return x == nil } +// SubscriptionsListenResult is the response to a "subscriptions/listen" +// request, signalling that the subscription has ended gracefully (for example, +// during server shutdown). Because the listen stream is long-lived, this +// result is sent only when the server tears the subscription down; an abrupt +// transport close carries no response. +type SubscriptionsListenResult struct { + completeResultWithType + Meta `json:"_meta"` +} + +func (*SubscriptionsListenResult) isResult() {} + // TODO(jba): add CompleteRequest and related types. // A request from the server to elicit additional information from the user via the client. diff --git a/mcp/server.go b/mcp/server.go index 3e6d95a..34cbf93 100644 --- a/mcp/server.go +++ b/mcp/server.go @@ -1180,7 +1180,7 @@ func (s *Server) unsubscribe(ctx context.Context, req *UnsubscribeRequest) (*emp return &emptyResult{}, nil } -func (s *Server) subscriptionsListen(ctx context.Context, req *SubscriptionsListenRequest) (*emptyResult, error) { +func (s *Server) subscriptionsListen(ctx context.Context, req *SubscriptionsListenRequest) (*SubscriptionsListenResult, error) { requestID, ok := ctx.Value(idContextKey{}).(jsonrpc.ID) if !ok || !requestID.IsValid() { return nil, fmt.Errorf("%w: subscriptions/listen requires a request ID", jsonrpc2.ErrInvalidRequest) @@ -1242,7 +1242,9 @@ func (s *Server) subscriptionsListen(ctx context.Context, req *SubscriptionsList if len(allowed.ResourceSubscriptions) > 0 || allowed.ToolsListChanged || allowed.PromptsListChanged || allowed.ResourcesListChanged { <-ctx.Done() } - return &emptyResult{}, nil + return &SubscriptionsListenResult{ + Meta: Meta{MetaKeySubscriptionID: requestID.Raw()}, + }, nil } func (s *Server) allowedSubscriptions(want *NotificationSubscriptions) NotificationSubscriptions {