fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone - #2330

Merged
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes
Jun 22, 2026
Merged

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone#2330
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes

Conversation

@felixweinberger

Copy link
Copy Markdown
Contributor

Three small server/client correctness fixes for the 2026-07-28 per-request hosting path. Stacked on #2328.

Motivation and Context

  • ctx.mcpReq.log on per-request hosting — handler-emitted log notifications were sent via the session-scoped sendLoggingMessage path, which has no delivery channel under createMcpHandler's per-request model. Server.buildContext now wires log through the request context's ctx.mcpReq.notify (stamping relatedRequestId) so the notification rides the in-flight exchange the same way progress/notify already do. Level filtering branches by era: 2026-era reads the per-request _meta['io.modelcontextprotocol/logLevel'] envelope key (the modern equivalent now that logging/setLevel is removed); legacy-era keeps the existing _loggingLevels map lookup unchanged. The public sendLoggingMessage API is untouched.
  • 2026-era HTTP cancel = stream close — the spec wording at the 2026-07-28 revision is "no notifications/cancelled message is required or expected" when the transport carries a per-request stream. Protocol._requestWithSchemaViaCodec now threads a per-request AbortController and aborts it on cancel/timeout when codec.era === MODERN_WIRE_REVISION && transport.hasPerRequestStream; otherwise the legacy notifications/cancelled POST is byte-identical. New optional Transport.hasPerRequestStream flag, set true on StreamableHTTPClientTransport.
  • createMcpHandler clone nitclassifyEntryRequest grows a needsForward flag (default true) gating the body-preserving request.clone(); the isLegacyRequest path passes false so the unused forwarding tee is skipped.

How Has This Been Tested?

  • core unit tests cover modern×per-request-stream / modern×stdio / legacy×per-request-stream / modern timeout for the cancel path; client test pins hasPerRequestStream
  • e2e: protocol:cancel:http-stream-close supersedes the abort-signal / sends-cancellation rows (manifest-only); mcpserver:context:log-from-handler re-admitted to entryStateless + entryModern
  • Full gates at this tip: typecheck, lint, core (1207), client (535), server (320), integration (348), e2e (2535p/205xf)

Breaking Changes

None. New optional Transport.hasPerRequestStream flag; existing transports without it keep legacy cancel semantics.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

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

Additional context

Changesets + migration.md / migration-SKILL.md entries included for the cancel and log changes.

@felixweinberger
felixweinberger requested a review from a team as a code ownerJune 19, 2026 14:18
@changeset-bot

changeset-botBot commented Jun 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cf76ed1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@modelcontextprotocol/coreMinor
@modelcontextprotocol/clientMinor
@modelcontextprotocol/serverPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-newBot commented Jun 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

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

@modelcontextprotocol/codemod

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

@modelcontextprotocol/server

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

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server-legacy@2330

@modelcontextprotocol/express

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

@modelcontextprotocol/fastify

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

@modelcontextprotocol/hono

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

@modelcontextprotocol/node

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

commit: cf76ed1

@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch from 1e780e2 to 6e8a521CompareJune 19, 2026 14:24
Comment threadpackages/client/src/client/streamableHttp.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from 761bd17 to e230a8eCompareJune 19, 2026 14:37
Comment threadpackages/server/src/server/server.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch 9 times, most recently from 996359b to 55bc820CompareJune 22, 2026 15:22
Base automatically changed from fweinberger/sep2243-std to v2-2026-07-28June 22, 2026 15:28
… path
classifyEntryRequest grows a needsForward flag (default true) that gates the
body-preserving request.clone() it tees off for the legacy leg. isLegacyRequest
passes false: it already classifies a clone of the caller's request and never
reads forwardRequest, so the second clone buffered one extra body copy per
routed POST for nothing. The entry handler call site is unchanged (default
true) and the body-readability contract test stays.
Follow-up from the #2316 review (r3435103544); closes#36.
…tion timing-sensitive on CI
The 75% replay check depends on the request-related event-store path; on this stack the request-related routing change lands before the store-first fix, so the post-disconnect log is not reliably replayed on intermediate commits. Re-enable once the store-first fix is in the base.
…est stream, not POSTing notifications/cancelled
The 2026-07-28 spec (basic/patterns/cancellation §Transport-Specific) makes
closing the per-request SSE response stream the cancellation signal on
Streamable HTTP — no notifications/cancelled message is required or expected.
The general client request-abort path now routes accordingly: on a 2026-era
connection over a transport that opens a per-request stream, aborting the
caller signal or hitting the timeout aborts that request's
TransportSendOptions.requestSignal (the same per-request abort proven by the
listen driver) instead of POSTing notifications/cancelled. Legacy-era
connections, and stdio/in-memory at any era, keep the existing
notifications/cancelled POST path unchanged.
Adds the optional Transport.hasPerRequestStream capability flag (set on
StreamableHTTPClientTransport) so the protocol layer can route the
per-transport cancel path without inspecting transport type. e2e splits the
2025 cancel/timeout requirements via supersedes/supersededBy and adds the
2026 stream-close requirement on the entryModern arm.
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from e230a8e to ee14814CompareJune 22, 2026 16:53
Comment threaddocs/migration.md
…ing delivers it; 2026-era requests filter by _meta.logLevel
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from ee14814 to cf76ed1CompareJune 22, 2026 20:12
@felixweinberger
felixweinberger merged commit 5f18fcd into v2-2026-07-28Jun 22, 2026
20 checks passed
@felixweinberger
felixweinberger deleted the fweinberger/on-small-fixes branch June 22, 2026 21:26
felixweinberger added a commit that referenced this pull request Jun 24, 2026
Sign up for freeto 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.

1 participant

@felixweinberger
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone - #2330

Merged
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes
Jun 22, 2026
Merged

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone#2330
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes

Conversation

@felixweinberger

Copy link
Copy Markdown
Contributor

Three small server/client correctness fixes for the 2026-07-28 per-request hosting path. Stacked on #2328.

Motivation and Context

  • ctx.mcpReq.log on per-request hosting — handler-emitted log notifications were sent via the session-scoped sendLoggingMessage path, which has no delivery channel under createMcpHandler's per-request model. Server.buildContext now wires log through the request context's ctx.mcpReq.notify (stamping relatedRequestId) so the notification rides the in-flight exchange the same way progress/notify already do. Level filtering branches by era: 2026-era reads the per-request _meta['io.modelcontextprotocol/logLevel'] envelope key (the modern equivalent now that logging/setLevel is removed); legacy-era keeps the existing _loggingLevels map lookup unchanged. The public sendLoggingMessage API is untouched.
  • 2026-era HTTP cancel = stream close — the spec wording at the 2026-07-28 revision is "no notifications/cancelled message is required or expected" when the transport carries a per-request stream. Protocol._requestWithSchemaViaCodec now threads a per-request AbortController and aborts it on cancel/timeout when codec.era === MODERN_WIRE_REVISION && transport.hasPerRequestStream; otherwise the legacy notifications/cancelled POST is byte-identical. New optional Transport.hasPerRequestStream flag, set true on StreamableHTTPClientTransport.
  • createMcpHandler clone nitclassifyEntryRequest grows a needsForward flag (default true) gating the body-preserving request.clone(); the isLegacyRequest path passes false so the unused forwarding tee is skipped.

How Has This Been Tested?

  • core unit tests cover modern×per-request-stream / modern×stdio / legacy×per-request-stream / modern timeout for the cancel path; client test pins hasPerRequestStream
  • e2e: protocol:cancel:http-stream-close supersedes the abort-signal / sends-cancellation rows (manifest-only); mcpserver:context:log-from-handler re-admitted to entryStateless + entryModern
  • Full gates at this tip: typecheck, lint, core (1207), client (535), server (320), integration (348), e2e (2535p/205xf)

Breaking Changes

None. New optional Transport.hasPerRequestStream flag; existing transports without it keep legacy cancel semantics.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

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

Additional context

Changesets + migration.md / migration-SKILL.md entries included for the cancel and log changes.

@felixweinberger
felixweinberger requested a review from a team as a code ownerJune 19, 2026 14:18
@changeset-bot

changeset-botBot commented Jun 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cf76ed1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@modelcontextprotocol/coreMinor
@modelcontextprotocol/clientMinor
@modelcontextprotocol/serverPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-newBot commented Jun 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

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

@modelcontextprotocol/codemod

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

@modelcontextprotocol/server

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

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server-legacy@2330

@modelcontextprotocol/express

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

@modelcontextprotocol/fastify

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

@modelcontextprotocol/hono

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

@modelcontextprotocol/node

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

commit: cf76ed1

@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch from 1e780e2 to 6e8a521CompareJune 19, 2026 14:24
Comment threadpackages/client/src/client/streamableHttp.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from 761bd17 to e230a8eCompareJune 19, 2026 14:37
Comment threadpackages/server/src/server/server.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch 9 times, most recently from 996359b to 55bc820CompareJune 22, 2026 15:22
Base automatically changed from fweinberger/sep2243-std to v2-2026-07-28June 22, 2026 15:28
… path
classifyEntryRequest grows a needsForward flag (default true) that gates the
body-preserving request.clone() it tees off for the legacy leg. isLegacyRequest
passes false: it already classifies a clone of the caller's request and never
reads forwardRequest, so the second clone buffered one extra body copy per
routed POST for nothing. The entry handler call site is unchanged (default
true) and the body-readability contract test stays.
Follow-up from the #2316 review (r3435103544); closes#36.
…tion timing-sensitive on CI
The 75% replay check depends on the request-related event-store path; on this stack the request-related routing change lands before the store-first fix, so the post-disconnect log is not reliably replayed on intermediate commits. Re-enable once the store-first fix is in the base.
…est stream, not POSTing notifications/cancelled
The 2026-07-28 spec (basic/patterns/cancellation §Transport-Specific) makes
closing the per-request SSE response stream the cancellation signal on
Streamable HTTP — no notifications/cancelled message is required or expected.
The general client request-abort path now routes accordingly: on a 2026-era
connection over a transport that opens a per-request stream, aborting the
caller signal or hitting the timeout aborts that request's
TransportSendOptions.requestSignal (the same per-request abort proven by the
listen driver) instead of POSTing notifications/cancelled. Legacy-era
connections, and stdio/in-memory at any era, keep the existing
notifications/cancelled POST path unchanged.
Adds the optional Transport.hasPerRequestStream capability flag (set on
StreamableHTTPClientTransport) so the protocol layer can route the
per-transport cancel path without inspecting transport type. e2e splits the
2025 cancel/timeout requirements via supersedes/supersededBy and adds the
2026 stream-close requirement on the entryModern arm.
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from e230a8e to ee14814CompareJune 22, 2026 16:53
Comment threaddocs/migration.md
…ing delivers it; 2026-era requests filter by _meta.logLevel
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from ee14814 to cf76ed1CompareJune 22, 2026 20:12
@felixweinberger
felixweinberger merged commit 5f18fcd into v2-2026-07-28Jun 22, 2026
20 checks passed
@felixweinberger
felixweinberger deleted the fweinberger/on-small-fixes branch June 22, 2026 21:26
felixweinberger added a commit that referenced this pull request Jun 24, 2026
Sign up for freeto 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.

1 participant

@felixweinberger
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone - #2330

Merged
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes
Jun 22, 2026
Merged

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone#2330
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes

Conversation

@felixweinberger

Copy link
Copy Markdown
Contributor

Three small server/client correctness fixes for the 2026-07-28 per-request hosting path. Stacked on #2328.

Motivation and Context

  • ctx.mcpReq.log on per-request hosting — handler-emitted log notifications were sent via the session-scoped sendLoggingMessage path, which has no delivery channel under createMcpHandler's per-request model. Server.buildContext now wires log through the request context's ctx.mcpReq.notify (stamping relatedRequestId) so the notification rides the in-flight exchange the same way progress/notify already do. Level filtering branches by era: 2026-era reads the per-request _meta['io.modelcontextprotocol/logLevel'] envelope key (the modern equivalent now that logging/setLevel is removed); legacy-era keeps the existing _loggingLevels map lookup unchanged. The public sendLoggingMessage API is untouched.
  • 2026-era HTTP cancel = stream close — the spec wording at the 2026-07-28 revision is "no notifications/cancelled message is required or expected" when the transport carries a per-request stream. Protocol._requestWithSchemaViaCodec now threads a per-request AbortController and aborts it on cancel/timeout when codec.era === MODERN_WIRE_REVISION && transport.hasPerRequestStream; otherwise the legacy notifications/cancelled POST is byte-identical. New optional Transport.hasPerRequestStream flag, set true on StreamableHTTPClientTransport.
  • createMcpHandler clone nitclassifyEntryRequest grows a needsForward flag (default true) gating the body-preserving request.clone(); the isLegacyRequest path passes false so the unused forwarding tee is skipped.

How Has This Been Tested?

  • core unit tests cover modern×per-request-stream / modern×stdio / legacy×per-request-stream / modern timeout for the cancel path; client test pins hasPerRequestStream
  • e2e: protocol:cancel:http-stream-close supersedes the abort-signal / sends-cancellation rows (manifest-only); mcpserver:context:log-from-handler re-admitted to entryStateless + entryModern
  • Full gates at this tip: typecheck, lint, core (1207), client (535), server (320), integration (348), e2e (2535p/205xf)

Breaking Changes

None. New optional Transport.hasPerRequestStream flag; existing transports without it keep legacy cancel semantics.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

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

Additional context

Changesets + migration.md / migration-SKILL.md entries included for the cancel and log changes.

@felixweinberger
felixweinberger requested a review from a team as a code ownerJune 19, 2026 14:18
@changeset-bot

changeset-botBot commented Jun 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cf76ed1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@modelcontextprotocol/coreMinor
@modelcontextprotocol/clientMinor
@modelcontextprotocol/serverPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-newBot commented Jun 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

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

@modelcontextprotocol/codemod

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

@modelcontextprotocol/server

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

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server-legacy@2330

@modelcontextprotocol/express

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

@modelcontextprotocol/fastify

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

@modelcontextprotocol/hono

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

@modelcontextprotocol/node

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

commit: cf76ed1

@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch from 1e780e2 to 6e8a521CompareJune 19, 2026 14:24
Comment threadpackages/client/src/client/streamableHttp.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from 761bd17 to e230a8eCompareJune 19, 2026 14:37
Comment threadpackages/server/src/server/server.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch 9 times, most recently from 996359b to 55bc820CompareJune 22, 2026 15:22
Base automatically changed from fweinberger/sep2243-std to v2-2026-07-28June 22, 2026 15:28
… path
classifyEntryRequest grows a needsForward flag (default true) that gates the
body-preserving request.clone() it tees off for the legacy leg. isLegacyRequest
passes false: it already classifies a clone of the caller's request and never
reads forwardRequest, so the second clone buffered one extra body copy per
routed POST for nothing. The entry handler call site is unchanged (default
true) and the body-readability contract test stays.
Follow-up from the #2316 review (r3435103544); closes#36.
…tion timing-sensitive on CI
The 75% replay check depends on the request-related event-store path; on this stack the request-related routing change lands before the store-first fix, so the post-disconnect log is not reliably replayed on intermediate commits. Re-enable once the store-first fix is in the base.
…est stream, not POSTing notifications/cancelled
The 2026-07-28 spec (basic/patterns/cancellation §Transport-Specific) makes
closing the per-request SSE response stream the cancellation signal on
Streamable HTTP — no notifications/cancelled message is required or expected.
The general client request-abort path now routes accordingly: on a 2026-era
connection over a transport that opens a per-request stream, aborting the
caller signal or hitting the timeout aborts that request's
TransportSendOptions.requestSignal (the same per-request abort proven by the
listen driver) instead of POSTing notifications/cancelled. Legacy-era
connections, and stdio/in-memory at any era, keep the existing
notifications/cancelled POST path unchanged.
Adds the optional Transport.hasPerRequestStream capability flag (set on
StreamableHTTPClientTransport) so the protocol layer can route the
per-transport cancel path without inspecting transport type. e2e splits the
2025 cancel/timeout requirements via supersedes/supersededBy and adds the
2026 stream-close requirement on the entryModern arm.
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from e230a8e to ee14814CompareJune 22, 2026 16:53
Comment threaddocs/migration.md
…ing delivers it; 2026-era requests filter by _meta.logLevel
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from ee14814 to cf76ed1CompareJune 22, 2026 20:12
@felixweinberger
felixweinberger merged commit 5f18fcd into v2-2026-07-28Jun 22, 2026
20 checks passed
@felixweinberger
felixweinberger deleted the fweinberger/on-small-fixes branch June 22, 2026 21:26
felixweinberger added a commit that referenced this pull request Jun 24, 2026
Sign up for freeto 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.

1 participant

@felixweinberger
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone - #2330

Merged
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes
Jun 22, 2026
Merged

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone#2330
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes

Conversation

@felixweinberger

Copy link
Copy Markdown
Contributor

Three small server/client correctness fixes for the 2026-07-28 per-request hosting path. Stacked on #2328.

Motivation and Context

  • ctx.mcpReq.log on per-request hosting — handler-emitted log notifications were sent via the session-scoped sendLoggingMessage path, which has no delivery channel under createMcpHandler's per-request model. Server.buildContext now wires log through the request context's ctx.mcpReq.notify (stamping relatedRequestId) so the notification rides the in-flight exchange the same way progress/notify already do. Level filtering branches by era: 2026-era reads the per-request _meta['io.modelcontextprotocol/logLevel'] envelope key (the modern equivalent now that logging/setLevel is removed); legacy-era keeps the existing _loggingLevels map lookup unchanged. The public sendLoggingMessage API is untouched.
  • 2026-era HTTP cancel = stream close — the spec wording at the 2026-07-28 revision is "no notifications/cancelled message is required or expected" when the transport carries a per-request stream. Protocol._requestWithSchemaViaCodec now threads a per-request AbortController and aborts it on cancel/timeout when codec.era === MODERN_WIRE_REVISION && transport.hasPerRequestStream; otherwise the legacy notifications/cancelled POST is byte-identical. New optional Transport.hasPerRequestStream flag, set true on StreamableHTTPClientTransport.
  • createMcpHandler clone nitclassifyEntryRequest grows a needsForward flag (default true) gating the body-preserving request.clone(); the isLegacyRequest path passes false so the unused forwarding tee is skipped.

How Has This Been Tested?

  • core unit tests cover modern×per-request-stream / modern×stdio / legacy×per-request-stream / modern timeout for the cancel path; client test pins hasPerRequestStream
  • e2e: protocol:cancel:http-stream-close supersedes the abort-signal / sends-cancellation rows (manifest-only); mcpserver:context:log-from-handler re-admitted to entryStateless + entryModern
  • Full gates at this tip: typecheck, lint, core (1207), client (535), server (320), integration (348), e2e (2535p/205xf)

Breaking Changes

None. New optional Transport.hasPerRequestStream flag; existing transports without it keep legacy cancel semantics.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

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

Additional context

Changesets + migration.md / migration-SKILL.md entries included for the cancel and log changes.

@felixweinberger
felixweinberger requested a review from a team as a code ownerJune 19, 2026 14:18
@changeset-bot

changeset-botBot commented Jun 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cf76ed1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@modelcontextprotocol/coreMinor
@modelcontextprotocol/clientMinor
@modelcontextprotocol/serverPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-newBot commented Jun 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

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

@modelcontextprotocol/codemod

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

@modelcontextprotocol/server

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

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server-legacy@2330

@modelcontextprotocol/express

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

@modelcontextprotocol/fastify

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

@modelcontextprotocol/hono

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

@modelcontextprotocol/node

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

commit: cf76ed1

@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch from 1e780e2 to 6e8a521CompareJune 19, 2026 14:24
Comment threadpackages/client/src/client/streamableHttp.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from 761bd17 to e230a8eCompareJune 19, 2026 14:37
Comment threadpackages/server/src/server/server.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch 9 times, most recently from 996359b to 55bc820CompareJune 22, 2026 15:22
Base automatically changed from fweinberger/sep2243-std to v2-2026-07-28June 22, 2026 15:28
… path
classifyEntryRequest grows a needsForward flag (default true) that gates the
body-preserving request.clone() it tees off for the legacy leg. isLegacyRequest
passes false: it already classifies a clone of the caller's request and never
reads forwardRequest, so the second clone buffered one extra body copy per
routed POST for nothing. The entry handler call site is unchanged (default
true) and the body-readability contract test stays.
Follow-up from the #2316 review (r3435103544); closes#36.
…tion timing-sensitive on CI
The 75% replay check depends on the request-related event-store path; on this stack the request-related routing change lands before the store-first fix, so the post-disconnect log is not reliably replayed on intermediate commits. Re-enable once the store-first fix is in the base.
…est stream, not POSTing notifications/cancelled
The 2026-07-28 spec (basic/patterns/cancellation §Transport-Specific) makes
closing the per-request SSE response stream the cancellation signal on
Streamable HTTP — no notifications/cancelled message is required or expected.
The general client request-abort path now routes accordingly: on a 2026-era
connection over a transport that opens a per-request stream, aborting the
caller signal or hitting the timeout aborts that request's
TransportSendOptions.requestSignal (the same per-request abort proven by the
listen driver) instead of POSTing notifications/cancelled. Legacy-era
connections, and stdio/in-memory at any era, keep the existing
notifications/cancelled POST path unchanged.
Adds the optional Transport.hasPerRequestStream capability flag (set on
StreamableHTTPClientTransport) so the protocol layer can route the
per-transport cancel path without inspecting transport type. e2e splits the
2025 cancel/timeout requirements via supersedes/supersededBy and adds the
2026 stream-close requirement on the entryModern arm.
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from e230a8e to ee14814CompareJune 22, 2026 16:53
Comment threaddocs/migration.md
…ing delivers it; 2026-era requests filter by _meta.logLevel
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from ee14814 to cf76ed1CompareJune 22, 2026 20:12
@felixweinberger
felixweinberger merged commit 5f18fcd into v2-2026-07-28Jun 22, 2026
20 checks passed
@felixweinberger
felixweinberger deleted the fweinberger/on-small-fixes branch June 22, 2026 21:26
felixweinberger added a commit that referenced this pull request Jun 24, 2026
Sign up for freeto 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.

1 participant

@felixweinberger
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone - #2330

Merged
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes
Jun 22, 2026
Merged

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone#2330
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes

Conversation

@felixweinberger

Copy link
Copy Markdown
Contributor

