Skip to content

fix(initialize scenario): return 202 No Content for notifications (closes #274)#299

Open
adityachilka1 wants to merge 1 commit into
modelcontextprotocol:mainfrom
adityachilka1:fix/scenario-202-for-notifications
Open

fix(initialize scenario): return 202 No Content for notifications (closes #274)#299
adityachilka1 wants to merge 1 commit into
modelcontextprotocol:mainfrom
adityachilka1:fix/scenario-202-for-notifications

Conversation

@adityachilka1
Copy link
Copy Markdown

Closes #274.

The bug

The initialize scenario test server had a fall-through branch for post-negotiation
requests:

// src/scenarios/client/initialize.ts:78~90
if (request.method === 'initialize') {
  this.handleInitialize(request, res);
} else if (request.method === 'tools/list') {
  this.handleToolsList(request, res);
} else {
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: {} }));
}

When the client POSTs notifications/initialized (no id field), that
falls into the else branch and the server replied with
{"jsonrpc":"2.0","id":undefined,"result":{}}. That violates the transport
spec (2025-11-25 § 2.1, point 4):

If the input is a JSON-RPC response or notification:

  • If the server accepts the input, the server MUST return HTTP status
    code 202 Accepted with no body.

Strict clients that reject 200-with-body-for-notification were failing against
the scenario.

The fix

Insert a branch for request.id == null before the 200-placeholder else:

         } else if (request.method === 'tools/list') {
           this.handleToolsList(request, res);
+        } else if (request.id == null) {
+          res.writeHead(202);
+          res.end();
         } else {
           res.writeHead(200, { 'Content-Type': 'application/json' });

== null catches both id: null (explicitly passed) and id-omitted (the
JSON.parse result for a notification payload).

Verification

npm run typecheck  # tsgo --noEmit
npm run lint       # eslint + prettier

Both pass clean.

No other behavioral changes

  • Initialize and tools/list flows are untouched.
  • Requests with a present id still hit the 200-placeholder else branch.
  • Only the true notification case (e.g. notifications/initialized) changes -
    from 200+JSON to 202+empty.

Comment thread src/scenarios/client/initialize.ts Outdated
this.handleInitialize(request, res);
} else if (request.method === 'tools/list') {
this.handleToolsList(request, res);
} else if (request.id == null) {
Copy link
Copy Markdown
Contributor

@nbarbettini nbarbettini May 21, 2026

Choose a reason for hiding this comment

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

Can this condition be made more explicit: check that it is in fact a notifications/initialized notification and not something else?

…oses modelcontextprotocol#274)

The transport spec (2025-11-25 § 2.1, point 4) requires that a server
reply to a JSON-RPC notification with 202 Accepted and no body. The
fall-through branch in the initialize scenario test server returned
200 with `{"jsonrpc":"2.0","id":null,"result":{}}` for all non-initialize,
non-tools/list requests - including `notifications/initialized`.

Branch on `request.id == null` (notification) and emit 202 with no body
in that case. Non-notification requests still get the prior placeholder
200-response behavior.

Closes modelcontextprotocol#274.
@adityachilka1 adityachilka1 force-pushed the fix/scenario-202-for-notifications branch from 1f0db44 to 6f51715 Compare May 21, 2026 22:03
@adityachilka1
Copy link
Copy Markdown
Author

Good call. Narrowed the condition to check the exact method (notifications/initialized) rather than any id-less payload. Anything else - stray notifications, malformed requests - falls through to the existing else branch, so the prior behavior for those cases is preserved.

Force-pushed - single-commit PR, one-line diff from the previous revision. Typecheck and lint clean.

@nbarbettini
Copy link
Copy Markdown
Contributor

This LGTM 👍

Sign up for free to 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.

initialize scenario test server violates specification causing client failure

3 participants