Skip to content

Conversation

@GreenStage
Copy link
Contributor

@GreenStage GreenStage commented Nov 26, 2025

Addresses #1172

Motivation and Context

Fix the errors in Cloudflare workers when multiple requests go inflight without reading response bodies, which are breaking my SDK client flows :(

How Has This Been Tested?

Unit tests and manually tested with Cloudflare workers

Types of changes

  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

@GreenStage GreenStage requested review from a team as code owners November 26, 2025 10:26
@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 26, 2025

Open in StackBlitz

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/sdk@1173

commit: 17754cb

};

const response = await (this._fetch ?? fetch)(this._url, init);
await response.body?.cancel();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feels weird cancelling the body before checking if response is okay. I'm not super clued up on this I just want to check line 620 will still work as expected.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question.

I think both .ok and .status should still be available on the response objects after cancellation and unaffected by it.

Tried it out with this script:

(async () => {
        // Fetch something that returns a body
        const response = await fetch('https://httpbin.org/json');

        console.log('Before cancel:');
        console.log('  status:', response.status);
        console.log('  ok:', response.ok);
        console.log('  headers.content-type:', response.headers.get('content-type'));

        // Cancel the body stream
        await response.body?.cancel();
        console.log('\nBody cancelled.\n');

        console.log('After cancel:');
        console.log('  status:', response.status);
        console.log('  ok:', response.ok);
        console.log('  headers.content-type:', response.headers.get('content-type'));

        // Try to read the body now - this should fail
        console.log('\nTrying to read body after cancel:');
        try {
          const text = await response.text();
          console.log('  text:', text.substring(0, 50));
        } catch (e) {
          console.log('  ERROR:', e.message);
        }
      })();

Which gave the following output:

Before cancel:
 status: 200
 ok: true
 headers.content-type: application/json

Body cancelled.

After cancel:
 status: 200
 ok: true
 headers.content-type: application/json

Trying to read body after cancel:
 ERROR: Body is unusable: Body has already been read

So this looks good to me.

We could put 2 cancels, one inside the 405 check and then one at the end of the function, but this seems fine.


// If the response is 202 Accepted, there's no body to process
if (response.status === 202) {
await response.body?.cancel();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome. This line I have been meaning to add for ages

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a script and not part of the sdk. can remove changes to this :)

Copy link
Contributor

@mattzcarey mattzcarey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One question but strong approval when answered

@mattzcarey
Copy link
Contributor

The context for this is that Cloudflare Durable Objects have a 6 simultaneous connection limit which is often reached by MCP clients. This should help us use the full amount with active requests.

felixweinberger and others added 3 commits November 26, 2025 13:28
Calling body.cancel() then response.text() would fail because the
stream is already closed. Use text() first to consume the body.
This script runs in Node.js at build time, not in Cloudflare Workers,
so it doesn't need the body consumption fix.
@felixweinberger
Copy link
Contributor

felixweinberger commented Nov 26, 2025

@mattzcarey pushed some small clean-ups to this branch, feel free to accept and merge if you're happy with it

@mattzcarey mattzcarey requested review from pcarleton and removed request for pcarleton November 26, 2025 15:48
@mattzcarey
Copy link
Contributor

I think I need a review from the auth team @pcarleton :)

@felixweinberger felixweinberger merged commit 466483f into modelcontextprotocol:main Nov 26, 2025
6 checks passed
felixweinberger added a commit to LucaButBoring/mcp-ts-sdk that referenced this pull request Nov 26, 2025
…#1173)

Co-authored-by: Eduardo Gomes <egomes@cloudflare.com>
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants