Request Limits
Bounded bodies and strict content types, before anything is parsed.
Every JSON-RPC POST is bounded before its body is read: at most 4 MiB, Content-Type: application/json. Both checks run after origin and auth — cheap header checks first — and before any parsing.
defineMcpHandler({
name: "my-server",
version: "1.0.0",
limits: {
maxBodySize: 1024 * 1024, // bytes (default: 4 MiB); `false` for unbounded
contentType: ["application/json"], // default; `false` to accept any type
},
tools: [/* ... */],
});#Responses
| Request | Response |
|---|---|
Body larger than maxBodySize | 413 |
Missing Content-Type | 400 |
Malformed Content-Type | 422 |
Content-Type not in the accepted list | 415 |
The rejection messages are static — a bad Content-Type is never echoed back to the sender.
#Never Fully Buffered
An oversized body is rejected up front when Content-Length declares it, and otherwise aborted as it is read, so a lying-small Content-Length on a chunked upload is still caught mid-stream. The body is never accumulated in memory just to measure it.
Only the request body is bounded, so a subscriptions/listen POST — small body, long-lived response stream — is unaffected. GET and DELETE carry no body and skip both checks.
#Disabling
defineMcpHandler({
name: "my-server",
version: "1.0.0",
limits: false,
tools: [/* ... */],
});Or relax one half only:
limits: {
maxBodySize: false;
} // unbounded body, still application/json only
limits: {
contentType: false;
} // any content type, still 4 MiBWarning
limits: false makes the endpoint accept an unbounded body of any type. If something upstream already enforces limits, fine — otherwise this is the easiest denial-of-service vector an MCP endpoint has, because a single POST can consume memory before any of your code runs.
#Sizing the Limit
4 MiB is generous for JSON-RPC. Tighten it to what your largest legitimate request needs — a tool that accepts a pasted document may need a few hundred KiB; one that takes an id needs a few hundred bytes.
Remember that the limit applies to the whole body: a tools/call embedding base64 content pays roughly 4/3 of the raw byte size. If you need genuinely large inputs, take a URI or an id and fetch it server-side rather than raising the cap.