From 61b305b2634d58a70d0c845a4fb3f5f01b49732d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Theodor=20N=2E=20Eng=C3=B8y?= Date: Sun, 8 Feb 2026 00:01:06 +0100 Subject: [PATCH] express: add maxBodyBytes guard for JSON parsing --- .changeset/express-body-size-limit.md | 6 ++++++ packages/middleware/express/README.md | 2 ++ packages/middleware/express/src/express.ts | 14 ++++++++++++-- packages/middleware/express/test/express.test.ts | 13 +++++++++++++ packages/middleware/node/README.md | 2 ++ 5 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 .changeset/express-body-size-limit.md diff --git a/.changeset/express-body-size-limit.md b/.changeset/express-body-size-limit.md new file mode 100644 index 000000000..f1a12cc9a --- /dev/null +++ b/.changeset/express-body-size-limit.md @@ -0,0 +1,6 @@ +--- +'@modelcontextprotocol/express': patch +--- + +Add `maxBodyBytes` option to `createMcpExpressApp()` and enforce a default JSON request body size limit. + diff --git a/packages/middleware/express/README.md b/packages/middleware/express/README.md index 386141d14..8c642c74a 100644 --- a/packages/middleware/express/README.md +++ b/packages/middleware/express/README.md @@ -34,6 +34,8 @@ import { createMcpExpressApp } from '@modelcontextprotocol/express'; const app = createMcpExpressApp(); // default host is 127.0.0.1; protection enabled ``` +`createMcpExpressApp()` also installs `express.json()` with a default request body size limit (`maxBodyBytes`, default: `1_000_000` bytes). + ### Streamable HTTP endpoint (Express) ```ts diff --git a/packages/middleware/express/src/express.ts b/packages/middleware/express/src/express.ts index ff23cde85..fa6867fed 100644 --- a/packages/middleware/express/src/express.ts +++ b/packages/middleware/express/src/express.ts @@ -3,6 +3,8 @@ import express from 'express'; import { hostHeaderValidation, localhostHostValidation } from './middleware/hostHeaderValidation.js'; +const DEFAULT_MAX_BODY_BYTES = 1_000_000; // 1MB + /** * Options for creating an MCP Express application. */ @@ -22,6 +24,14 @@ export interface CreateMcpExpressAppOptions { * to restrict which hostnames are allowed. */ allowedHosts?: string[]; + + /** + * Maximum JSON request body size in bytes. + * Used by the built-in `express.json()` middleware for basic DoS resistance. + * + * @default 1_000_000 (1 MB) + */ + maxBodyBytes?: number; } /** @@ -48,10 +58,10 @@ export interface CreateMcpExpressAppOptions { * ``` */ export function createMcpExpressApp(options: CreateMcpExpressAppOptions = {}): Express { - const { host = '127.0.0.1', allowedHosts } = options; + const { host = '127.0.0.1', allowedHosts, maxBodyBytes = DEFAULT_MAX_BODY_BYTES } = options; const app = express(); - app.use(express.json()); + app.use(express.json({ limit: maxBodyBytes })); // If allowedHosts is explicitly provided, use that for validation if (allowedHosts) { diff --git a/packages/middleware/express/test/express.test.ts b/packages/middleware/express/test/express.test.ts index 64cf533bc..11d98db15 100644 --- a/packages/middleware/express/test/express.test.ts +++ b/packages/middleware/express/test/express.test.ts @@ -1,4 +1,5 @@ import type { NextFunction, Request, Response } from 'express'; +import request from 'supertest'; import { vi } from 'vitest'; import { createMcpExpressApp } from '../src/express.js'; @@ -178,5 +179,17 @@ describe('@modelcontextprotocol/express', () => { warn.mockRestore(); }); + + test('should enforce maxBodyBytes on the built-in JSON parser', async () => { + const app = createMcpExpressApp({ maxBodyBytes: 10 }); + app.post('/echo', (_req, res) => { + res.status(200).json({ ok: true }); + }); + + const body = JSON.stringify({ a: '0123456789' }); // > 10 bytes + const res = await request(app).post('/echo').set('Host', '127.0.0.1').set('Content-Type', 'application/json').send(body); + + expect(res.status).toBe(413); + }); }); }); diff --git a/packages/middleware/node/README.md b/packages/middleware/node/README.md index 678e1d452..391279926 100644 --- a/packages/middleware/node/README.md +++ b/packages/middleware/node/README.md @@ -28,6 +28,8 @@ import { McpServer } from '@modelcontextprotocol/server'; const server = new McpServer({ name: 'my-server', version: '1.0.0' }); const app = createMcpExpressApp(); +// You can tune the built-in JSON body size limit: +// const app = createMcpExpressApp({ maxBodyBytes: 200_000 }); app.post('/mcp', async (req, res) => { const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined });