@@ -12,6 +12,7 @@ import (
1212 "encoding/json"
1313 "fmt"
1414 "iter"
15+ "log/slog"
1516 "maps"
1617 "net/url"
1718 "path/filepath"
@@ -32,8 +33,9 @@ const DefaultPageSize = 1000
3233// sessions by using [Server.Run].
3334type Server struct {
3435 // fixed at creation
35- impl * Implementation
36- opts ServerOptions
36+ impl * Implementation
37+ opts ServerOptions
38+ logger * slog.Logger
3739
3840 mu sync.Mutex
3941 prompts * featureSet [* serverPrompt ]
@@ -50,6 +52,8 @@ type Server struct {
5052type ServerOptions struct {
5153 // Optional instructions for connected clients.
5254 Instructions string
55+ // Logger is used for server-side logging. If nil, slog.Default() is used.
56+ Logger * slog.Logger
5357 // If non-nil, called when "notifications/initialized" is received.
5458 InitializedHandler func (context.Context , * ServerRequest [* InitializedParams ])
5559 // PageSize is the maximum number of items to return in a single page for
@@ -108,9 +112,11 @@ func NewServer(impl *Implementation, opts *ServerOptions) *Server {
108112 if opts .UnsubscribeHandler != nil && opts .SubscribeHandler == nil {
109113 panic ("UnsubscribeHandler requires SubscribeHandler" )
110114 }
115+ l := ensureLogger (opts .Logger )
111116 return & Server {
112117 impl : impl ,
113118 opts : * opts ,
119+ logger : l ,
114120 prompts : newFeatureSet (func (p * serverPrompt ) string { return p .prompt .Name }),
115121 tools : newFeatureSet (func (t * serverTool ) string { return t .tool .Name }),
116122 resources : newFeatureSet (func (r * serverResource ) string { return r .resource .URI }),
@@ -462,6 +468,7 @@ func (s *Server) ResourceUpdated(ctx context.Context, params *ResourceUpdatedNot
462468 sessions := slices .Collect (maps .Keys (subscribedSessions ))
463469 s .mu .Unlock ()
464470 notifySessions (sessions , notificationResourceUpdated , params )
471+ s .logger .Info ("resource updated notification sent" , "uri" , params .URI , "subscriber_count" , len (sessions ))
465472 return nil
466473}
467474
@@ -479,6 +486,7 @@ func (s *Server) subscribe(ctx context.Context, req *ServerRequest[*SubscribePar
479486 s .resourceSubscriptions [req .Params .URI ] = make (map [* ServerSession ]bool )
480487 }
481488 s .resourceSubscriptions [req .Params .URI ][req .Session ] = true
489+ s .logger .Info ("resource subscribed" , "uri" , req .Params .URI , "session_id" , req .Session .ID ())
482490
483491 return & emptyResult {}, nil
484492}
@@ -500,6 +508,7 @@ func (s *Server) unsubscribe(ctx context.Context, req *ServerRequest[*Unsubscrib
500508 delete (s .resourceSubscriptions , req .Params .URI )
501509 }
502510 }
511+ s .logger .Info ("resource unsubscribed" , "uri" , req .Params .URI , "session_id" , req .Session .ID ())
503512
504513 return & emptyResult {}, nil
505514}
@@ -518,8 +527,10 @@ func (s *Server) unsubscribe(ctx context.Context, req *ServerRequest[*Unsubscrib
518527// It need not be called on servers that are used for multiple concurrent connections,
519528// as with [StreamableHTTPHandler].
520529func (s * Server ) Run (ctx context.Context , t Transport ) error {
530+ s .logger .Info ("server run start" )
521531 ss , err := s .Connect (ctx , t , nil )
522532 if err != nil {
533+ s .logger .Error ("server connect failed" , "error" , err )
523534 return err
524535 }
525536
@@ -531,8 +542,14 @@ func (s *Server) Run(ctx context.Context, t Transport) error {
531542 select {
532543 case <- ctx .Done ():
533544 ss .Close ()
545+ s .logger .Info ("server run cancelled" , "error" , ctx .Err ())
534546 return ctx .Err ()
535547 case err := <- ssClosed :
548+ if err != nil {
549+ s .logger .Error ("server session ended with error" , "error" , err )
550+ } else {
551+ s .logger .Info ("server session ended" )
552+ }
536553 return err
537554 }
538555}
@@ -548,6 +565,7 @@ func (s *Server) bind(mcpConn Connection, conn *jsonrpc2.Connection, state *Serv
548565 s .mu .Lock ()
549566 s .sessions = append (s .sessions , ss )
550567 s .mu .Unlock ()
568+ s .logger .Info ("server session connected" , "session_id" , ss .ID ())
551569 return ss
552570}
553571
@@ -563,6 +581,7 @@ func (s *Server) disconnect(cc *ServerSession) {
563581 for _ , subscribedSessions := range s .resourceSubscriptions {
564582 delete (subscribedSessions , cc )
565583 }
584+ s .logger .Info ("server session disconnected" , "session_id" , cc .ID ())
566585}
567586
568587// ServerSessionOptions configures the server session.
@@ -583,7 +602,13 @@ func (s *Server) Connect(ctx context.Context, t Transport, opts *ServerSessionOp
583602 if opts != nil {
584603 state = opts .State
585604 }
586- return connect (ctx , t , s , state )
605+ s .logger .Info ("server connecting" )
606+ ss , err := connect (ctx , t , s , state )
607+ if err != nil {
608+ s .logger .Error ("server connect error" , "error" , err )
609+ return nil , err
610+ }
611+ return ss , nil
587612}
588613
589614// TODO: (nit) move all ServerSession methods below the ServerSession declaration.
@@ -606,14 +631,17 @@ func (ss *ServerSession) initialized(ctx context.Context, params *InitializedPar
606631 })
607632
608633 if ! wasInit {
634+ ss .server .logger .Warn ("initialized before initialize" )
609635 return nil , fmt .Errorf ("%q before %q" , notificationInitialized , methodInitialize )
610636 }
611637 if wasInitd {
638+ ss .server .logger .Warn ("duplicate initialized notification" )
612639 return nil , fmt .Errorf ("duplicate %q received" , notificationInitialized )
613640 }
614641 if h := ss .server .opts .InitializedHandler ; h != nil {
615642 h (ctx , serverRequestFor (ss , params ))
616643 }
644+ ss .server .logger .Info ("session initialized" )
617645 return nil , nil
618646}
619647
@@ -798,6 +826,7 @@ func (ss *ServerSession) handle(ctx context.Context, req *jsonrpc.Request) (any,
798826 case methodInitialize , methodPing , notificationInitialized :
799827 default :
800828 if ! initialized {
829+ ss .server .logger .Warn ("method invalid during initialization" , "method" , req .Method )
801830 return nil , fmt .Errorf ("method %q is invalid during session initialization" , req .Method )
802831 }
803832 }
@@ -842,6 +871,7 @@ func (ss *ServerSession) setLevel(_ context.Context, params *SetLevelParams) (*e
842871 ss .updateState (func (state * ServerSessionState ) {
843872 state .LogLevel = params .Level
844873 })
874+ ss .server .logger .Info ("client log level set" , "level" , params .Level )
845875 return & emptyResult {}, nil
846876}
847877
0 commit comments