update Subscribe/Unsubscribe handlers in ServerOpts (#231)

## Background

Since the Server has a map of ServersSessions->subscriptions, and takes
care of routing ResourceUpdate messages to ServerSessions, I think the
decision was made to leave *ServerSession out of the
SubscribeHandler/UnsubscribeHandler signatures. However, resource
subscriptions are not specific to the a particular client and this
information is kept private in the Session today.

## Proposal

Align the Subscribe/Unsubscribe handlers with the other two handlers
that are client specific (ProgressNotificationHandler, and
RootListChangeHanlder), and include *ServerSession in the signature.

It comes in practice because because a gateway server that needs to
forward subscriptions still needs this information. The routing logic in
Server is convenient and means that most servers won't need to know
which ServerSessions are subscribed. But I think there are still use
cases where the user will need to know.
This commit is contained in:
Jim Clark 2025-08-04 11:46:49 -07:00 • committed by GitHub
commit cb17593a5d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 12 additions and 12 deletions

View file

@ -776,9 +776,9 @@ If a server author wants to support resource subscriptions, they must provide ha
type ServerOptions struct {
...
// Function called when a client session subscribes to a resource.
SubscribeHandler func(context.Context, *SubscribeParams) error
SubscribeHandler func(context.Context, ss *ServerSession, *SubscribeParams) error
// Function called when a client session unsubscribes from a resource.
UnsubscribeHandler func(context.Context, *UnsubscribeParams) error
UnsubscribeHandler func(context.Context, ss *ServerSession, *UnsubscribeParams) error
}
```

View file

@ -78,11 +78,11 @@ func TestEndToEnd(t *testing.T) {
ProgressNotificationHandler: func(context.Context, *ServerSession, *ProgressNotificationParams) {
notificationChans["progress_server"] <- 0
},
SubscribeHandler: func(context.Context, *SubscribeParams) error {
SubscribeHandler: func(context.Context, *ServerSession, *SubscribeParams) error {
notificationChans["subscribe"] <- 0
return nil
},
UnsubscribeHandler: func(context.Context, *UnsubscribeParams) error {
UnsubscribeHandler: func(context.Context, *ServerSession, *UnsubscribeParams) error {
notificationChans["unsubscribe"] <- 0
return nil
},

View file

@ -66,9 +66,9 @@ type ServerOptions struct {
// the session is automatically closed.
KeepAlive time.Duration
// Function called when a client session subscribes to a resource.
SubscribeHandler func(context.Context, *SubscribeParams) error
SubscribeHandler func(context.Context, *ServerSession, *SubscribeParams) error
// Function called when a client session unsubscribes from a resource.
UnsubscribeHandler func(context.Context, *UnsubscribeParams) error
UnsubscribeHandler func(context.Context, *ServerSession, *UnsubscribeParams) error
// If true, advertises the prompts capability during initialization,
// even if no prompts have been registered.
HasPrompts bool
@ -469,7 +469,7 @@ func (s *Server) subscribe(ctx context.Context, ss *ServerSession, params *Subsc
if s.opts.SubscribeHandler == nil {
return nil, fmt.Errorf("%w: server does not support resource subscriptions", jsonrpc2.ErrMethodNotFound)
}
if err := s.opts.SubscribeHandler(ctx, params); err != nil {
if err := s.opts.SubscribeHandler(ctx, ss, params); err != nil {
return nil, err
}
@ -488,7 +488,7 @@ func (s *Server) unsubscribe(ctx context.Context, ss *ServerSession, params *Uns
return nil, jsonrpc2.ErrMethodNotFound
}
if err := s.opts.UnsubscribeHandler(ctx, params); err != nil {
if err := s.opts.UnsubscribeHandler(ctx, ss, params); err != nil {
return nil, err
}

View file

@ -281,10 +281,10 @@ func TestServerCapabilities(t *testing.T) {
s.AddResourceTemplate(&ResourceTemplate{URITemplate: "file:///rt"}, nil)
},
serverOpts: ServerOptions{
SubscribeHandler: func(ctx context.Context, sp *SubscribeParams) error {
SubscribeHandler: func(ctx context.Context, _ *ServerSession, sp *SubscribeParams) error {
return nil
},
UnsubscribeHandler: func(ctx context.Context, up *UnsubscribeParams) error {
UnsubscribeHandler: func(ctx context.Context, _ *ServerSession, up *UnsubscribeParams) error {
return nil
},
},
@ -325,10 +325,10 @@ func TestServerCapabilities(t *testing.T) {
s.AddTool(tool, nil)
},
serverOpts: ServerOptions{
SubscribeHandler: func(ctx context.Context, sp *SubscribeParams) error {
SubscribeHandler: func(ctx context.Context, _ *ServerSession, sp *SubscribeParams) error {
return nil
},
UnsubscribeHandler: func(ctx context.Context, up *UnsubscribeParams) error {
UnsubscribeHandler: func(ctx context.Context, _ *ServerSession, up *UnsubscribeParams) error {
return nil
},
CompletionHandler: func(ctx context.Context, ss *ServerSession, params *CompleteParams) (*CompleteResult, error) {