44
55package io .modelcontextprotocol .server .transport ;
66
7- import java .io .BufferedReader ;
87import java .io .IOException ;
98import java .io .PrintWriter ;
109import java .time .Duration ;
7069@ WebServlet (asyncSupported = true )
7170public class HttpServletSseServerTransportProvider extends HttpServlet implements McpServerTransportProvider {
7271
72+ /**
73+ * Default maximum size of a single request body: 16 MiB (16 * 1024 * 1024 bytes).
74+ */
75+ private static final int DEFAULT_REQUEST_MAX_SIZE = 16 * 1024 * 1024 ;
76+
7377 /**
7478 * Logger for this class
7579 */
@@ -105,6 +109,11 @@ public class HttpServletSseServerTransportProvider extends HttpServlet implement
105109 */
106110 private final McpJsonMapper jsonMapper ;
107111
112+ /**
113+ * Maximum size, in bytes, of a single request body accepted by this transport.
114+ */
115+ private final int requestMaxSize ;
116+
108117 /**
109118 * Base URL for the server transport
110119 */
@@ -160,24 +169,28 @@ public class HttpServletSseServerTransportProvider extends HttpServlet implement
160169 * keep-alive functionality
161170 * @param contextExtractor The extractor for transport context from the request.
162171 * @param securityValidator The security validator for validating HTTP requests.
172+ * @param requestMaxSize The maximum size, in bytes, of a single request body. Must be
173+ * positive.
163174 */
164175 private HttpServletSseServerTransportProvider (McpJsonMapper jsonMapper , String baseUrl , String messageEndpoint ,
165176 String sseEndpoint , Duration keepAliveInterval ,
166177 McpTransportContextExtractor <HttpServletRequest > contextExtractor ,
167- ServerTransportSecurityValidator securityValidator ) {
178+ ServerTransportSecurityValidator securityValidator , int requestMaxSize ) {
168179
169180 Assert .notNull (jsonMapper , "JsonMapper must not be null" );
170181 Assert .notNull (messageEndpoint , "messageEndpoint must not be null" );
171182 Assert .notNull (sseEndpoint , "sseEndpoint must not be null" );
172183 Assert .notNull (contextExtractor , "Context extractor must not be null" );
173184 Assert .notNull (securityValidator , "Security validator must not be null" );
185+ Assert .isTrue (requestMaxSize > 0 , "requestMaxSize must be positive" );
174186
175187 this .jsonMapper = jsonMapper ;
176188 this .baseUrl = baseUrl ;
177189 this .messageEndpoint = messageEndpoint ;
178190 this .sseEndpoint = sseEndpoint ;
179191 this .contextExtractor = contextExtractor ;
180192 this .securityValidator = securityValidator ;
193+ this .requestMaxSize = requestMaxSize ;
181194
182195 if (keepAliveInterval != null ) {
183196
@@ -321,6 +334,11 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
321334 return ;
322335 }
323336
337+ if (request .getContentLengthLong () > this .requestMaxSize ) {
338+ response .sendError (HttpServletResponse .SC_REQUEST_ENTITY_TOO_LARGE );
339+ return ;
340+ }
341+
324342 String requestURI = request .getRequestURI ();
325343 if (!requestURI .endsWith (messageEndpoint )) {
326344 response .sendError (HttpServletResponse .SC_NOT_FOUND );
@@ -363,22 +381,20 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
363381 }
364382
365383 try {
366- BufferedReader reader = request .getReader ();
367- StringBuilder body = new StringBuilder ();
368- String line ;
369- while ((line = reader .readLine ()) != null ) {
370- body .append (line );
371- }
384+ String body = HttpServletRequestUtils .readBody (request , this .requestMaxSize );
372385
373386 final McpTransportContext transportContext = this .contextExtractor .extract (request );
374- McpSchema .JSONRPCMessage message = McpSchema .deserializeJsonRpcMessage (jsonMapper , body . toString () );
387+ McpSchema .JSONRPCMessage message = McpSchema .deserializeJsonRpcMessage (jsonMapper , body );
375388
376389 // Process the message through the session's handle method
377390 // Block for Servlet compatibility
378391 session .handle (message ).contextWrite (ctx -> ctx .put (McpTransportContext .KEY , transportContext )).block ();
379392
380393 response .setStatus (HttpServletResponse .SC_OK );
381394 }
395+ catch (MaxSizeExceededException e ) {
396+ response .sendError (HttpServletResponse .SC_REQUEST_ENTITY_TOO_LARGE );
397+ }
382398 catch (Exception e ) {
383399 logger .error ("Error processing message: {}" , e .getMessage ());
384400 try {
@@ -574,6 +590,8 @@ public static class Builder {
574590
575591 private ServerTransportSecurityValidator securityValidator = ServerTransportSecurityValidator .NOOP ;
576592
593+ private int requestMaxSize = DEFAULT_REQUEST_MAX_SIZE ;
594+
577595 /**
578596 * Sets the JsonMapper implementation to use for serialization/deserialization. If
579597 * not specified, a JacksonJsonMapper will be created from the configured
@@ -660,6 +678,19 @@ public Builder securityValidator(ServerTransportSecurityValidator securityValida
660678 return this ;
661679 }
662680
681+ /**
682+ * Sets the maximum size, in bytes, of a single request body accepted by this
683+ * transport. Requests whose body exceeds this size are rejected with a 413
684+ * (Payload Too Large) response. Defaults to 16 MiB if not set.
685+ * @param requestMaxSize The maximum request body size, in bytes. Must be
686+ * positive.
687+ * @return This builder instance
688+ */
689+ public Builder maxRequestSize (int requestMaxSize ) {
690+ this .requestMaxSize = requestMaxSize ;
691+ return this ;
692+ }
693+
663694 /**
664695 * Builds a new instance of HttpServletSseServerTransportProvider with the
665696 * configured settings.
@@ -672,7 +703,7 @@ public HttpServletSseServerTransportProvider build() {
672703 }
673704 return new HttpServletSseServerTransportProvider (
674705 jsonMapper == null ? McpJsonDefaults .getMapper () : jsonMapper , baseUrl , messageEndpoint ,
675- sseEndpoint , keepAliveInterval , contextExtractor , securityValidator );
706+ sseEndpoint , keepAliveInterval , contextExtractor , securityValidator , requestMaxSize );
676707 }
677708
678709 }
0 commit comments