Three small server/client correctness fixes for the 2026-07-28 per-request hosting path. Stacked on #2328.

Motivation and Context

  • ctx.mcpReq.log on per-request hosting — handler-emitted log notifications were sent via the session-scoped sendLoggingMessage path, which has no delivery channel under createMcpHandler's per-request model. Server.buildContext now wires log through the request context's ctx.mcpReq.notify (stamping relatedRequestId) so the notification rides the in-flight exchange the same way progress/notify already do. Level filtering branches by era: 2026-era reads the per-request _meta['io.modelcontextprotocol/logLevel'] envelope key (the modern equivalent now that logging/setLevel is removed); legacy-era keeps the existing _loggingLevels map lookup unchanged. The public sendLoggingMessage API is untouched.
  • 2026-era HTTP cancel = stream close — the spec wording at the 2026-07-28 revision is "no notifications/cancelled message is required or expected" when the transport carries a per-request stream. Protocol._requestWithSchemaViaCodec now threads a per-request AbortController and aborts it on cancel/timeout when codec.era === MODERN_WIRE_REVISION && transport.hasPerRequestStream; otherwise the legacy notifications/cancelled POST is byte-identical. New optional Transport.hasPerRequestStream flag, set true on StreamableHTTPClientTransport.
  • createMcpHandler clone nitclassifyEntryRequest grows a needsForward flag (default true) gating the body-preserving request.clone(); the isLegacyRequest path passes false so the unused forwarding tee is skipped.

How Has This Been Tested?

  • core unit tests cover modern×per-request-stream / modern×stdio / legacy×per-request-stream / modern timeout for the cancel path; client test pins hasPerRequestStream
  • e2e: protocol:cancel:http-stream-close supersedes the abort-signal / sends-cancellation rows (manifest-only); mcpserver:context:log-from-handler re-admitted to entryStateless + entryModern
  • Full gates at this tip: typecheck, lint, core (1207), client (535), server (320), integration (348), e2e (2535p/205xf)

Breaking Changes

None. New optional Transport.hasPerRequestStream flag; existing transports without it keep legacy cancel semantics.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

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

Additional context

Changesets + migration.md / migration-SKILL.md entries included for the cancel and log changes.

@felixweinberger
felixweinberger requested a review from a team as a code ownerJune 19, 2026 14:18
@changeset-bot

changeset-botBot commented Jun 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cf76ed1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@modelcontextprotocol/coreMinor
@modelcontextprotocol/clientMinor
@modelcontextprotocol/serverPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-newBot commented Jun 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

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

@modelcontextprotocol/codemod

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

@modelcontextprotocol/server

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

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server-legacy@2330

@modelcontextprotocol/express

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

@modelcontextprotocol/fastify

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

@modelcontextprotocol/hono

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

@modelcontextprotocol/node

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

commit: cf76ed1

@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch from 1e780e2 to 6e8a521CompareJune 19, 2026 14:24
Comment threadpackages/client/src/client/streamableHttp.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from 761bd17 to e230a8eCompareJune 19, 2026 14:37
Comment threadpackages/server/src/server/server.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch 9 times, most recently from 996359b to 55bc820CompareJune 22, 2026 15:22
Base automatically changed from fweinberger/sep2243-std to v2-2026-07-28June 22, 2026 15:28
… path
classifyEntryRequest grows a needsForward flag (default true) that gates the
body-preserving request.clone() it tees off for the legacy leg. isLegacyRequest
passes false: it already classifies a clone of the caller's request and never
reads forwardRequest, so the second clone buffered one extra body copy per
routed POST for nothing. The entry handler call site is unchanged (default
true) and the body-readability contract test stays.
Follow-up from the #2316 review (r3435103544); closes#36.
…tion timing-sensitive on CI
The 75% replay check depends on the request-related event-store path; on this stack the request-related routing change lands before the store-first fix, so the post-disconnect log is not reliably replayed on intermediate commits. Re-enable once the store-first fix is in the base.
…est stream, not POSTing notifications/cancelled
The 2026-07-28 spec (basic/patterns/cancellation §Transport-Specific) makes
closing the per-request SSE response stream the cancellation signal on
Streamable HTTP — no notifications/cancelled message is required or expected.
The general client request-abort path now routes accordingly: on a 2026-era
connection over a transport that opens a per-request stream, aborting the
caller signal or hitting the timeout aborts that request's
TransportSendOptions.requestSignal (the same per-request abort proven by the
listen driver) instead of POSTing notifications/cancelled. Legacy-era
connections, and stdio/in-memory at any era, keep the existing
notifications/cancelled POST path unchanged.
Adds the optional Transport.hasPerRequestStream capability flag (set on
StreamableHTTPClientTransport) so the protocol layer can route the
per-transport cancel path without inspecting transport type. e2e splits the
2025 cancel/timeout requirements via supersedes/supersededBy and adds the
2026 stream-close requirement on the entryModern arm.
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from e230a8e to ee14814CompareJune 22, 2026 16:53
Comment threaddocs/migration.md
…ing delivers it; 2026-era requests filter by _meta.logLevel
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from ee14814 to cf76ed1CompareJune 22, 2026 20:12
@felixweinberger
felixweinberger merged commit 5f18fcd into v2-2026-07-28Jun 22, 2026
20 checks passed
@felixweinberger
felixweinberger deleted the fweinberger/on-small-fixes branch June 22, 2026 21:26
felixweinberger added a commit that referenced this pull request Jun 24, 2026
Sign up for freeto 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.

1 participant

@felixweinberger
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone - #2330

Merged
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes
Jun 22, 2026
Merged

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone#2330
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes

Conversation

@felixweinberger

Copy link
Copy Markdown
Contributor

Three small server/client correctness fixes for the 2026-07-28 per-request hosting path. Stacked on #2328.

Motivation and Context

  • ctx.mcpReq.log on per-request hosting — handler-emitted log notifications were sent via the session-scoped sendLoggingMessage path, which has no delivery channel under createMcpHandler's per-request model. Server.buildContext now wires log through the request context's ctx.mcpReq.notify (stamping relatedRequestId) so the notification rides the in-flight exchange the same way progress/notify already do. Level filtering branches by era: 2026-era reads the per-request _meta['io.modelcontextprotocol/logLevel'] envelope key (the modern equivalent now that logging/setLevel is removed); legacy-era keeps the existing _loggingLevels map lookup unchanged. The public sendLoggingMessage API is untouched.
  • 2026-era HTTP cancel = stream close — the spec wording at the 2026-07-28 revision is "no notifications/cancelled message is required or expected" when the transport carries a per-request stream. Protocol._requestWithSchemaViaCodec now threads a per-request AbortController and aborts it on cancel/timeout when codec.era === MODERN_WIRE_REVISION && transport.hasPerRequestStream; otherwise the legacy notifications/cancelled POST is byte-identical. New optional Transport.hasPerRequestStream flag, set true on StreamableHTTPClientTransport.
  • createMcpHandler clone nitclassifyEntryRequest grows a needsForward flag (default true) gating the body-preserving request.clone(); the isLegacyRequest path passes false so the unused forwarding tee is skipped.

How Has This Been Tested?

  • core unit tests cover modern×per-request-stream / modern×stdio / legacy×per-request-stream / modern timeout for the cancel path; client test pins hasPerRequestStream
  • e2e: protocol:cancel:http-stream-close supersedes the abort-signal / sends-cancellation rows (manifest-only); mcpserver:context:log-from-handler re-admitted to entryStateless + entryModern
  • Full gates at this tip: typecheck, lint, core (1207), client (535), server (320), integration (348), e2e (2535p/205xf)

Breaking Changes

None. New optional Transport.hasPerRequestStream flag; existing transports without it keep legacy cancel semantics.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

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

Additional context

Changesets + migration.md / migration-SKILL.md entries included for the cancel and log changes.

@felixweinberger
felixweinberger requested a review from a team as a code ownerJune 19, 2026 14:18
@changeset-bot

changeset-botBot commented Jun 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cf76ed1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@modelcontextprotocol/coreMinor
@modelcontextprotocol/clientMinor
@modelcontextprotocol/serverPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-newBot commented Jun 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

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

@modelcontextprotocol/codemod

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

@modelcontextprotocol/server

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

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server-legacy@2330

@modelcontextprotocol/express

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

@modelcontextprotocol/fastify

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

@modelcontextprotocol/hono

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

@modelcontextprotocol/node

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

commit: cf76ed1

@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch from 1e780e2 to 6e8a521CompareJune 19, 2026 14:24
Comment threadpackages/client/src/client/streamableHttp.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from 761bd17 to e230a8eCompareJune 19, 2026 14:37
Comment threadpackages/server/src/server/server.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch 9 times, most recently from 996359b to 55bc820CompareJune 22, 2026 15:22
Base automatically changed from fweinberger/sep2243-std to v2-2026-07-28June 22, 2026 15:28
… path
classifyEntryRequest grows a needsForward flag (default true) that gates the
body-preserving request.clone() it tees off for the legacy leg. isLegacyRequest
passes false: it already classifies a clone of the caller's request and never
reads forwardRequest, so the second clone buffered one extra body copy per
routed POST for nothing. The entry handler call site is unchanged (default
true) and the body-readability contract test stays.
Follow-up from the #2316 review (r3435103544); closes#36.
…tion timing-sensitive on CI
The 75% replay check depends on the request-related event-store path; on this stack the request-related routing change lands before the store-first fix, so the post-disconnect log is not reliably replayed on intermediate commits. Re-enable once the store-first fix is in the base.
…est stream, not POSTing notifications/cancelled
The 2026-07-28 spec (basic/patterns/cancellation §Transport-Specific) makes
closing the per-request SSE response stream the cancellation signal on
Streamable HTTP — no notifications/cancelled message is required or expected.
The general client request-abort path now routes accordingly: on a 2026-era
connection over a transport that opens a per-request stream, aborting the
caller signal or hitting the timeout aborts that request's
TransportSendOptions.requestSignal (the same per-request abort proven by the
listen driver) instead of POSTing notifications/cancelled. Legacy-era
connections, and stdio/in-memory at any era, keep the existing
notifications/cancelled POST path unchanged.
Adds the optional Transport.hasPerRequestStream capability flag (set on
StreamableHTTPClientTransport) so the protocol layer can route the
per-transport cancel path without inspecting transport type. e2e splits the
2025 cancel/timeout requirements via supersedes/supersededBy and adds the
2026 stream-close requirement on the entryModern arm.
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from e230a8e to ee14814CompareJune 22, 2026 16:53
Comment threaddocs/migration.md
…ing delivers it; 2026-era requests filter by _meta.logLevel
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from ee14814 to cf76ed1CompareJune 22, 2026 20:12
@felixweinberger
felixweinberger merged commit 5f18fcd into v2-2026-07-28Jun 22, 2026
20 checks passed
@felixweinberger
felixweinberger deleted the fweinberger/on-small-fixes branch June 22, 2026 21:26
felixweinberger added a commit that referenced this pull request Jun 24, 2026
Sign up for freeto 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.

1 participant

@felixweinberger
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone - #2330

Merged
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes
Jun 22, 2026
Merged

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone#2330
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes

Conversation

@felixweinberger

Copy link
Copy Markdown
Contributor

Three small server/client correctness fixes for the 2026-07-28 per-request hosting path. Stacked on #2328.

