@@ -963,6 +963,50 @@ func TestMCPServer_Prompts(t *testing.T) {
963
963
assert .Equal (t , "test-prompt-2" , prompts [1 ].Name )
964
964
},
965
965
},
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
+ },
966
1010
}
967
1011
for _ , tt := range tests {
968
1012
t .Run (tt .name , func (t * testing.T ) {
@@ -998,6 +1042,211 @@ func TestMCPServer_Prompts(t *testing.T) {
998
1042
}
999
1043
}
1000
1044
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
+
1001
1250
func TestMCPServer_HandleInvalidMessages (t * testing.T ) {
1002
1251
var errs []error
1003
1252
hooks := & Hooks {}
0 commit comments