@@ -963,6 +963,50 @@ func TestMCPServer_Prompts(t *testing.T) {
963963 assert .Equal (t , "test-prompt-2" , prompts [1 ].Name )
964964 },
965965 },
966+ {
967+ name : "SetPrompts sends single notifications/prompts/list_changed with one active session" ,
968+ action : func (t * testing.T , server * MCPServer , notificationChannel chan mcp.JSONRPCNotification ) {
969+ err := server .RegisterSession (context .TODO (), & fakeSession {
970+ sessionID : "test" ,
971+ notificationChannel : notificationChannel ,
972+ initialized : true ,
973+ })
974+ require .NoError (t , err )
975+ server .SetPrompts (ServerPrompt {
976+ Prompt : mcp.Prompt {
977+ Name : "test-prompt-1" ,
978+ Description : "A test prompt" ,
979+ Arguments : []mcp.PromptArgument {
980+ {
981+ Name : "arg1" ,
982+ Description : "First argument" ,
983+ },
984+ },
985+ },
986+ Handler : nil ,
987+ }, ServerPrompt {
988+ Prompt : mcp.Prompt {
989+ Name : "test-prompt-2" ,
990+ Description : "Another test prompt" ,
991+ Arguments : []mcp.PromptArgument {
992+ {
993+ Name : "arg2" ,
994+ Description : "Second argument" ,
995+ },
996+ },
997+ },
998+ Handler : nil ,
999+ })
1000+ },
1001+ expectedNotifications : 1 ,
1002+ validate : func (t * testing.T , notifications []mcp.JSONRPCNotification , promptsList mcp.JSONRPCMessage ) {
1003+ assert .Equal (t , mcp .MethodNotificationPromptsListChanged , notifications [0 ].Method )
1004+ prompts := promptsList .(mcp.JSONRPCResponse ).Result .(mcp.ListPromptsResult ).Prompts
1005+ assert .Len (t , prompts , 2 )
1006+ assert .Equal (t , "test-prompt-1" , prompts [0 ].Name )
1007+ assert .Equal (t , "test-prompt-2" , prompts [1 ].Name )
1008+ },
1009+ },
9661010 }
9671011 for _ , tt := range tests {
9681012 t .Run (tt .name , func (t * testing.T ) {
@@ -998,6 +1042,211 @@ func TestMCPServer_Prompts(t *testing.T) {
9981042 }
9991043}
10001044
1045+ func TestMCPServer_Resources (t * testing.T ) {
1046+ tests := []struct {
1047+ name string
1048+ action func (* testing.T , * MCPServer , chan mcp.JSONRPCNotification )
1049+ expectedNotifications int
1050+ validate func (* testing.T , []mcp.JSONRPCNotification , mcp.JSONRPCMessage )
1051+ }{
1052+ {
1053+ name : "DeleteResources sends single notifications/resources/list_changed" ,
1054+ action : func (t * testing.T , server * MCPServer , notificationChannel chan mcp.JSONRPCNotification ) {
1055+ err := server .RegisterSession (context .TODO (), & fakeSession {
1056+ sessionID : "test" ,
1057+ notificationChannel : notificationChannel ,
1058+ initialized : true ,
1059+ })
1060+ require .NoError (t , err )
1061+ server .AddResource (
1062+ mcp.Resource {
1063+ URI : "test://test-resource-1" ,
1064+ Name : "Test Resource 1" ,
1065+ },
1066+ func (ctx context.Context , request mcp.ReadResourceRequest ) ([]mcp.ResourceContents , error ) {
1067+ return []mcp.ResourceContents {}, nil
1068+ },
1069+ )
1070+ server .DeleteResources ("test://test-resource-1" )
1071+ },
1072+ expectedNotifications : 2 ,
1073+ validate : func (t * testing.T , notifications []mcp.JSONRPCNotification , resourcesList mcp.JSONRPCMessage ) {
1074+ // One for AddResource
1075+ assert .Equal (t , mcp .MethodNotificationResourcesListChanged , notifications [0 ].Method )
1076+ // One for DeleteResources
1077+ assert .Equal (t , mcp .MethodNotificationResourcesListChanged , notifications [1 ].Method )
1078+
1079+ // Expect a successful response with an empty list of resources
1080+ resp , ok := resourcesList .(mcp.JSONRPCResponse )
1081+ assert .True (t , ok , "Expected JSONRPCResponse, got %T" , resourcesList )
1082+
1083+ result , ok := resp .Result .(mcp.ListResourcesResult )
1084+ assert .True (t , ok , "Expected ListResourcesResult, got %T" , resp .Result )
1085+
1086+ assert .Empty (t , result .Resources , "Expected empty resources list" )
1087+ },
1088+ },
1089+ {
1090+ name : "DeleteResources removes the first resource and retains the other" ,
1091+ action : func (t * testing.T , server * MCPServer , notificationChannel chan mcp.JSONRPCNotification ) {
1092+ err := server .RegisterSession (context .TODO (), & fakeSession {
1093+ sessionID : "test" ,
1094+ notificationChannel : notificationChannel ,
1095+ initialized : true ,
1096+ })
1097+ require .NoError (t , err )
1098+ server .AddResource (
1099+ mcp.Resource {
1100+ URI : "test://test-resource-1" ,
1101+ Name : "Test Resource 1" ,
1102+ },
1103+ func (ctx context.Context , request mcp.ReadResourceRequest ) ([]mcp.ResourceContents , error ) {
1104+ return []mcp.ResourceContents {}, nil
1105+ },
1106+ )
1107+ server .AddResource (
1108+ mcp.Resource {
1109+ URI : "test://test-resource-2" ,
1110+ Name : "Test Resource 2" ,
1111+ },
1112+ func (ctx context.Context , request mcp.ReadResourceRequest ) ([]mcp.ResourceContents , error ) {
1113+ return []mcp.ResourceContents {}, nil
1114+ },
1115+ )
1116+ server .DeleteResources ("test://test-resource-1" )
1117+ },
1118+ expectedNotifications : 3 ,
1119+ validate : func (t * testing.T , notifications []mcp.JSONRPCNotification , resourcesList mcp.JSONRPCMessage ) {
1120+ // first notification expected for AddResource test-resource-1
1121+ assert .Equal (t , mcp .MethodNotificationResourcesListChanged , notifications [0 ].Method )
1122+ // second notification expected for AddResource test-resource-2
1123+ assert .Equal (t , mcp .MethodNotificationResourcesListChanged , notifications [1 ].Method )
1124+ // third notification expected for DeleteResources test-resource-1
1125+ assert .Equal (t , mcp .MethodNotificationResourcesListChanged , notifications [2 ].Method )
1126+
1127+ // Confirm the resource list contains only test-resource-2
1128+ resources := resourcesList .(mcp.JSONRPCResponse ).Result .(mcp.ListResourcesResult ).Resources
1129+ assert .Len (t , resources , 1 )
1130+ assert .Equal (t , "test://test-resource-2" , resources [0 ].URI )
1131+ },
1132+ },
1133+ {
1134+ name : "DeleteResources with non-existent resources does nothing and not receives notifications from MCPServer" ,
1135+ action : func (t * testing.T , server * MCPServer , notificationChannel chan mcp.JSONRPCNotification ) {
1136+ err := server .RegisterSession (context .TODO (), & fakeSession {
1137+ sessionID : "test" ,
1138+ notificationChannel : notificationChannel ,
1139+ initialized : true ,
1140+ })
1141+ require .NoError (t , err )
1142+ server .AddResource (
1143+ mcp.Resource {
1144+ URI : "test://test-resource-1" ,
1145+ Name : "Test Resource 1" ,
1146+ },
1147+ func (ctx context.Context , request mcp.ReadResourceRequest ) ([]mcp.ResourceContents , error ) {
1148+ return []mcp.ResourceContents {}, nil
1149+ },
1150+ )
1151+ server .AddResource (
1152+ mcp.Resource {
1153+ URI : "test://test-resource-2" ,
1154+ Name : "Test Resource 2" ,
1155+ },
1156+ func (ctx context.Context , request mcp.ReadResourceRequest ) ([]mcp.ResourceContents , error ) {
1157+ return []mcp.ResourceContents {}, nil
1158+ },
1159+ )
1160+ // Remove non-existing resources
1161+ server .DeleteResources ("test://test-resource-3" , "test://test-resource-4" )
1162+ },
1163+ expectedNotifications : 2 ,
1164+ validate : func (t * testing.T , notifications []mcp.JSONRPCNotification , resourcesList mcp.JSONRPCMessage ) {
1165+ // first notification expected for AddResource test-resource-1
1166+ assert .Equal (t , mcp .MethodNotificationResourcesListChanged , notifications [0 ].Method )
1167+ // second notification expected for AddResource test-resource-2
1168+ assert .Equal (t , mcp .MethodNotificationResourcesListChanged , notifications [1 ].Method )
1169+
1170+ // Confirm the resource list does not change
1171+ resources := resourcesList .(mcp.JSONRPCResponse ).Result .(mcp.ListResourcesResult ).Resources
1172+ assert .Len (t , resources , 2 )
1173+ // Resources are sorted by name
1174+ assert .Equal (t , "test://test-resource-1" , resources [0 ].URI )
1175+ assert .Equal (t , "test://test-resource-2" , resources [1 ].URI )
1176+ },
1177+ },
1178+ {
1179+ name : "SetResources sends single notifications/resources/list_changed with one active session" ,
1180+ action : func (t * testing.T , server * MCPServer , notificationChannel chan mcp.JSONRPCNotification ) {
1181+ err := server .RegisterSession (context .TODO (), & fakeSession {
1182+ sessionID : "test" ,
1183+ notificationChannel : notificationChannel ,
1184+ initialized : true ,
1185+ })
1186+ require .NoError (t , err )
1187+ server .SetResources (ServerResource {
1188+ Resource : mcp.Resource {
1189+ URI : "test://test-resource-1" ,
1190+ Name : "Test Resource 1" ,
1191+ },
1192+ Handler : func (ctx context.Context , request mcp.ReadResourceRequest ) ([]mcp.ResourceContents , error ) {
1193+ return []mcp.ResourceContents {}, nil
1194+ },
1195+ }, ServerResource {
1196+ Resource : mcp.Resource {
1197+ URI : "test://test-resource-2" ,
1198+ Name : "Test Resource 2" ,
1199+ },
1200+ Handler : func (ctx context.Context , request mcp.ReadResourceRequest ) ([]mcp.ResourceContents , error ) {
1201+ return []mcp.ResourceContents {}, nil
1202+ },
1203+ })
1204+ },
1205+ expectedNotifications : 1 ,
1206+ validate : func (t * testing.T , notifications []mcp.JSONRPCNotification , resourcesList mcp.JSONRPCMessage ) {
1207+ assert .Equal (t , mcp .MethodNotificationResourcesListChanged , notifications [0 ].Method )
1208+ resources := resourcesList .(mcp.JSONRPCResponse ).Result .(mcp.ListResourcesResult ).Resources
1209+ assert .Len (t , resources , 2 )
1210+ // Resources are sorted by name
1211+ assert .Equal (t , "test://test-resource-1" , resources [0 ].URI )
1212+ assert .Equal (t , "test://test-resource-2" , resources [1 ].URI )
1213+ },
1214+ },
1215+ }
1216+ for _ , tt := range tests {
1217+ t .Run (tt .name , func (t * testing.T ) {
1218+ ctx := context .Background ()
1219+ server := NewMCPServer ("test-server" , "1.0.0" , WithResourceCapabilities (true , true ))
1220+ _ = server .HandleMessage (ctx , []byte (`{
1221+ "jsonrpc": "2.0",
1222+ "id": 1,
1223+ "method": "initialize"
1224+ }` ))
1225+ notificationChannel := make (chan mcp.JSONRPCNotification , 100 )
1226+ notifications := make ([]mcp.JSONRPCNotification , 0 )
1227+ tt .action (t , server , notificationChannel )
1228+ for done := false ; ! done ; {
1229+ select {
1230+ case serverNotification := <- notificationChannel :
1231+ notifications = append (notifications , serverNotification )
1232+ if len (notifications ) == tt .expectedNotifications {
1233+ done = true
1234+ }
1235+ case <- time .After (1 * time .Second ):
1236+ done = true
1237+ }
1238+ }
1239+ assert .Len (t , notifications , tt .expectedNotifications )
1240+ resourcesList := server .HandleMessage (ctx , []byte (`{
1241+ "jsonrpc": "2.0",
1242+ "id": 1,
1243+ "method": "resources/list"
1244+ }` ))
1245+ tt .validate (t , notifications , resourcesList )
1246+ })
1247+ }
1248+ }
1249+
10011250func TestMCPServer_HandleInvalidMessages (t * testing.T ) {
10021251 var errs []error
10031252 hooks := & Hooks {}
0 commit comments