Motivation and Context

  • ctx.mcpReq.log on per-request hosting — handler-emitted log notifications were sent via the session-scoped sendLoggingMessage path, which has no delivery channel under createMcpHandler's per-request model. Server.buildContext now wires log through the request context's ctx.mcpReq.notify (stamping relatedRequestId) so the notification rides the in-flight exchange the same way progress/notify already do. Level filtering branches by era: 2026-era reads the per-request _meta['io.modelcontextprotocol/logLevel'] envelope key (the modern equivalent now that logging/setLevel is removed); legacy-era keeps the existing _loggingLevels map lookup unchanged. The public sendLoggingMessage API is untouched.
  • 2026-era HTTP cancel = stream close — the spec wording at the 2026-07-28 revision is "no notifications/cancelled message is required or expected" when the transport carries a per-request stream. Protocol._requestWithSchemaViaCodec now threads a per-request AbortController and aborts it on cancel/timeout when codec.era === MODERN_WIRE_REVISION && transport.hasPerRequestStream; otherwise the legacy notifications/cancelled POST is byte-identical. New optional Transport.hasPerRequestStream flag, set true on StreamableHTTPClientTransport.
  • createMcpHandler clone nitclassifyEntryRequest grows a needsForward flag (default true) gating the body-preserving request.clone(); the isLegacyRequest path passes false so the unused forwarding tee is skipped.

How Has This Been Tested?

  • core unit tests cover modern×per-request-stream / modern×stdio / legacy×per-request-stream / modern timeout for the cancel path; client test pins hasPerRequestStream
  • e2e: protocol:cancel:http-stream-close supersedes the abort-signal / sends-cancellation rows (manifest-only); mcpserver:context:log-from-handler re-admitted to entryStateless + entryModern
  • Full gates at this tip: typecheck, lint, core (1207), client (535), server (320), integration (348), e2e (2535p/205xf)

Breaking Changes

None. New optional Transport.hasPerRequestStream flag; existing transports without it keep legacy cancel semantics.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

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

Additional context

Changesets + migration.md / migration-SKILL.md entries included for the cancel and log changes.

@felixweinberger
felixweinberger requested a review from a team as a code ownerJune 19, 2026 14:18
@changeset-bot

changeset-botBot commented Jun 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cf76ed1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@modelcontextprotocol/coreMinor
@modelcontextprotocol/clientMinor
@modelcontextprotocol/serverPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-newBot commented Jun 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

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

@modelcontextprotocol/codemod

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

@modelcontextprotocol/server

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

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server-legacy@2330

@modelcontextprotocol/express

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

@modelcontextprotocol/fastify

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

@modelcontextprotocol/hono

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

@modelcontextprotocol/node

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

commit: cf76ed1

@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch from 1e780e2 to 6e8a521CompareJune 19, 2026 14:24
Comment threadpackages/client/src/client/streamableHttp.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from 761bd17 to e230a8eCompareJune 19, 2026 14:37
Comment threadpackages/server/src/server/server.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch 9 times, most recently from 996359b to 55bc820CompareJune 22, 2026 15:22
Base automatically changed from fweinberger/sep2243-std to v2-2026-07-28June 22, 2026 15:28
… path
classifyEntryRequest grows a needsForward flag (default true) that gates the
body-preserving request.clone() it tees off for the legacy leg. isLegacyRequest
passes false: it already classifies a clone of the caller's request and never
reads forwardRequest, so the second clone buffered one extra body copy per
routed POST for nothing. The entry handler call site is unchanged (default
true) and the body-readability contract test stays.
Follow-up from the #2316 review (r3435103544); closes#36.
…tion timing-sensitive on CI
The 75% replay check depends on the request-related event-store path; on this stack the request-related routing change lands before the store-first fix, so the post-disconnect log is not reliably replayed on intermediate commits. Re-enable once the store-first fix is in the base.
…est stream, not POSTing notifications/cancelled
The 2026-07-28 spec (basic/patterns/cancellation §Transport-Specific) makes
closing the per-request SSE response stream the cancellation signal on
Streamable HTTP — no notifications/cancelled message is required or expected.
The general client request-abort path now routes accordingly: on a 2026-era
connection over a transport that opens a per-request stream, aborting the
caller signal or hitting the timeout aborts that request's
TransportSendOptions.requestSignal (the same per-request abort proven by the
listen driver) instead of POSTing notifications/cancelled. Legacy-era
connections, and stdio/in-memory at any era, keep the existing
notifications/cancelled POST path unchanged.
Adds the optional Transport.hasPerRequestStream capability flag (set on
StreamableHTTPClientTransport) so the protocol layer can route the
per-transport cancel path without inspecting transport type. e2e splits the
2025 cancel/timeout requirements via supersedes/supersededBy and adds the
2026 stream-close requirement on the entryModern arm.
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from e230a8e to ee14814CompareJune 22, 2026 16:53
Comment threaddocs/migration.md
…ing delivers it; 2026-era requests filter by _meta.logLevel
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from ee14814 to cf76ed1CompareJune 22, 2026 20:12
@felixweinberger
felixweinberger merged commit 5f18fcd into v2-2026-07-28Jun 22, 2026
20 checks passed
@felixweinberger
felixweinberger deleted the fweinberger/on-small-fixes branch June 22, 2026 21:26
felixweinberger added a commit that referenced this pull request Jun 24, 2026
Sign up for freeto 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.

1 participant

@felixweinberger
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone - #2330

Merged
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes
Jun 22, 2026
Merged

fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone#2330
felixweinberger merged 4 commits into
v2-2026-07-28from
fweinberger/on-small-fixes

Conversation

@felixweinberger

Copy link
Copy Markdown
Contributor

