From 80665eeffa6ad9aefb551b0083975c0f26f17e57 Mon Sep 17 00:00:00 2001 From: mahsaforati Date: Fri, 18 Sep 2026 13:58:50 -0700 Subject: [PATCH] mcp: order initialized notification before the first request Keep sends serialized until notifications/initialized has been delivered. Each message is a separate HTTP POST, so otherwise the notification races the first request that follows it, and servers tracking session state can reject that request as uninitialized. Fixes #336841 --- src/vs/workbench/api/common/extHostMcp.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostMcp.ts b/src/vs/workbench/api/common/extHostMcp.ts index bcce7dd46bbceb..7fc4f8f0324972 100644 --- a/src/vs/workbench/api/common/extHostMcp.ts +++ b/src/vs/workbench/api/common/extHostMcp.ts @@ -365,6 +365,7 @@ export class McpHTTPHandle extends Disposable { private readonly _abortCtrl = new AbortController(); private _authMetadata?: AuthMetadata; private _didSendClose = false; + private _didSendInitialized = false; constructor( private readonly _id: number, @@ -384,8 +385,17 @@ export class McpHTTPHandle extends Disposable { async send(message: string) { try { - if (this._mode.value === HttpMode.Unknown) { - await this._requestSequencer.queue(() => this._send(message)); + // Keep sends serialized until `notifications/initialized` has been + // delivered. Each message is a separate HTTP POST, so otherwise the + // notification and the first request that follows it race, and servers + // that track session state can reject the request as uninitialized. + if (this._mode.value === HttpMode.Unknown || !this._didSendInitialized) { + await this._requestSequencer.queue(async () => { + await this._send(message); + if (isInitializedNotification(message)) { + this._didSendInitialized = true; + } + }); } else { await this._send(message); } @@ -956,6 +966,14 @@ function isJSON(str: string): boolean { } } +function isInitializedNotification(message: string): boolean { + try { + return JSON.parse(message)?.method === 'notifications/initialized'; + } catch (e) { + return false; + } +} + function isAuthStatusCode(status: number): boolean { return status === 401 || status === 403; }