Skip to content

core: client notifications carry "id":null; JSON-RPC notifications omit id #1520

Closed

Description

@panyam

core.Request.MarshalJSON always emits "id", so every notification the client sends goes out as {"jsonrpc":"2.0","id":null,"method":"notifications/initialized"}. JSON-RPC 2.0 defines a notification as a request object without an id member. With "id":null it's a request with a null id, which the spec discourages and a strict peer may answer or reject. It applies to every transport, since client.notifyMethod marshals through core.Request.

Seen while fixing #1517 (client/stdio_framing_test.go asserts the method rather than the exact bytes because of it). Both reference SDKs tolerate it today: @modelcontextprotocol/server-everything (TS, via tests/e2e/stdio_interop_test.go) and the Python mcp 2.3.0 client session against our server. So nothing is broken that we know of.

Wanted

  • Omit id when Request.ID is nil (json:"id,omitempty" in the marshal shim), and check the server's IsNotification and the stateless wire still classify both shapes the same way.
  • A wire test pinning a notification's exact bytes.

waiting — trigger: a peer rejects or answers an mcpkit notification, or the 1.0 wire freeze (#1356 is the same kind of decision).

Activity

  1. added
    P3Lowest priority — nice-to-have, revisit occasionally
    waitingParked until the trigger in its body exists
    on Oct 14, 2026
  2. panyam commented on Oct 14, 2026

    @panyam
    OwnerAuthor

    Planned 2026-10-14, parked in favour of #1502. Two more findings for whoever picks it up:

    • server/dispatch.go:377 (panic recovery) checks req.ID != nil, so a notification sent with "id":null whose handler panics gets a -32603 response. Everywhere else uses req.IsNotification(), which treats nil and null alike; use it here too.
    • Fix shape: omitempty on id in the Request.MarshalJSON shim (an explicit json.RawMessage("null") would still be written). core.Response stays as is. No fixture in the tree pins "id":null, and server-side notifications already omit id via core.MarshalNotification. Tighten client/stdio_framing_test.go back to an exact-bytes check as the wire test.
  3. added
    P2Low priority — periodic review
    mission_wire_2026_07_28Mission: 2026-07-28 wire correctness on both wires
    and removed
    P3Lowest priority — nice-to-have, revisit occasionally
    waitingParked until the trigger in its body exists
    on Oct 16, 2026
  4. panyam commented on Oct 16, 2026

    @panyam
    OwnerAuthor

    P3/waiting → P2: JSON-RPC wire correctness, which mission #1552 covers; it was parked only because no mission held it.

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

    P2Low priority — periodic reviewmission_wire_2026_07_28Mission: 2026-07-28 wire correctness on both wires

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions