@@ -829,267 +829,27 @@ describe("Content block modality validation", () => {
829829 await bridgeTransport . close ( ) ;
830830 } ) ;
831831
832- describe ( "Guest -side validation (sendMessage) " , ( ) => {
833- it ( "throws when sending unsupported content type " , async ( ) => {
832+ describe ( "Host -side validation" , ( ) => {
833+ it ( "host rejects unsupported content in onmessage " , async ( ) => {
834834 [ appTransport , bridgeTransport ] = InMemoryTransport . createLinkedPair ( ) ;
835835 const capabilities : McpUiHostCapabilities = {
836836 ...testHostCapabilities ,
837837 message : { text : { } } ,
838838 } ;
839- bridge = new AppBridge (
840- createMockClient ( ) as Client ,
841- testHostInfo ,
842- capabilities ,
843- ) ;
839+ bridge = new AppBridge ( null , testHostInfo , capabilities ) ;
844840 bridge . onmessage = async ( ) => ( { } ) ;
845841 app = new App ( testAppInfo , { } , { autoResize : false } ) ;
846842
847843 await bridge . connect ( bridgeTransport ) ;
848844 await app . connect ( appTransport ) ;
849845
850- expect ( ( ) =>
846+ await expect (
851847 app . sendMessage ( {
852848 role : "user" ,
853849 content : [
854850 { type : "image" , data : "base64data" , mimeType : "image/png" } ,
855851 ] ,
856852 } ) ,
857- ) . toThrow ( "unsupported content type(s): image" ) ;
858- } ) ;
859-
860- it ( "allows content when modality is declared" , async ( ) => {
861- [ appTransport , bridgeTransport ] = InMemoryTransport . createLinkedPair ( ) ;
862- const capabilities : McpUiHostCapabilities = {
863- ...testHostCapabilities ,
864- message : { text : { } , image : { } } ,
865- } ;
866- bridge = new AppBridge (
867- createMockClient ( ) as Client ,
868- testHostInfo ,
869- capabilities ,
870- ) ;
871- bridge . onmessage = async ( ) => ( { } ) ;
872- app = new App ( testAppInfo , { } , { autoResize : false } ) ;
873-
874- await bridge . connect ( bridgeTransport ) ;
875- await app . connect ( appTransport ) ;
876-
877- const result = await app . sendMessage ( {
878- role : "user" ,
879- content : [
880- { type : "text" , text : "hello" } ,
881- { type : "image" , data : "base64data" , mimeType : "image/png" } ,
882- ] ,
883- } ) ;
884- expect ( result ) . toEqual ( { } ) ;
885- } ) ;
886-
887- it ( "skips validation when message capability is not declared (backwards compat)" , async ( ) => {
888- [ appTransport , bridgeTransport ] = InMemoryTransport . createLinkedPair ( ) ;
889- // No message capability declared
890- bridge = new AppBridge (
891- createMockClient ( ) as Client ,
892- testHostInfo ,
893- testHostCapabilities ,
894- ) ;
895- bridge . onmessage = async ( ) => ( { } ) ;
896- app = new App ( testAppInfo , { } , { autoResize : false } ) ;
897-
898- await bridge . connect ( bridgeTransport ) ;
899- await app . connect ( appTransport ) ;
900-
901- // Should succeed even with image content since message capability is not declared
902- const result = await app . sendMessage ( {
903- role : "user" ,
904- content : [ { type : "image" , data : "base64data" , mimeType : "image/png" } ] ,
905- } ) ;
906- expect ( result ) . toEqual ( { } ) ;
907- } ) ;
908-
909- it ( "rejects all types when message capability is empty object" , async ( ) => {
910- [ appTransport , bridgeTransport ] = InMemoryTransport . createLinkedPair ( ) ;
911- const capabilities : McpUiHostCapabilities = {
912- ...testHostCapabilities ,
913- message : { } ,
914- } ;
915- bridge = new AppBridge (
916- createMockClient ( ) as Client ,
917- testHostInfo ,
918- capabilities ,
919- ) ;
920- bridge . onmessage = async ( ) => ( { } ) ;
921- app = new App ( testAppInfo , { } , { autoResize : false } ) ;
922-
923- await bridge . connect ( bridgeTransport ) ;
924- await app . connect ( appTransport ) ;
925-
926- expect ( ( ) =>
927- app . sendMessage ( {
928- role : "user" ,
929- content : [ { type : "text" , text : "hello" } ] ,
930- } ) ,
931- ) . toThrow ( "unsupported content type(s): text" ) ;
932- } ) ;
933-
934- it ( "resource_link type maps to resourceLink modality correctly" , async ( ) => {
935- [ appTransport , bridgeTransport ] = InMemoryTransport . createLinkedPair ( ) ;
936- const capabilities : McpUiHostCapabilities = {
937- ...testHostCapabilities ,
938- message : { resourceLink : { } } ,
939- } ;
940- bridge = new AppBridge (
941- createMockClient ( ) as Client ,
942- testHostInfo ,
943- capabilities ,
944- ) ;
945- bridge . onmessage = async ( ) => ( { } ) ;
946- app = new App ( testAppInfo , { } , { autoResize : false } ) ;
947-
948- await bridge . connect ( bridgeTransport ) ;
949- await app . connect ( appTransport ) ;
950-
951- const result = await app . sendMessage ( {
952- role : "user" ,
953- content : [
954- {
955- type : "resource_link" ,
956- uri : "test://resource" ,
957- name : "Test Resource" ,
958- } ,
959- ] ,
960- } ) ;
961- expect ( result ) . toEqual ( { } ) ;
962- } ) ;
963- } ) ;
964-
965- describe ( "Guest-side validation (updateModelContext)" , ( ) => {
966- it ( "throws when sending unsupported content type" , async ( ) => {
967- [ appTransport , bridgeTransport ] = InMemoryTransport . createLinkedPair ( ) ;
968- const capabilities : McpUiHostCapabilities = {
969- ...testHostCapabilities ,
970- updateModelContext : { text : { } } ,
971- } ;
972- bridge = new AppBridge (
973- createMockClient ( ) as Client ,
974- testHostInfo ,
975- capabilities ,
976- ) ;
977- bridge . onupdatemodelcontext = async ( ) => ( { } ) ;
978- app = new App ( testAppInfo , { } , { autoResize : false } ) ;
979-
980- await bridge . connect ( bridgeTransport ) ;
981- await app . connect ( appTransport ) ;
982-
983- expect ( ( ) =>
984- app . updateModelContext ( {
985- content : [
986- { type : "image" , data : "base64data" , mimeType : "image/png" } ,
987- ] ,
988- } ) ,
989- ) . toThrow ( "unsupported content type(s): image" ) ;
990- } ) ;
991-
992- it ( "throws when sending structuredContent without capability" , async ( ) => {
993- [ appTransport , bridgeTransport ] = InMemoryTransport . createLinkedPair ( ) ;
994- const capabilities : McpUiHostCapabilities = {
995- ...testHostCapabilities ,
996- updateModelContext : { text : { } } ,
997- } ;
998- bridge = new AppBridge (
999- createMockClient ( ) as Client ,
1000- testHostInfo ,
1001- capabilities ,
1002- ) ;
1003- bridge . onupdatemodelcontext = async ( ) => ( { } ) ;
1004- app = new App ( testAppInfo , { } , { autoResize : false } ) ;
1005-
1006- await bridge . connect ( bridgeTransport ) ;
1007- await app . connect ( appTransport ) ;
1008-
1009- expect ( ( ) =>
1010- app . updateModelContext ( {
1011- structuredContent : { key : "value" } ,
1012- } ) ,
1013- ) . toThrow ( "structuredContent is not supported" ) ;
1014- } ) ;
1015-
1016- it ( "allows structuredContent when declared" , async ( ) => {
1017- [ appTransport , bridgeTransport ] = InMemoryTransport . createLinkedPair ( ) ;
1018- const capabilities : McpUiHostCapabilities = {
1019- ...testHostCapabilities ,
1020- updateModelContext : { text : { } , structuredContent : { } } ,
1021- } ;
1022- bridge = new AppBridge (
1023- createMockClient ( ) as Client ,
1024- testHostInfo ,
1025- capabilities ,
1026- ) ;
1027- bridge . onupdatemodelcontext = async ( ) => ( { } ) ;
1028- app = new App ( testAppInfo , { } , { autoResize : false } ) ;
1029-
1030- await bridge . connect ( bridgeTransport ) ;
1031- await app . connect ( appTransport ) ;
1032-
1033- const result = await app . updateModelContext ( {
1034- structuredContent : { key : "value" } ,
1035- } ) ;
1036- expect ( result ) . toEqual ( { } ) ;
1037- } ) ;
1038-
1039- it ( "skips validation when updateModelContext capability is not declared" , async ( ) => {
1040- [ appTransport , bridgeTransport ] = InMemoryTransport . createLinkedPair ( ) ;
1041- bridge = new AppBridge (
1042- createMockClient ( ) as Client ,
1043- testHostInfo ,
1044- testHostCapabilities ,
1045- ) ;
1046- bridge . onupdatemodelcontext = async ( ) => ( { } ) ;
1047- app = new App ( testAppInfo , { } , { autoResize : false } ) ;
1048-
1049- await bridge . connect ( bridgeTransport ) ;
1050- await app . connect ( appTransport ) ;
1051-
1052- const result = await app . updateModelContext ( {
1053- content : [ { type : "image" , data : "base64data" , mimeType : "image/png" } ] ,
1054- structuredContent : { key : "value" } ,
1055- } ) ;
1056- expect ( result ) . toEqual ( { } ) ;
1057- } ) ;
1058- } ) ;
1059-
1060- describe ( "Host-side validation" , ( ) => {
1061- it ( "host rejects unsupported content in onmessage" , async ( ) => {
1062- [ appTransport , bridgeTransport ] = InMemoryTransport . createLinkedPair ( ) ;
1063- const capabilities : McpUiHostCapabilities = {
1064- ...testHostCapabilities ,
1065- message : { text : { } } ,
1066- } ;
1067- // Use null client so we don't get guest-side validation
1068- // (the bridge still validates on the host side)
1069- bridge = new AppBridge ( null , testHostInfo , capabilities ) ;
1070- bridge . onmessage = async ( ) => ( { } ) ;
1071-
1072- // Create app without host capabilities knowledge to bypass guest validation
1073- app = new App ( testAppInfo , { } , { autoResize : false } ) ;
1074-
1075- await bridge . connect ( bridgeTransport ) ;
1076- await app . connect ( appTransport ) ;
1077-
1078- // The app received capabilities during connect, so it will validate.
1079- // To test host-side validation independently, we directly send the request.
1080- await expect (
1081- app . request (
1082- {
1083- method : "ui/message" as any ,
1084- params : {
1085- role : "user" ,
1086- content : [
1087- { type : "image" , data : "base64data" , mimeType : "image/png" } ,
1088- ] ,
1089- } ,
1090- } ,
1091- EmptyResultSchema ,
1092- ) ,
1093853 ) . rejects . toThrow ( "unsupported content type(s): image" ) ;
1094854 } ) ;
1095855
@@ -1107,17 +867,11 @@ describe("Content block modality validation", () => {
1107867 await app . connect ( appTransport ) ;
1108868
1109869 await expect (
1110- app . request (
1111- {
1112- method : "ui/update-model-context" as any ,
1113- params : {
1114- content : [
1115- { type : "audio" , data : "base64" , mimeType : "audio/mp3" } ,
1116- ] ,
1117- } ,
1118- } ,
1119- EmptyResultSchema ,
1120- ) ,
870+ app . updateModelContext ( {
871+ content : [
872+ { type : "audio" , data : "base64" , mimeType : "audio/mp3" } ,
873+ ] ,
874+ } ) ,
1121875 ) . rejects . toThrow ( "unsupported content type(s): audio" ) ;
1122876 } ) ;
1123877
@@ -1135,15 +889,9 @@ describe("Content block modality validation", () => {
1135889 await app . connect ( appTransport ) ;
1136890
1137891 await expect (
1138- app . request (
1139- {
1140- method : "ui/update-model-context" as any ,
1141- params : {
1142- structuredContent : { key : "value" } ,
1143- } ,
1144- } ,
1145- EmptyResultSchema ,
1146- ) ,
892+ app . updateModelContext ( {
893+ structuredContent : { key : "value" } ,
894+ } ) ,
1147895 ) . rejects . toThrow ( "structuredContent is not supported" ) ;
1148896 } ) ;
1149897 } ) ;
0 commit comments