Skip to content

createMcpHonoApp: JSON body middleware has a size limit but no time limit (stalled body holds the request open) #3131

Open

Description

@timeleft--

Summary

createMcpHonoApp's JSON body middleware bounds how many bytes it reads (maxRequestBodySize) but not how long the body takes to arrive. A client that sends part of a body and then stalls keeps the request open with no response from the middleware. The only limit is the underlying HTTP server's own timeout (for Node, server.requestTimeout, 300 s by default).

Reproduction

@modelcontextprotocol/hono 2.0.2, @modelcontextprotocol/server 2.3.1, @hono/node-server 2.1.5, Node 24.

import { serve } from '@hono/node-server';
import { createMcpHonoApp } from '@modelcontextprotocol/hono';
import { request } from 'node:http';

const app = createMcpHonoApp({ host: '127.0.0.1' });
app.all('/mcp', (c) => c.text('unreachable while the body is stalled'));
const server = serve({ fetch: app.fetch, port: 0, hostname: '127.0.0.1' }, ({ port }) => {
  const req = request(`http://127.0.0.1:${port}/mcp`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'Content-Length': '100' },
  }, (res) => console.log('responded', res.statusCode));
  req.write('{'); // 1 of 100 declared bytes, then nothing
  setTimeout(() => { console.log('still open after 3s'); req.destroy(); server.close(); }, 3000);
});

Output: still open after 3s. The request is held until the client leaves or the server's timeout fires.

Expected

An option to bound body arrival time (for example a bodyTimeoutMs), answering 408 and closing the connection when it expires. The readRequestBody helper in @modelcontextprotocol/server could accept an AbortSignal for the same purpose, so adapters can supply their own deadline.

Impact

Low for a loopback development server. For a deployed server, each stalled request holds a connection and a handler slot until the server's timeout.

I am not asking for a particular design, only noting that the size limit has no time counterpart. We document the gap and rely on the server timeout meanwhile.

Activity

  1. added
    v2Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes
    v1Issues / PRs related to v1.x
    on Oct 30, 2026
  2. Ben-Home commented on Oct 30, 2026

    @Ben-Home
  3. Muzzy5150 commented on Oct 31, 2026

    @Muzzy5150

    I would like to work on this if the scope aligns. Proposed narrow main/v2 scope:

    • Add optional, cancellation-aware request-body reading while preserving existing callers and the 4 MiB default size limit.
    • Add an opt-in bodyTimeoutMs policy only at the Hono adapter boundary; keep the unconfigured default unchanged.
    • Map an expired configured deadline to HTTP 408, retain 400 for malformed completed JSON and 413 for oversized bodies, and ensure reader/listener/timer cleanup without dispatching partial requests.
    • Add deterministic unit and streaming integration coverage for pre-abort, pending-read abort, timeout, cleanup, isolation, and no later tool execution.

    I would not extend deadline policy to the other HTTP entry points without separate approval. I also noticed open PR #3028 touches requestBody.ts for the Firefox no-stream fallback; I will rebase/coordinate so this work does not overwrite it.

    Could a maintainer confirm this approach and assign the issue before I begin significant implementation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    v1Issues / PRs related to v1.xv2Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions