@@ -91,6 +91,10 @@ MCP Go handles all the complex protocol details and server management, so you ca
9191 - [ Tools] ( #tools )
9292 - [ Prompts] ( #prompts )
9393- [ Examples] ( #examples )
94+ - [ Extras] ( #extras )
95+ - [ Session Management] ( #session-management )
96+ - [ Request Hooks] ( #request-hooks )
97+ - [ Tool Handler Middleware] ( #tool-handler-middleware )
9498- [ Contributing] ( #contributing )
9599 - [ Prerequisites] ( #prerequisites )
96100 - [ Installation] ( #installation-1 )
@@ -516,6 +520,214 @@ For examples, see the `examples/` directory.
516520
517521## Extras
518522
523+ ### Session Management
524+
525+ MCP-Go provides a robust session management system that allows you to:
526+ - Maintain separate state for each connected client
527+ - Register and track client sessions
528+ - Send notifications to specific clients
529+ - Provide per-session tool customization
530+
531+ <details >
532+ <summary >Show Session Management Examples</summary >
533+
534+ #### Basic Session Handling
535+
536+ ``` go
537+ // Create a server with session capabilities
538+ s := server.NewMCPServer (
539+ " Session Demo" ,
540+ " 1.0.0" ,
541+ server.WithToolCapabilities (true ),
542+ )
543+
544+ // Implement your own ClientSession
545+ type MySession struct {
546+ id string
547+ notifChannel chan mcp.JSONRPCNotification
548+ isInitialized bool
549+ // Add custom fields for your application
550+ }
551+
552+ // Implement the ClientSession interface
553+ func (s *MySession ) SessionID () string {
554+ return s.id
555+ }
556+
557+ func (s *MySession ) NotificationChannel () chan <- mcp .JSONRPCNotification {
558+ return s.notifChannel
559+ }
560+
561+ func (s *MySession ) Initialize () {
562+ s.isInitialized = true
563+ }
564+
565+ func (s *MySession ) Initialized () bool {
566+ return s.isInitialized
567+ }
568+
569+ // Register a session
570+ session := &MySession{
571+ id: " user-123" ,
572+ notifChannel: make (chan mcp.JSONRPCNotification , 10 ),
573+ }
574+ if err := s.RegisterSession (context.Background (), session); err != nil {
575+ log.Printf (" Failed to register session: %v " , err)
576+ }
577+
578+ // Send notification to a specific client
579+ err := s.SendNotificationToSpecificClient (
580+ session.SessionID (),
581+ " notification/update" ,
582+ map [string ]any{" message" : " New data available!" },
583+ )
584+ if err != nil {
585+ log.Printf (" Failed to send notification: %v " , err)
586+ }
587+
588+ // Unregister session when done
589+ s.UnregisterSession (context.Background (), session.SessionID ())
590+ ```
591+
592+ #### Per-Session Tools
593+
594+ For more advanced use cases, you can implement the ` SessionWithTools ` interface to support per-session tool customization:
595+
596+ ``` go
597+ // Implement SessionWithTools interface for per-session tools
598+ type MyAdvancedSession struct {
599+ MySession // Embed the basic session
600+ sessionTools map [string ]server.ServerTool
601+ }
602+
603+ // Implement additional methods for SessionWithTools
604+ func (s *MyAdvancedSession ) GetSessionTools () map [string ]server .ServerTool {
605+ return s.sessionTools
606+ }
607+
608+ func (s *MyAdvancedSession ) SetSessionTools (tools map [string ]server .ServerTool ) {
609+ s.sessionTools = tools
610+ }
611+
612+ // Create and register a session with tools support
613+ advSession := &MyAdvancedSession{
614+ MySession : MySession {
615+ id: " user-456" ,
616+ notifChannel: make (chan mcp.JSONRPCNotification , 10 ),
617+ },
618+ sessionTools: make (map [string ]server.ServerTool ),
619+ }
620+ if err := s.RegisterSession (context.Background (), advSession); err != nil {
621+ log.Printf (" Failed to register session: %v " , err)
622+ }
623+
624+ // Add session-specific tools
625+ userSpecificTool := mcp.NewTool (
626+ " user_data" ,
627+ mcp.WithDescription (" Access user-specific data" ),
628+ )
629+ // You can use AddSessionTool (similar to AddTool)
630+ err := s.AddSessionTool (
631+ advSession.SessionID (),
632+ userSpecificTool,
633+ func (ctx context .Context , req mcp .CallToolRequest ) (*mcp .CallToolResult , error ) {
634+ // This handler is only available to this specific session
635+ return mcp.NewToolResultText (" User-specific data for " + advSession.SessionID ()), nil
636+ },
637+ )
638+ if err != nil {
639+ log.Printf (" Failed to add session tool: %v " , err)
640+ }
641+
642+ // Or use AddSessionTools directly with ServerTool
643+ /*
644+ err := s.AddSessionTools(
645+ advSession.SessionID(),
646+ server.ServerTool{
647+ Tool: userSpecificTool,
648+ Handler: func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
649+ // This handler is only available to this specific session
650+ return mcp.NewToolResultText("User-specific data for " + advSession.SessionID()), nil
651+ },
652+ },
653+ )
654+ if err != nil {
655+ log.Printf("Failed to add session tool: %v", err)
656+ }
657+ */
658+
659+ // Delete session-specific tools when no longer needed
660+ err = s.DeleteSessionTools (advSession.SessionID (), " user_data" )
661+ if err != nil {
662+ log.Printf (" Failed to delete session tool: %v " , err)
663+ }
664+ ```
665+
666+ #### Tool Filtering
667+
668+ You can also apply filters to control which tools are available to certain sessions:
669+
670+ ``` go
671+ // Add a tool filter that only shows tools with certain prefixes
672+ s := server.NewMCPServer (
673+ " Tool Filtering Demo" ,
674+ " 1.0.0" ,
675+ server.WithToolCapabilities (true ),
676+ server.WithToolFilter (func (ctx context.Context , tools []mcp.Tool ) []mcp.Tool {
677+ // Get session from context
678+ session := server.ClientSessionFromContext (ctx)
679+ if session == nil {
680+ return tools // Return all tools if no session
681+ }
682+
683+ // Example: filter tools based on session ID prefix
684+ if strings.HasPrefix (session.SessionID (), " admin-" ) {
685+ // Admin users get all tools
686+ return tools
687+ } else {
688+ // Regular users only get tools with "public-" prefix
689+ var filteredTools []mcp.Tool
690+ for _ , tool := range tools {
691+ if strings.HasPrefix (tool.Name , " public-" ) {
692+ filteredTools = append (filteredTools, tool)
693+ }
694+ }
695+ return filteredTools
696+ }
697+ }),
698+ )
699+ ```
700+
701+ #### Working with Context
702+
703+ The session context is automatically passed to tool and resource handlers:
704+
705+ ``` go
706+ s.AddTool (mcp.NewTool (" session_aware" ), func (ctx context.Context , req mcp.CallToolRequest ) (*mcp.CallToolResult , error ) {
707+ // Get the current session from context
708+ session := server.ClientSessionFromContext (ctx)
709+ if session == nil {
710+ return mcp.NewToolResultError (" No active session" ), nil
711+ }
712+
713+ return mcp.NewToolResultText (" Hello, session " + session.SessionID ()), nil
714+ })
715+
716+ // When using handlers in HTTP/SSE servers, you need to pass the context with the session
717+ httpHandler := func (w http.ResponseWriter , r *http.Request ) {
718+ // Get session from somewhere (like a cookie or header)
719+ session := getSessionFromRequest (r)
720+
721+ // Add session to context
722+ ctx := s.WithContext (r.Context (), session)
723+
724+ // Use this context when handling requests
725+ // ...
726+ }
727+ ```
728+
729+ </details >
730+
519731### Request Hooks
520732
521733Hook into the request lifecycle by creating a ` Hooks ` object with your
0 commit comments