test: address Copilot review — tighten progress-token guard and de-flake relay tests (#2028)

- progressTokenOf now delegates to the SDK's ProgressTokenSchema instead of a
  bare typeof check, so fractional and past-MAX_SAFE_INTEGER numbers are
  rejected (matching isProgressToken in inspectorClient.ts); tests cover both.
- Browser relay timeout tests use fake timers with advanceTimersByTimeAsync and
  flush the SSE consumer between pushes, replacing real-sleep windows that a
  loaded runner could race (#1596).
- The log-only timeout test holds and awaits its rejection rather than
  swallowing it in an empty catch.
- progressToken.test.ts gains a file header; the remote-session malformed-token
  fixture uses a single-cast helper instead of `as unknown as`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PsmUAZ6aLUAFqenjJKSSRp
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-09-13 21:30:29 -04:00
commit fb28b0fe64

View file

@ -4,15 +4,20 @@
* and the Node `RemoteSession` import it rather than each carrying a copy.
*/
import { ProgressTokenSchema } from "@modelcontextprotocol/core";
import type { JSONRPCMessage } from "@modelcontextprotocol/client";
/**
* The id of the request a `notifications/progress` belongs to, or `undefined`
* for any other message. The SDK stamps `progressToken: messageId` on the
* outbound request, so a progress notification's `progressToken` is the id of
* the request whose wait should be re-armed. A JSON-RPC progress token is a
* string or a number; anything else is ignored so a malformed token can't be
* coerced into a map key.
* The token identifying the request a `notifications/progress` belongs to, or
* `undefined` for any other message. The SDK stamps `progressToken: messageId`
* on the outbound request, so a progress notification's `progressToken` is the
* id of the request whose wait should be re-armed.
*
* Validation is delegated to the SDK's own `ProgressTokenSchema` rather than a
* hand-rolled `typeof` check — the protocol allows only a string or a *safe*
* integer, and zod's `.int()` rejects a fractional or past-`MAX_SAFE_INTEGER`
* value that a bare `typeof token === "number"` would wave through (the same
* drift `isProgressToken` in `core/mcp/inspectorClient.ts` exists to avoid).
*/
export function progressTokenOf(
message: JSONRPCMessage,
@ -24,9 +29,11 @@ export function progressTokenOf(
typeof message.params === "object" &&
message.params !== null
) {
const token = (message.params as { progressToken?: unknown }).progressToken;
if (typeof token === "string" || typeof token === "number") {
return token;
const parsed = ProgressTokenSchema.safeParse(
(message.params as { progressToken?: unknown }).progressToken,
);
if (parsed.success) {
return parsed.data;
}
}
return undefined;