44
55package io .modelcontextprotocol .server .transport ;
66
7- import java .io .BufferedReader ;
87import java .io .IOException ;
98import java .io .PrintWriter ;
109import java .time .Duration ;
7675@ WebServlet (asyncSupported = true )
7776public class HttpServletSseServerTransportProvider extends HttpServlet implements McpServerTransportProvider {
7877
78+ /**
79+ * Default maximum size of a single request body: 16 MiB (16 * 1024 * 1024 bytes).
80+ */
81+ private static final int DEFAULT_REQUEST_MAX_SIZE = 16 * 1024 * 1024 ;
82+
7983 /**
8084 * Logger for this class
8185 */
@@ -111,6 +115,11 @@ public class HttpServletSseServerTransportProvider extends HttpServlet implement
111115 */
112116 private final McpJsonMapper jsonMapper ;
113117
118+ /**
119+ * Maximum size, in bytes, of a single request body accepted by this transport.
120+ */
121+ private final int requestMaxSize ;
122+
114123 /**
115124 * Base URL for the server transport
116125 */
@@ -166,24 +175,28 @@ public class HttpServletSseServerTransportProvider extends HttpServlet implement
166175 * keep-alive functionality
167176 * @param contextExtractor The extractor for transport context from the request.
168177 * @param securityValidator The security validator for validating HTTP requests.
178+ * @param requestMaxSize The maximum size, in bytes, of a single request body. Must be
179+ * positive.
169180 */
170181 private HttpServletSseServerTransportProvider (McpJsonMapper jsonMapper , String baseUrl , String messageEndpoint ,
171182 String sseEndpoint , Duration keepAliveInterval ,
172183 McpTransportContextExtractor <HttpServletRequest > contextExtractor ,
173- ServerTransportSecurityValidator securityValidator ) {
184+ ServerTransportSecurityValidator securityValidator , int requestMaxSize ) {
174185
175186 Assert .notNull (jsonMapper , "JsonMapper must not be null" );
176187 Assert .notNull (messageEndpoint , "messageEndpoint must not be null" );
177188 Assert .notNull (sseEndpoint , "sseEndpoint must not be null" );
178189 Assert .notNull (contextExtractor , "Context extractor must not be null" );
179190 Assert .notNull (securityValidator , "Security validator must not be null" );
191+ Assert .isTrue (requestMaxSize > 0 , "requestMaxSize must be positive" );
180192
181193 this .jsonMapper = jsonMapper ;
182194 this .baseUrl = baseUrl ;
183195 this .messageEndpoint = messageEndpoint ;
184196 this .sseEndpoint = sseEndpoint ;
185197 this .contextExtractor = contextExtractor ;
186198 this .securityValidator = securityValidator ;
199+ this .requestMaxSize = requestMaxSize ;
187200
188201 if (keepAliveInterval != null ) {
189202
@@ -346,6 +359,11 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
346359 return ;
347360 }
348361
362+ if (request .getContentLengthLong () > this .requestMaxSize ) {
363+ response .sendError (HttpServletResponse .SC_REQUEST_ENTITY_TOO_LARGE );
364+ return ;
365+ }
366+
349367 String requestURI = request .getRequestURI ();
350368 if (!requestURI .endsWith (messageEndpoint )) {
351369 response .sendError (HttpServletResponse .SC_NOT_FOUND );
@@ -392,22 +410,20 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
392410 }
393411
394412 try {
395- BufferedReader reader = request .getReader ();
396- StringBuilder body = new StringBuilder ();
397- String line ;
398- while ((line = reader .readLine ()) != null ) {
399- body .append (line );
400- }
413+ String body = HttpServletRequestUtils .readBody (request , this .requestMaxSize );
401414
402415 final McpTransportContext transportContext = this .contextExtractor .extract (request );
403- McpSchema .JSONRPCMessage message = McpSchema .deserializeJsonRpcMessage (jsonMapper , body . toString () );
416+ McpSchema .JSONRPCMessage message = McpSchema .deserializeJsonRpcMessage (jsonMapper , body );
404417
405418 // Process the message through the session's handle method
406419 // Block for Servlet compatibility
407420 session .handle (message ).contextWrite (ctx -> ctx .put (McpTransportContext .KEY , transportContext )).block ();
408421
409422 response .setStatus (HttpServletResponse .SC_OK );
410423 }
424+ catch (MaxSizeExceededException e ) {
425+ response .sendError (HttpServletResponse .SC_REQUEST_ENTITY_TOO_LARGE );
426+ }
411427 catch (Exception e ) {
412428 logger .error ("Error processing message: {}" , e .getMessage ());
413429 try {
@@ -605,6 +621,8 @@ public static class Builder {
605621
606622 private ServerTransportSecurityValidator securityValidator = ServerTransportSecurityValidator .NOOP ;
607623
624+ private int requestMaxSize = DEFAULT_REQUEST_MAX_SIZE ;
625+
608626 /**
609627 * Sets the JsonMapper implementation to use for serialization/deserialization. If
610628 * not specified, a JacksonJsonMapper will be created from the configured
@@ -691,6 +709,19 @@ public Builder securityValidator(ServerTransportSecurityValidator securityValida
691709 return this ;
692710 }
693711
712+ /**
713+ * Sets the maximum size, in bytes, of a single request body accepted by this
714+ * transport. Requests whose body exceeds this size are rejected with a 413
715+ * (Payload Too Large) response. Defaults to 16 MiB if not set.
716+ * @param requestMaxSize The maximum request body size, in bytes. Must be
717+ * positive.
718+ * @return This builder instance
719+ */
720+ public Builder maxRequestSize (int requestMaxSize ) {
721+ this .requestMaxSize = requestMaxSize ;
722+ return this ;
723+ }
724+
694725 /**
695726 * Builds a new instance of HttpServletSseServerTransportProvider with the
696727 * configured settings.
@@ -703,7 +734,7 @@ public HttpServletSseServerTransportProvider build() {
703734 }
704735 return new HttpServletSseServerTransportProvider (
705736 jsonMapper == null ? McpJsonDefaults .getMapper () : jsonMapper , baseUrl , messageEndpoint ,
706- sseEndpoint , keepAliveInterval , contextExtractor , securityValidator );
737+ sseEndpoint , keepAliveInterval , contextExtractor , securityValidator , requestMaxSize );
707738 }
708739
709740 }
0 commit comments