Skip to content

[v2] Expose the JSON-RPC request id to callers of request() (onRequestId) #3061

Open

Description

@DanielSLew

Summary

Protocol.request() assigns the JSON-RPC id internally (this._requestMessageId++) and never exposes it to the caller. An extension that opens a long-lived request, and then receives notifications correlated to that request, can't tell which request a given notification belongs to.

Why it matters

In the Events extension (SEP-3415, see #2945), a client may hold several events/stream requests open at once. Each stream starts with a notifications/events/active that confirms the subscription. On transports without per-request streams (stdio), the client needs the request id to match that notification to the open that produced it.

In @mastra/mcp we first bound each open to the first unclaimed active notification. That can mismatch subscriptions when one confirmation arrives late after a timeout. We now work around it by wrapping transport.send to observe the outgoing message and read its id. That relies on internal envelope details.

Proposal

Expose the id through a RequestOptions callback, for example onRequestId?: (id: RequestId) => void, invoked synchronously before the message is sent. An alternative would be a lower-level API that returns { id, result } for the request.

Versions

We hit this on @modelcontextprotocol/client@2.2.0. It's still present on main (c05225a6), in _requestWithSchemaViaCodec in packages/core-internal/src/shared/protocol.ts.

Activity

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

    @Arham-Begani

    I'd like to take this on for v2. Could a maintainer confirm the direction and assign #3061 to @Arham-Begani?

    I propose an optional RequestOptions.onRequestId callback, called once with the actual allocated JSON-RPC ID before transport dispatch. This would let callers correlate concurrent extension requests without wrapping transport.send; request allocation would remain inside the protocol.

    Planned coverage:

    • ID zero, distinct concurrent IDs and notifications arriving out of order.
    • Callback ordering, plus unchanged behavior when the option is absent.
    • Abort, timeout and send-failure cleanup; relevant protocol/client tests, type checks, lint and docs checks.

    One API detail to settle: if the callback throws, should the request reject before sending? That seems preferable to dispatching a request the caller could not correlate.

    I found no matching open PR in issue-number or onRequestId searches. No implementation has started; I'll wait for approval and assignment.

    This proposal was prepared with AI assistance for research and drafting. Any resulting AI-assisted changes will be disclosed and personally reviewed before submission.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions