Skip to content

Commit 8cdb6c6

Browse files
authored
Dustin/138 deadlock remove global mutex (#139)
* fix: remove global mutex * chore: remove uncessary comments * chore: remove comment * chore: remove comment * chore: explicit err checks
1 parent c509bf9 commit 8cdb6c6

File tree

2 files changed

+253
-33
lines changed

2 files changed

+253
-33
lines changed

server/server.go

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,14 @@ type NotificationHandlerFunc func(ctx context.Context, notification mcp.JSONRPCN
141141
// MCPServer implements a Model Control Protocol server that can handle various types of requests
142142
// including resources, prompts, and tools.
143143
type MCPServer struct {
144-
mu sync.RWMutex // Add mutex for protecting shared resources
144+
// Separate mutexes for different resource types
145+
resourcesMu sync.RWMutex
146+
promptsMu sync.RWMutex
147+
toolsMu sync.RWMutex
148+
middlewareMu sync.RWMutex
149+
notificationHandlersMu sync.RWMutex
150+
capabilitiesMu sync.RWMutex
151+
145152
name string
146153
version string
147154
instructions string
@@ -301,7 +308,9 @@ func WithToolHandlerMiddleware(
301308
toolHandlerMiddleware ToolHandlerMiddleware,
302309
) ServerOption {
303310
return func(s *MCPServer) {
311+
s.middlewareMu.Lock()
304312
s.toolHandlerMiddlewares = append(s.toolHandlerMiddlewares, toolHandlerMiddleware)
313+
s.middlewareMu.Unlock()
305314
}
306315
}
307316

@@ -396,11 +405,14 @@ func (s *MCPServer) AddResource(
396405
resource mcp.Resource,
397406
handler ResourceHandlerFunc,
398407
) {
408+
s.capabilitiesMu.Lock()
399409
if s.capabilities.resources == nil {
400410
s.capabilities.resources = &resourceCapabilities{}
401411
}
402-
s.mu.Lock()
403-
defer s.mu.Unlock()
412+
s.capabilitiesMu.Unlock()
413+
414+
s.resourcesMu.Lock()
415+
defer s.resourcesMu.Unlock()
404416
s.resources[resource.URI] = resourceEntry{
405417
resource: resource,
406418
handler: handler,
@@ -412,11 +424,14 @@ func (s *MCPServer) AddResourceTemplate(
412424
template mcp.ResourceTemplate,
413425
handler ResourceTemplateHandlerFunc,
414426
) {
427+
s.capabilitiesMu.Lock()
415428
if s.capabilities.resources == nil {
416429
s.capabilities.resources = &resourceCapabilities{}
417430
}
418-
s.mu.Lock()
419-
defer s.mu.Unlock()
431+
s.capabilitiesMu.Unlock()
432+
433+
s.resourcesMu.Lock()
434+
defer s.resourcesMu.Unlock()
420435
s.resourceTemplates[template.URITemplate.Raw()] = resourceTemplateEntry{
421436
template: template,
422437
handler: handler,
@@ -425,11 +440,14 @@ func (s *MCPServer) AddResourceTemplate(
425440

426441
// AddPrompt registers a new prompt handler with the given name
427442
func (s *MCPServer) AddPrompt(prompt mcp.Prompt, handler PromptHandlerFunc) {
443+
s.capabilitiesMu.Lock()
428444
if s.capabilities.prompts == nil {
429445
s.capabilities.prompts = &promptCapabilities{}
430446
}
431-
s.mu.Lock()
432-
defer s.mu.Unlock()
447+
s.capabilitiesMu.Unlock()
448+
449+
s.promptsMu.Lock()
450+
defer s.promptsMu.Unlock()
433451
s.prompts[prompt.Name] = prompt
434452
s.promptHandlers[prompt.Name] = handler
435453
}
@@ -441,34 +459,37 @@ func (s *MCPServer) AddTool(tool mcp.Tool, handler ToolHandlerFunc) {
441459

442460
// AddTools registers multiple tools at once
443461
func (s *MCPServer) AddTools(tools ...ServerTool) {
462+
s.capabilitiesMu.Lock()
444463
if s.capabilities.tools == nil {
445464
s.capabilities.tools = &toolCapabilities{}
446465
}
447-
s.mu.Lock()
466+
s.capabilitiesMu.Unlock()
467+
468+
s.toolsMu.Lock()
448469
for _, entry := range tools {
449470
s.tools[entry.Tool.Name] = entry
450471
}
451-
s.mu.Unlock()
472+
s.toolsMu.Unlock()
452473

453474
// Send notification to all initialized sessions
454475
s.sendNotificationToAllClients("notifications/tools/list_changed", nil)
455476
}
456477

457478
// SetTools replaces all existing tools with the provided list
458479
func (s *MCPServer) SetTools(tools ...ServerTool) {
459-
s.mu.Lock()
480+
s.toolsMu.Lock()
460481
s.tools = make(map[string]ServerTool)
461-
s.mu.Unlock()
482+
s.toolsMu.Unlock()
462483
s.AddTools(tools...)
463484
}
464485

465486
// DeleteTools removes a tool from the server
466487
func (s *MCPServer) DeleteTools(names ...string) {
467-
s.mu.Lock()
488+
s.toolsMu.Lock()
468489
for _, name := range names {
469490
delete(s.tools, name)
470491
}
471-
s.mu.Unlock()
492+
s.toolsMu.Unlock()
472493

473494
// Send notification to all initialized sessions
474495
s.sendNotificationToAllClients("notifications/tools/list_changed", nil)
@@ -479,8 +500,8 @@ func (s *MCPServer) AddNotificationHandler(
479500
method string,
480501
handler NotificationHandlerFunc,
481502
) {
482-
s.mu.Lock()
483-
defer s.mu.Unlock()
503+
s.notificationHandlersMu.Lock()
504+
defer s.notificationHandlersMu.Unlock()
484505
s.notificationHandlers[method] = handler
485506
}
486507

@@ -589,12 +610,12 @@ func (s *MCPServer) handleListResources(
589610
id interface{},
590611
request mcp.ListResourcesRequest,
591612
) (*mcp.ListResourcesResult, *requestError) {
592-
s.mu.RLock()
613+
s.resourcesMu.RLock()
593614
resources := make([]mcp.Resource, 0, len(s.resources))
594615
for _, entry := range s.resources {
595616
resources = append(resources, entry.resource)
596617
}
597-
s.mu.RUnlock()
618+
s.resourcesMu.RUnlock()
598619

599620
// Sort the resources by name
600621
sort.Slice(resources, func(i, j int) bool {
@@ -622,12 +643,12 @@ func (s *MCPServer) handleListResourceTemplates(
622643
id interface{},
623644
request mcp.ListResourceTemplatesRequest,
624645
) (*mcp.ListResourceTemplatesResult, *requestError) {
625-
s.mu.RLock()
646+
s.resourcesMu.RLock()
626647
templates := make([]mcp.ResourceTemplate, 0, len(s.resourceTemplates))
627648
for _, entry := range s.resourceTemplates {
628649
templates = append(templates, entry.template)
629650
}
630-
s.mu.RUnlock()
651+
s.resourcesMu.RUnlock()
631652
sort.Slice(templates, func(i, j int) bool {
632653
return templates[i].Name < templates[j].Name
633654
})
@@ -653,11 +674,11 @@ func (s *MCPServer) handleReadResource(
653674
id interface{},
654675
request mcp.ReadResourceRequest,
655676
) (*mcp.ReadResourceResult, *requestError) {
656-
s.mu.RLock()
677+
s.resourcesMu.RLock()
657678
// First try direct resource handlers
658679
if entry, ok := s.resources[request.Params.URI]; ok {
659680
handler := entry.handler
660-
s.mu.RUnlock()
681+
s.resourcesMu.RUnlock()
661682
contents, err := handler(ctx, request)
662683
if err != nil {
663684
return nil, &requestError{
@@ -686,7 +707,7 @@ func (s *MCPServer) handleReadResource(
686707
break
687708
}
688709
}
689-
s.mu.RUnlock()
710+
s.resourcesMu.RUnlock()
690711

691712
if matched {
692713
contents, err := matchedHandler(ctx, request)
@@ -717,12 +738,12 @@ func (s *MCPServer) handleListPrompts(
717738
id interface{},
718739
request mcp.ListPromptsRequest,
719740
) (*mcp.ListPromptsResult, *requestError) {
720-
s.mu.RLock()
741+
s.promptsMu.RLock()
721742
prompts := make([]mcp.Prompt, 0, len(s.prompts))
722743
for _, prompt := range s.prompts {
723744
prompts = append(prompts, prompt)
724745
}
725-
s.mu.RUnlock()
746+
s.promptsMu.RUnlock()
726747

727748
// sort prompts by name
728749
sort.Slice(prompts, func(i, j int) bool {
@@ -750,9 +771,9 @@ func (s *MCPServer) handleGetPrompt(
750771
id interface{},
751772
request mcp.GetPromptRequest,
752773
) (*mcp.GetPromptResult, *requestError) {
753-
s.mu.RLock()
774+
s.promptsMu.RLock()
754775
handler, ok := s.promptHandlers[request.Params.Name]
755-
s.mu.RUnlock()
776+
s.promptsMu.RUnlock()
756777

757778
if !ok {
758779
return nil, &requestError{
@@ -779,7 +800,7 @@ func (s *MCPServer) handleListTools(
779800
id interface{},
780801
request mcp.ListToolsRequest,
781802
) (*mcp.ListToolsResult, *requestError) {
782-
s.mu.RLock()
803+
s.toolsMu.RLock()
783804
tools := make([]mcp.Tool, 0, len(s.tools))
784805

785806
// Get all tool names for consistent ordering
@@ -795,6 +816,8 @@ func (s *MCPServer) handleListTools(
795816
for _, name := range toolNames {
796817
tools = append(tools, s.tools[name].Tool)
797818
}
819+
s.toolsMu.RUnlock()
820+
798821
toolsToReturn, nextCursor, err := listByPagination[mcp.Tool](ctx, s, request.Params.Cursor, tools)
799822
if err != nil {
800823
return nil, &requestError{
@@ -817,9 +840,9 @@ func (s *MCPServer) handleToolCall(
817840
id interface{},
818841
request mcp.CallToolRequest,
819842
) (*mcp.CallToolResult, *requestError) {
820-
s.mu.RLock()
843+
s.toolsMu.RLock()
821844
tool, ok := s.tools[request.Params.Name]
822-
s.mu.RUnlock()
845+
s.toolsMu.RUnlock()
823846

824847
if !ok {
825848
return nil, &requestError{
@@ -830,9 +853,16 @@ func (s *MCPServer) handleToolCall(
830853
}
831854

832855
finalHandler := tool.Handler
833-
for i := len(s.toolHandlerMiddlewares) - 1; i >= 0; i-- {
834-
finalHandler = s.toolHandlerMiddlewares[i](finalHandler)
856+
857+
s.middlewareMu.RLock()
858+
mw := s.toolHandlerMiddlewares
859+
s.middlewareMu.RUnlock()
860+
861+
// Apply middlewares in reverse order
862+
for i := len(mw) - 1; i >= 0; i-- {
863+
finalHandler = mw[i](finalHandler)
835864
}
865+
836866
result, err := finalHandler(ctx, request)
837867
if err != nil {
838868
return nil, &requestError{
@@ -849,9 +879,9 @@ func (s *MCPServer) handleNotification(
849879
ctx context.Context,
850880
notification mcp.JSONRPCNotification,
851881
) mcp.JSONRPCMessage {
852-
s.mu.RLock()
882+
s.notificationHandlersMu.RLock()
853883
handler, ok := s.notificationHandlers[notification.Method]
854-
s.mu.RUnlock()
884+
s.notificationHandlersMu.RUnlock()
855885

856886
if ok {
857887
handler(ctx, notification)

0 commit comments

Comments
 (0)