') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); chore: fast-follow nits — SEP-2243 Number() coercion, connect({prior}) docs, icons example by felixweinberger · Pull Request #2364 · modelcontextprotocol/typescript-sdk · GitHub
Skip to content

chore: fast-follow nits — SEP-2243 Number() coercion, connect({prior}) docs, icons example - #2364

Merged
felixweinberger merged 1 commit into
v2-2026-07-28from
fweinberger/fast-follows-70-80-97
Jun 24, 2026
Merged

chore: fast-follow nits — SEP-2243 Number() coercion, connect({prior}) docs, icons example#2364
felixweinberger merged 1 commit into
v2-2026-07-28from
fweinberger/fast-follows-70-80-97

Conversation

@felixweinberger

Copy link
Copy Markdown
Contributor

Three small fast-follows on the v2 integration branch.

  • validateMcpParamHeaders: gate the integer/number numeric comparison on a strict-decimal pattern (/^-?\d+(\.\d+)?$/) so bare Number() never coerces hex (0x2a), exponent (4.2e1), or padded forms into a false equality with the body value. Adds a regression test.
  • client.ts_onnotification comment: now names ClientResponseCache.write (the encapsulated home of the race guard; _cacheListResult was renamed in feat(client): minimal response-cache substrate (ResponseCacheStore + aggregate-then-write list*()) #2336).
  • docs/client.md: new "Skipping the probe: connect({ prior })" subsection under the version-negotiation section, covering ConnectOptions.prior, getDiscoverResult(), discover(), and the gateway/ example. Type-checked snippet region added in clientGuide.examples.ts.
  • examples/tools/: the calc tool now advertises an icons entry and the client asserts it reaches tools/list — first self-verifying coverage of the icons field that the rebase onto main brought in.

Motivation and Context

Post-merge nits flagged on #2327 / #2336 / #2350, plus the example-coverage gap noted under the rebase-onto-main audit.

How Has This Been Tested?

typecheck:all, lint:all, core suite (1285 tests), sync:snippets --check, docs:check, run:examples (65/65 legs, tools story across all four transport×era legs).

Breaking Changes

None.

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

The ClientOptions.responseCacheStore JSDoc tense (the second comment nit on #2336) was already corrected in a subsequent PR; nothing to do there.

@felixweinberger
felixweinberger requested a review from a team as a code ownerJune 24, 2026 16:44
@changeset-bot

changeset-botBot commented Jun 24, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: d159ef6

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@pkg-pr-new

pkg-pr-newBot commented Jun 24, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

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

@modelcontextprotocol/codemod

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

@modelcontextprotocol/server

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

@modelcontextprotocol/server-legacy

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

@modelcontextprotocol/express

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

@modelcontextprotocol/fastify

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

@modelcontextprotocol/hono

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

@modelcontextprotocol/node

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

commit: d159ef6

Comment threadpackages/core/src/shared/mcpParamHeaders.ts Outdated
…) docs, icons example
- mcpParamHeaders: gate the integer/number numeric comparison on a strict
decimal pattern so bare Number() never coerces hex / exponent / padded
forms into a false equality with the body value (with test).
- client.ts: _onnotification comment now names ClientResponseCache.write
(the encapsulated home of the race guard; _cacheListResult is gone).
- docs/client.md: add "Skipping the probe: connect({ prior })" under the
version-negotiation section, covering ConnectOptions.prior,
getDiscoverResult(), discover(), and the gateway/ example. New
type-checked snippet region in clientGuide.examples.ts.
- examples/tools: advertise an icon on the calc tool and assert it
reaches tools/list — first self-verifying coverage of the icons field.
@felixweinberger
felixweinbergerforce-pushed the fweinberger/fast-follows-70-80-97 branch from cd90c2e to d159ef6CompareJune 24, 2026 18:31

@claudeclaudeBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM — the body-side coercion concern from my earlier comment is addressed (the gate now applies only to the decoded header, with on the body side plus a regression test for the exponent-form case).

Extended reasoning...

Overview

This is a small fast-follow PR touching three independent areas: (1) the SEP-2243 validateMcpParamHeaders numeric comparison in packages/core/src/shared/mcpParamHeaders.ts, now gated by a strict-decimal regex on the decoded header value and a typeof bodyRaw === 'number' check on the body side; (2) a comment rename in packages/client/src/client/client.ts (_cacheListResultClientResponseCache.write); (3) docs/example additions — a connect({ prior }) subsection in docs/client.md with a type-checked snippet, and an icons entry in the examples/tools/ story with a client-side assertion.

Prior review state

On the first revision I flagged that the strict-decimal gate was applied to the body-derived string as well, which would reject numerically-equal pairs whose body value stringifies in exponent form (e.g. 0.0000001'1e-7'). The current revision (d159ef6) implements exactly the suggested fix — header-side regex gate plus typeof bodyRaw === 'number' body-side gate comparing Number(decoded) === bodyRaw — and adds a regression test covering the exponent-form body value as well as the hex/exponent header rejection cases. No new issues were found in this run.

Security risks

The only logic change tightens header/body cross-check semantics: looser numeric forms in the attacker-controllable header (0x2a, 4.2e1, padded strings) no longer coerce into a false equality, which is a strict hardening relative to the previous bare Number() comparison. No auth, crypto, or data-exposure surface is touched. The docs note about reusing a persisted DiscoverResult only across the same authorization context is appropriate.

Level of scrutiny

Moderate-low. The core change is a ~10-line validation tweak with direct unit-test coverage of both the new acceptance case and the new rejection cases; the rest is documentation, comments, and example coverage. The PR description reports the full check matrix (typecheck, lint, core suite, snippet sync, docs check, all example legs) passing. The missing changeset is consistent with this being a chore-level fast-follow on the v2 integration branch.

Other factors

The added test cases directly exercise the regression scenario raised in the previous review round, and the example/docs additions are self-verifying (snippet region is type-checked, the icons assertion runs in the example harness). Overall the changes are small, contained, and correct.

@felixweinberger
felixweinberger merged commit 3d52d12 into v2-2026-07-28Jun 24, 2026
17 checks passed
@felixweinberger
felixweinberger deleted the fweinberger/fast-follows-70-80-97 branch June 24, 2026 21:56
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