@@ -674,4 +674,118 @@ describe("StreamableHTTPServerTransport", () => {
674674 expect ( onErrorMock ) . toHaveBeenCalled ( ) ;
675675 } ) ;
676676 } ) ;
677+
678+ describe ( "Handling Pre-Parsed Body" , ( ) => {
679+ it ( "should accept pre-parsed request body" , async ( ) => {
680+ const message : JSONRPCMessage = {
681+ jsonrpc : "2.0" ,
682+ method : "initialize" ,
683+ params : {
684+ clientInfo : { name : "test-client" , version : "1.0" } ,
685+ protocolVersion : "2025-03-26"
686+ } ,
687+ id : "pre-parsed-test" ,
688+ } ;
689+
690+ // Create a request without actual body content
691+ const req = createMockRequest ( {
692+ method : "POST" ,
693+ headers : {
694+ "content-type" : "application/json" ,
695+ "accept" : "application/json" ,
696+ } ,
697+ // No body provided here - it will be passed as parsedBody
698+ } ) ;
699+
700+ const onMessageMock = jest . fn ( ) ;
701+ transport . onmessage = onMessageMock ;
702+
703+ // Pass the pre-parsed body directly
704+ await transport . handleRequest ( req , mockResponse , message ) ;
705+
706+ // Verify the message was processed correctly
707+ expect ( onMessageMock ) . toHaveBeenCalledWith ( message ) ;
708+ expect ( mockResponse . writeHead ) . toHaveBeenCalledWith (
709+ 200 ,
710+ expect . objectContaining ( {
711+ "Content-Type" : "application/json" ,
712+ } )
713+ ) ;
714+ } ) ;
715+
716+ it ( "should handle pre-parsed batch messages" , async ( ) => {
717+ const batchMessages : JSONRPCMessage [ ] = [
718+ {
719+ jsonrpc : "2.0" ,
720+ method : "method1" ,
721+ params : { data : "test1" } ,
722+ id : "batch1"
723+ } ,
724+ {
725+ jsonrpc : "2.0" ,
726+ method : "method2" ,
727+ params : { data : "test2" } ,
728+ id : "batch2"
729+ } ,
730+ ] ;
731+
732+ // Create a request without actual body content
733+ const req = createMockRequest ( {
734+ method : "POST" ,
735+ headers : {
736+ "content-type" : "application/json" ,
737+ "accept" : "text/event-stream" ,
738+ "mcp-session-id" : transport . sessionId ,
739+ } ,
740+ // No body provided here - it will be passed as parsedBody
741+ } ) ;
742+
743+ const onMessageMock = jest . fn ( ) ;
744+ transport . onmessage = onMessageMock ;
745+
746+ // Pass the pre-parsed body directly
747+ await transport . handleRequest ( req , mockResponse , batchMessages ) ;
748+
749+ // Should be called for each message in the batch
750+ expect ( onMessageMock ) . toHaveBeenCalledTimes ( 2 ) ;
751+ expect ( onMessageMock ) . toHaveBeenCalledWith ( batchMessages [ 0 ] ) ;
752+ expect ( onMessageMock ) . toHaveBeenCalledWith ( batchMessages [ 1 ] ) ;
753+ } ) ;
754+
755+ it ( "should prefer pre-parsed body over request body" , async ( ) => {
756+ const requestBodyMessage : JSONRPCMessage = {
757+ jsonrpc : "2.0" ,
758+ method : "fromRequestBody" ,
759+ params : { } ,
760+ id : "request-body" ,
761+ } ;
762+
763+ const parsedBodyMessage : JSONRPCMessage = {
764+ jsonrpc : "2.0" ,
765+ method : "fromParsedBody" ,
766+ params : { } ,
767+ id : "parsed-body" ,
768+ } ;
769+
770+ // Create a request with actual body content
771+ const req = createMockRequest ( {
772+ method : "POST" ,
773+ headers : {
774+ "content-type" : "application/json" ,
775+ "accept" : "application/json" ,
776+ } ,
777+ body : JSON . stringify ( requestBodyMessage ) ,
778+ } ) ;
779+
780+ const onMessageMock = jest . fn ( ) ;
781+ transport . onmessage = onMessageMock ;
782+
783+ // Pass the pre-parsed body directly
784+ await transport . handleRequest ( req , mockResponse , parsedBodyMessage ) ;
785+
786+ // Should use the parsed body instead of the request body
787+ expect ( onMessageMock ) . toHaveBeenCalledWith ( parsedBodyMessage ) ;
788+ expect ( onMessageMock ) . not . toHaveBeenCalledWith ( requestBodyMessage ) ;
789+ } ) ;
790+ } ) ;
677791} ) ;
0 commit comments