@@ -765,4 +765,175 @@ describe('Zod v4', () => {
765765 await expect ( transport . start ( ) ) . rejects . toThrow ( 'Transport already started' ) ;
766766 } ) ;
767767 } ) ;
768+
769+ describe ( 'HTTPServerTransport - onerror callback' , ( ) => {
770+ let transport : WebStandardStreamableHTTPServerTransport ;
771+ let mcpServer : McpServer ;
772+ let errors : Error [ ] ;
773+
774+ beforeEach ( async ( ) => {
775+ errors = [ ] ;
776+ mcpServer = new McpServer ( { name : 'test-server' , version : '1.0.0' } , { capabilities : { } } ) ;
777+
778+ transport = new WebStandardStreamableHTTPServerTransport ( {
779+ sessionIdGenerator : ( ) => randomUUID ( )
780+ } ) ;
781+
782+ transport . onerror = err => errors . push ( err ) ;
783+
784+ await mcpServer . connect ( transport ) ;
785+ } ) ;
786+
787+ afterEach ( async ( ) => {
788+ await transport . close ( ) ;
789+ } ) ;
790+
791+ async function initializeServer ( ) : Promise < string > {
792+ const request = createRequest ( 'POST' , TEST_MESSAGES . initialize ) ;
793+ const response = await transport . handleRequest ( request ) ;
794+ return response . headers . get ( 'mcp-session-id' ) as string ;
795+ }
796+
797+ it ( 'should call onerror for invalid JSON' , async ( ) => {
798+ const request = new Request ( 'http://localhost/mcp' , {
799+ method : 'POST' ,
800+ headers : {
801+ Accept : 'application/json, text/event-stream' ,
802+ 'Content-Type' : 'application/json'
803+ } ,
804+ body : 'not valid json'
805+ } ) ;
806+
807+ const response = await transport . handleRequest ( request ) ;
808+
809+ expect ( response . status ) . toBe ( 400 ) ;
810+ expect ( errors . length ) . toBeGreaterThan ( 0 ) ;
811+ expect ( errors [ 0 ] . message ) . toContain ( 'Parse error' ) ;
812+ } ) ;
813+
814+ it ( 'should call onerror for invalid JSON-RPC message' , async ( ) => {
815+ const request = new Request ( 'http://localhost/mcp' , {
816+ method : 'POST' ,
817+ headers : {
818+ Accept : 'application/json, text/event-stream' ,
819+ 'Content-Type' : 'application/json'
820+ } ,
821+ body : JSON . stringify ( { not : 'valid jsonrpc' } )
822+ } ) ;
823+
824+ const response = await transport . handleRequest ( request ) ;
825+
826+ expect ( response . status ) . toBe ( 400 ) ;
827+ expect ( errors . length ) . toBeGreaterThan ( 0 ) ;
828+ expect ( errors [ 0 ] . message ) . toContain ( 'Parse error' ) ;
829+ } ) ;
830+
831+ it ( 'should call onerror for missing Accept header on POST' , async ( ) => {
832+ const request = createRequest ( 'POST' , TEST_MESSAGES . initialize , { accept : 'application/json' } ) ;
833+
834+ const response = await transport . handleRequest ( request ) ;
835+
836+ expect ( response . status ) . toBe ( 406 ) ;
837+ expect ( errors . length ) . toBeGreaterThan ( 0 ) ;
838+ expect ( errors [ 0 ] . message ) . toContain ( 'Not Acceptable' ) ;
839+ } ) ;
840+
841+ it ( 'should call onerror for unsupported Content-Type' , async ( ) => {
842+ const request = new Request ( 'http://localhost/mcp' , {
843+ method : 'POST' ,
844+ headers : {
845+ Accept : 'application/json, text/event-stream' ,
846+ 'Content-Type' : 'text/plain'
847+ } ,
848+ body : JSON . stringify ( TEST_MESSAGES . initialize )
849+ } ) ;
850+
851+ const response = await transport . handleRequest ( request ) ;
852+
853+ expect ( response . status ) . toBe ( 415 ) ;
854+ expect ( errors . length ) . toBeGreaterThan ( 0 ) ;
855+ expect ( errors [ 0 ] . message ) . toContain ( 'Unsupported Media Type' ) ;
856+ } ) ;
857+
858+ it ( 'should call onerror for server not initialized' , async ( ) => {
859+ const request = createRequest ( 'POST' , TEST_MESSAGES . toolsList ) ;
860+
861+ const response = await transport . handleRequest ( request ) ;
862+
863+ expect ( response . status ) . toBe ( 400 ) ;
864+ expect ( errors . length ) . toBeGreaterThan ( 0 ) ;
865+ expect ( errors [ 0 ] . message ) . toContain ( 'Server not initialized' ) ;
866+ } ) ;
867+
868+ it ( 'should call onerror for invalid session ID' , async ( ) => {
869+ await initializeServer ( ) ;
870+
871+ const request = createRequest ( 'POST' , TEST_MESSAGES . toolsList , { sessionId : 'invalid-session-id' } ) ;
872+
873+ const response = await transport . handleRequest ( request ) ;
874+
875+ expect ( response . status ) . toBe ( 404 ) ;
876+ expect ( errors . length ) . toBeGreaterThan ( 0 ) ;
877+ expect ( errors [ 0 ] . message ) . toContain ( 'Session not found' ) ;
878+ } ) ;
879+
880+ it ( 'should call onerror for re-initialization attempt' , async ( ) => {
881+ await initializeServer ( ) ;
882+
883+ const request = createRequest ( 'POST' , TEST_MESSAGES . initialize ) ;
884+
885+ const response = await transport . handleRequest ( request ) ;
886+
887+ expect ( response . status ) . toBe ( 400 ) ;
888+ expect ( errors . length ) . toBeGreaterThan ( 0 ) ;
889+ expect ( errors [ 0 ] . message ) . toContain ( 'Server already initialized' ) ;
890+ } ) ;
891+
892+ it ( 'should call onerror for GET without Accept header' , async ( ) => {
893+ const sessionId = await initializeServer ( ) ;
894+
895+ const request = createRequest ( 'GET' , undefined , { sessionId, accept : 'application/json' } ) ;
896+
897+ const response = await transport . handleRequest ( request ) ;
898+
899+ expect ( response . status ) . toBe ( 406 ) ;
900+ expect ( errors . length ) . toBeGreaterThan ( 0 ) ;
901+ expect ( errors [ 0 ] . message ) . toContain ( 'Not Acceptable' ) ;
902+ } ) ;
903+
904+ it ( 'should call onerror for concurrent SSE streams' , async ( ) => {
905+ const sessionId = await initializeServer ( ) ;
906+
907+ const request1 = createRequest ( 'GET' , undefined , { sessionId } ) ;
908+ await transport . handleRequest ( request1 ) ;
909+
910+ const request2 = createRequest ( 'GET' , undefined , { sessionId } ) ;
911+ const response2 = await transport . handleRequest ( request2 ) ;
912+
913+ expect ( response2 . status ) . toBe ( 409 ) ;
914+ expect ( errors . length ) . toBeGreaterThan ( 0 ) ;
915+ expect ( errors [ 0 ] . message ) . toContain ( 'Conflict' ) ;
916+ } ) ;
917+
918+ it ( 'should call onerror for unsupported protocol version' , async ( ) => {
919+ const sessionId = await initializeServer ( ) ;
920+
921+ const request = new Request ( 'http://localhost/mcp' , {
922+ method : 'POST' ,
923+ headers : {
924+ 'Content-Type' : 'application/json' ,
925+ Accept : 'application/json, text/event-stream' ,
926+ 'mcp-session-id' : sessionId ,
927+ 'mcp-protocol-version' : 'unsupported-version'
928+ } ,
929+ body : JSON . stringify ( TEST_MESSAGES . toolsList )
930+ } ) ;
931+
932+ const response = await transport . handleRequest ( request ) ;
933+
934+ expect ( response . status ) . toBe ( 400 ) ;
935+ expect ( errors . length ) . toBeGreaterThan ( 0 ) ;
936+ expect ( errors [ 0 ] . message ) . toContain ( 'Unsupported protocol version' ) ;
937+ } ) ;
938+ } ) ;
768939} ) ;
0 commit comments