Three small server/client correctness fixes for the 2026-07-28 per-request hosting path. Stacked on #2328.

Motivation and Context

  • ctx.mcpReq.log on per-request hosting — handler-emitted log notifications were sent via the session-scoped sendLoggingMessage path, which has no delivery channel under createMcpHandler's per-request model. Server.buildContext now wires log through the request context's ctx.mcpReq.notify (stamping relatedRequestId) so the notification rides the in-flight exchange the same way progress/notify already do. Level filtering branches by era: 2026-era reads the per-request _meta['io.modelcontextprotocol/logLevel'] envelope key (the modern equivalent now that logging/setLevel is removed); legacy-era keeps the existing _loggingLevels map lookup unchanged. The public sendLoggingMessage API is untouched.
  • 2026-era HTTP cancel = stream close — the spec wording at the 2026-07-28 revision is "no notifications/cancelled message is required or expected" when the transport carries a per-request stream. Protocol._requestWithSchemaViaCodec now threads a per-request AbortController and aborts it on cancel/timeout when codec.era === MODERN_WIRE_REVISION && transport.hasPerRequestStream; otherwise the legacy notifications/cancelled POST is byte-identical. New optional Transport.hasPerRequestStream flag, set true on StreamableHTTPClientTransport.
  • createMcpHandler clone nitclassifyEntryRequest grows a needsForward flag (default true) gating the body-preserving request.clone(); the isLegacyRequest path passes false so the unused forwarding tee is skipped.

How Has This Been Tested?

  • core unit tests cover modern×per-request-stream / modern×stdio / legacy×per-request-stream / modern timeout for the cancel path; client test pins hasPerRequestStream
  • e2e: protocol:cancel:http-stream-close supersedes the abort-signal / sends-cancellation rows (manifest-only); mcpserver:context:log-from-handler re-admitted to entryStateless + entryModern
  • Full gates at this tip: typecheck, lint, core (1207), client (535), server (320), integration (348), e2e (2535p/205xf)

Breaking Changes

None. New optional Transport.hasPerRequestStream flag; existing transports without it keep legacy cancel semantics.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

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

Additional context

Changesets + migration.md / migration-SKILL.md entries included for the cancel and log changes.

@felixweinberger
felixweinberger requested a review from a team as a code ownerJune 19, 2026 14:18
@changeset-bot

changeset-botBot commented Jun 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cf76ed1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@modelcontextprotocol/coreMinor
@modelcontextprotocol/clientMinor
@modelcontextprotocol/serverPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-newBot commented Jun 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

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

@modelcontextprotocol/codemod

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

@modelcontextprotocol/server

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

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server-legacy@2330

@modelcontextprotocol/express

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

@modelcontextprotocol/fastify

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

@modelcontextprotocol/hono

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

@modelcontextprotocol/node

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

commit: cf76ed1

@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch from 1e780e2 to 6e8a521CompareJune 19, 2026 14:24
Comment threadpackages/client/src/client/streamableHttp.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from 761bd17 to e230a8eCompareJune 19, 2026 14:37
Comment threadpackages/server/src/server/server.ts
Comment threadpackages/server/src/server/server.ts
@felixweinberger
felixweinbergerforce-pushed the fweinberger/sep2243-std branch 9 times, most recently from 996359b to 55bc820CompareJune 22, 2026 15:22
Base automatically changed from fweinberger/sep2243-std to v2-2026-07-28June 22, 2026 15:28
… path
classifyEntryRequest grows a needsForward flag (default true) that gates the
body-preserving request.clone() it tees off for the legacy leg. isLegacyRequest
passes false: it already classifies a clone of the caller's request and never
reads forwardRequest, so the second clone buffered one extra body copy per
routed POST for nothing. The entry handler call site is unchanged (default
true) and the body-readability contract test stays.
Follow-up from the #2316 review (r3435103544); closes#36.
…tion timing-sensitive on CI
The 75% replay check depends on the request-related event-store path; on this stack the request-related routing change lands before the store-first fix, so the post-disconnect log is not reliably replayed on intermediate commits. Re-enable once the store-first fix is in the base.
…est stream, not POSTing notifications/cancelled
The 2026-07-28 spec (basic/patterns/cancellation §Transport-Specific) makes
closing the per-request SSE response stream the cancellation signal on
Streamable HTTP — no notifications/cancelled message is required or expected.
The general client request-abort path now routes accordingly: on a 2026-era
connection over a transport that opens a per-request stream, aborting the
caller signal or hitting the timeout aborts that request's
TransportSendOptions.requestSignal (the same per-request abort proven by the
listen driver) instead of POSTing notifications/cancelled. Legacy-era
connections, and stdio/in-memory at any era, keep the existing
notifications/cancelled POST path unchanged.
Adds the optional Transport.hasPerRequestStream capability flag (set on
StreamableHTTPClientTransport) so the protocol layer can route the
per-transport cancel path without inspecting transport type. e2e splits the
2025 cancel/timeout requirements via supersedes/supersededBy and adds the
2026 stream-close requirement on the entryModern arm.
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from e230a8e to ee14814CompareJune 22, 2026 16:53
Comment threaddocs/migration.md
…ing delivers it; 2026-era requests filter by _meta.logLevel
@felixweinberger
felixweinbergerforce-pushed the fweinberger/on-small-fixes branch from ee14814 to cf76ed1CompareJune 22, 2026 20:12
@felixweinberger
felixweinberger merged commit 5f18fcd into v2-2026-07-28Jun 22, 2026
20 checks passed
@felixweinberger
felixweinberger deleted the fweinberger/on-small-fixes branch June 22, 2026 21:26
felixweinberger added a commit that referenced this pull request Jun 24, 2026
Sign up for freeto 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.

1 participant

@felixweinberger