Skip to content

Commit 1b036ad

Browse files
committed
[session-resources] tests
1 parent 0cbfd2a commit 1b036ad

File tree

2 files changed

+122
-1
lines changed

2 files changed

+122
-1
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/mark3labs/mcp-go
22

3-
go 1.23
3+
go 1.23.0
44

55
require (
66
github.com/google/uuid v1.6.0

server/session_test.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"errors"
7+
"maps"
78
"sync"
89
"sync/atomic"
910
"testing"
@@ -100,6 +101,60 @@ func (f *sessionTestClientWithTools) SetSessionTools(tools map[string]ServerTool
100101
f.sessionTools = toolsCopy
101102
}
102103

104+
// sessionTestClientWithTools implements the SessionWithTools interface for testing
105+
type sessionTestClientWithResources struct {
106+
sessionID string
107+
notificationChannel chan mcp.JSONRPCNotification
108+
initialized bool
109+
sessionResources map[string]ServerResource
110+
mu sync.RWMutex // Mutex to protect concurrent access to sessionTools
111+
}
112+
113+
func (f *sessionTestClientWithResources) SessionID() string {
114+
return f.sessionID
115+
}
116+
117+
func (f *sessionTestClientWithResources) NotificationChannel() chan<- mcp.JSONRPCNotification {
118+
return f.notificationChannel
119+
}
120+
121+
func (f *sessionTestClientWithResources) Initialize() {
122+
f.initialized = true
123+
}
124+
125+
func (f *sessionTestClientWithResources) Initialized() bool {
126+
return f.initialized
127+
}
128+
129+
func (f *sessionTestClientWithResources) GetSessionResources() map[string]ServerResource {
130+
f.mu.RLock()
131+
defer f.mu.RUnlock()
132+
133+
if f.sessionResources == nil {
134+
return nil
135+
}
136+
137+
// Return a copy of the map to prevent concurrent modification
138+
resourcesCopy := make(map[string]ServerResource, len(f.sessionResources))
139+
maps.Copy(resourcesCopy, f.sessionResources)
140+
return resourcesCopy
141+
}
142+
143+
func (f *sessionTestClientWithResources) SetSessionResources(resources map[string]ServerResource) {
144+
f.mu.Lock()
145+
defer f.mu.Unlock()
146+
147+
if resources == nil {
148+
f.sessionResources = nil
149+
return
150+
}
151+
152+
// Create a copy of the map to prevent concurrent modification
153+
resourcesCopy := make(map[string]ServerResource, len(resources))
154+
maps.Copy(resourcesCopy, resources)
155+
f.sessionResources = resourcesCopy
156+
}
157+
103158
// sessionTestClientWithClientInfo implements the SessionWithClientInfo interface for testing
104159
type sessionTestClientWithClientInfo struct {
105160
sessionID string
@@ -260,6 +315,72 @@ func TestSessionWithTools_Integration(t *testing.T) {
260315
})
261316
}
262317

318+
func TestSessionWithResources_Integration(t *testing.T) {
319+
server := NewMCPServer("test-server", "1.0.0", WithToolCapabilities(true))
320+
321+
// Create session-specific resources
322+
sessionResource := ServerResource{
323+
Resource: mcp.NewResource("ui://resource", "session-resource"),
324+
Handler: func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
325+
return []mcp.ResourceContents{mcp.TextResourceContents{Text: "session-tool result"}}, nil
326+
},
327+
}
328+
329+
// Create a session with resources
330+
session := &sessionTestClientWithResources{
331+
sessionID: "session-1",
332+
notificationChannel: make(chan mcp.JSONRPCNotification, 10),
333+
initialized: true,
334+
sessionResources: map[string]ServerResource{
335+
"session-resource": sessionResource,
336+
},
337+
}
338+
339+
// Register the session
340+
err := server.RegisterSession(context.Background(), session)
341+
require.NoError(t, err)
342+
343+
// Test that we can access the session-specific tool
344+
testReq := mcp.ReadResourceRequest{}
345+
testReq.Params.URI = "ui://resource"
346+
testReq.Params.Arguments = map[string]any{}
347+
348+
// Call using session context
349+
sessionCtx := server.WithContext(context.Background(), session)
350+
351+
// Check if the session was stored in the context correctly
352+
s := ClientSessionFromContext(sessionCtx)
353+
require.NotNil(t, s, "Session should be available from context")
354+
assert.Equal(t, session.SessionID(), s.SessionID(), "Session ID should match")
355+
356+
// Check if the session can be cast to SessionWithResources
357+
swr, ok := s.(SessionWithResources)
358+
require.True(t, ok, "Session should implement SessionWithResources")
359+
360+
// Check if the resources are accessible
361+
resources := swr.GetSessionResources()
362+
require.NotNil(t, resources, "Session resources should be available")
363+
require.Contains(t, resources, "session-resource", "Session should have session-resource")
364+
365+
// Test session resource access with session context
366+
t.Run("test session resource access", func(t *testing.T) {
367+
// First test directly getting the resource from session resources
368+
resource, exists := resources["session-resource"]
369+
require.True(t, exists, "Session resource should exist in the map")
370+
require.NotNil(t, resource, "Session resource should not be nil")
371+
372+
// Now test calling directly with the handler
373+
result, err := resource.Handler(sessionCtx, testReq)
374+
require.NoError(t, err, "No error calling session resource handler directly")
375+
require.NotNil(t, result, "Result should not be nil")
376+
require.Len(t, result, 1, "Result should have one content item")
377+
378+
textContent, ok := result[0].(mcp.TextResourceContents)
379+
require.True(t, ok, "Content should be TextResourceContents")
380+
assert.Equal(t, "session-tool result", textContent.Text, "Result text should match")
381+
})
382+
}
383+
263384
func TestMCPServer_ToolsWithSessionTools(t *testing.T) {
264385
// Basic test to verify that session-specific tools are returned correctly in a tools list
265386
server := NewMCPServer("test-server", "1.0.0", WithToolCapabilities(true))

0 commit comments

Comments
 (0)