@@ -17,10 +17,14 @@ import {
1717 ErrorCode ,
1818 ListToolsResultSchema ,
1919 CallToolResultSchema ,
20+ ListResourcesResultSchema ,
21+ ListResourceTemplatesResultSchema ,
22+ ReadResourceResultSchema ,
2023} from "../types.js" ;
2124import { Transport } from "../shared/transport.js" ;
2225import { InMemoryTransport } from "../inMemory.js" ;
2326import { Client } from "../client/index.js" ;
27+ import { UriTemplate } from "../shared/uriTemplate.js" ;
2428
2529test ( "should accept latest protocol version" , async ( ) => {
2630 let sendPromiseResolve : ( value : unknown ) => void ;
@@ -478,6 +482,7 @@ test("should handle server cancelling a request", async () => {
478482 // Request should be rejected
479483 await expect ( createMessagePromise ) . rejects . toBe ( "Cancelled by test" ) ;
480484} ) ;
485+
481486test ( "should handle request timeout" , async ( ) => {
482487 const server = new Server (
483488 {
@@ -927,3 +932,273 @@ describe("Server.tool", () => {
927932 ) . rejects . toThrow ( / T o o l n o n e x i s t e n t - t o o l n o t f o u n d / ) ;
928933 } ) ;
929934} ) ;
935+
936+ describe ( "Server.resource" , ( ) => {
937+ test ( "should register resource with uri and readCallback" , async ( ) => {
938+ const server = new Server ( {
939+ name : "test server" ,
940+ version : "1.0" ,
941+ } ) ;
942+ const client = new Client ( {
943+ name : "test client" ,
944+ version : "1.0" ,
945+ } ) ;
946+
947+ server . resource ( "test" , "test://resource" , async ( ) => ( {
948+ contents : [
949+ {
950+ uri : "test://resource" ,
951+ text : "Test content" ,
952+ } ,
953+ ] ,
954+ } ) ) ;
955+
956+ const [ clientTransport , serverTransport ] =
957+ InMemoryTransport . createLinkedPair ( ) ;
958+
959+ await Promise . all ( [
960+ client . connect ( clientTransport ) ,
961+ server . connect ( serverTransport ) ,
962+ ] ) ;
963+
964+ const result = await client . request (
965+ {
966+ method : "resources/list" ,
967+ } ,
968+ ListResourcesResultSchema ,
969+ ) ;
970+
971+ expect ( result . resources ) . toHaveLength ( 1 ) ;
972+ expect ( result . resources [ 0 ] . name ) . toBe ( "test" ) ;
973+ expect ( result . resources [ 0 ] . uri ) . toBe ( "test://resource" ) ;
974+ } ) ;
975+
976+ test ( "should register resource with metadata" , async ( ) => {
977+ const server = new Server ( {
978+ name : "test server" ,
979+ version : "1.0" ,
980+ } ) ;
981+ const client = new Client ( {
982+ name : "test client" ,
983+ version : "1.0" ,
984+ } ) ;
985+
986+ server . resource (
987+ "test" ,
988+ "test://resource" ,
989+ {
990+ description : "Test resource" ,
991+ mimeType : "text/plain" ,
992+ } ,
993+ async ( ) => ( {
994+ contents : [
995+ {
996+ uri : "test://resource" ,
997+ text : "Test content" ,
998+ } ,
999+ ] ,
1000+ } ) ,
1001+ ) ;
1002+
1003+ const [ clientTransport , serverTransport ] =
1004+ InMemoryTransport . createLinkedPair ( ) ;
1005+
1006+ await Promise . all ( [
1007+ client . connect ( clientTransport ) ,
1008+ server . connect ( serverTransport ) ,
1009+ ] ) ;
1010+
1011+ const result = await client . request (
1012+ {
1013+ method : "resources/list" ,
1014+ } ,
1015+ ListResourcesResultSchema ,
1016+ ) ;
1017+
1018+ expect ( result . resources ) . toHaveLength ( 1 ) ;
1019+ expect ( result . resources [ 0 ] . description ) . toBe ( "Test resource" ) ;
1020+ expect ( result . resources [ 0 ] . mimeType ) . toBe ( "text/plain" ) ;
1021+ } ) ;
1022+
1023+ test ( "should register resource template" , async ( ) => {
1024+ const server = new Server ( {
1025+ name : "test server" ,
1026+ version : "1.0" ,
1027+ } ) ;
1028+ const client = new Client ( {
1029+ name : "test client" ,
1030+ version : "1.0" ,
1031+ } ) ;
1032+
1033+ server . resource (
1034+ "test" ,
1035+ new UriTemplate ( "test://resource/{id}" ) ,
1036+ async ( ) => ( {
1037+ contents : [
1038+ {
1039+ uri : "test://resource/123" ,
1040+ text : "Test content" ,
1041+ } ,
1042+ ] ,
1043+ } ) ,
1044+ ) ;
1045+
1046+ const [ clientTransport , serverTransport ] =
1047+ InMemoryTransport . createLinkedPair ( ) ;
1048+
1049+ await Promise . all ( [
1050+ client . connect ( clientTransport ) ,
1051+ server . connect ( serverTransport ) ,
1052+ ] ) ;
1053+
1054+ const result = await client . request (
1055+ {
1056+ method : "resources/templates/list" ,
1057+ } ,
1058+ ListResourceTemplatesResultSchema ,
1059+ ) ;
1060+
1061+ expect ( result . resourceTemplates ) . toHaveLength ( 1 ) ;
1062+ expect ( result . resourceTemplates [ 0 ] . name ) . toBe ( "test" ) ;
1063+ expect ( result . resourceTemplates [ 0 ] . uriTemplate ) . toBe (
1064+ "test://resource/{id}" ,
1065+ ) ;
1066+ } ) ;
1067+
1068+ test ( "should prevent duplicate resource registration" , ( ) => {
1069+ const server = new Server ( {
1070+ name : "test server" ,
1071+ version : "1.0" ,
1072+ } ) ;
1073+
1074+ server . resource ( "test" , "test://resource" , async ( ) => ( {
1075+ contents : [
1076+ {
1077+ uri : "test://resource" ,
1078+ text : "Test content" ,
1079+ } ,
1080+ ] ,
1081+ } ) ) ;
1082+
1083+ expect ( ( ) => {
1084+ server . resource ( "test2" , "test://resource" , async ( ) => ( {
1085+ contents : [
1086+ {
1087+ uri : "test://resource" ,
1088+ text : "Test content 2" ,
1089+ } ,
1090+ ] ,
1091+ } ) ) ;
1092+ } ) . toThrow ( / a l r e a d y r e g i s t e r e d / ) ;
1093+ } ) ;
1094+
1095+ test ( "should prevent duplicate resource template registration" , ( ) => {
1096+ const server = new Server ( {
1097+ name : "test server" ,
1098+ version : "1.0" ,
1099+ } ) ;
1100+
1101+ server . resource (
1102+ "test" ,
1103+ new UriTemplate ( "test://resource/{id}" ) ,
1104+ async ( ) => ( {
1105+ contents : [
1106+ {
1107+ uri : "test://resource/123" ,
1108+ text : "Test content" ,
1109+ } ,
1110+ ] ,
1111+ } ) ,
1112+ ) ;
1113+
1114+ expect ( ( ) => {
1115+ server . resource (
1116+ "test" ,
1117+ new UriTemplate ( "test://resource/{id}" ) ,
1118+ async ( ) => ( {
1119+ contents : [
1120+ {
1121+ uri : "test://resource/123" ,
1122+ text : "Test content 2" ,
1123+ } ,
1124+ ] ,
1125+ } ) ,
1126+ ) ;
1127+ } ) . toThrow ( / a l r e a d y r e g i s t e r e d / ) ;
1128+ } ) ;
1129+
1130+ test ( "should handle resource read errors gracefully" , async ( ) => {
1131+ const server = new Server ( {
1132+ name : "test server" ,
1133+ version : "1.0" ,
1134+ } ) ;
1135+ const client = new Client ( {
1136+ name : "test client" ,
1137+ version : "1.0" ,
1138+ } ) ;
1139+
1140+ server . resource ( "error-test" , "test://error" , async ( ) => {
1141+ throw new Error ( "Resource read failed" ) ;
1142+ } ) ;
1143+
1144+ const [ clientTransport , serverTransport ] =
1145+ InMemoryTransport . createLinkedPair ( ) ;
1146+
1147+ await Promise . all ( [
1148+ client . connect ( clientTransport ) ,
1149+ server . connect ( serverTransport ) ,
1150+ ] ) ;
1151+
1152+ await expect (
1153+ client . request (
1154+ {
1155+ method : "resources/read" ,
1156+ params : {
1157+ uri : "test://error" ,
1158+ } ,
1159+ } ,
1160+ ReadResourceResultSchema ,
1161+ ) ,
1162+ ) . rejects . toThrow ( / R e s o u r c e r e a d f a i l e d / ) ;
1163+ } ) ;
1164+
1165+ test ( "should throw McpError for invalid resource URI" , async ( ) => {
1166+ const server = new Server ( {
1167+ name : "test server" ,
1168+ version : "1.0" ,
1169+ } ) ;
1170+ const client = new Client ( {
1171+ name : "test client" ,
1172+ version : "1.0" ,
1173+ } ) ;
1174+
1175+ server . resource ( "test" , "test://resource" , async ( ) => ( {
1176+ contents : [
1177+ {
1178+ uri : "test://resource" ,
1179+ text : "Test content" ,
1180+ } ,
1181+ ] ,
1182+ } ) ) ;
1183+
1184+ const [ clientTransport , serverTransport ] =
1185+ InMemoryTransport . createLinkedPair ( ) ;
1186+
1187+ await Promise . all ( [
1188+ client . connect ( clientTransport ) ,
1189+ server . connect ( serverTransport ) ,
1190+ ] ) ;
1191+
1192+ await expect (
1193+ client . request (
1194+ {
1195+ method : "resources/read" ,
1196+ params : {
1197+ uri : "test://nonexistent" ,
1198+ } ,
1199+ } ,
1200+ ReadResourceResultSchema ,
1201+ ) ,
1202+ ) . rejects . toThrow ( / R e s o u r c e t e s t : \/ \/ n o n e x i s t e n t n o t f o u n d / ) ;
1203+ } ) ;
1204+ } ) ;
0 commit comments