@modelcontextprotocol%2Fsdk@1.29.0.patch 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. diff --git a/dist/cjs/client/index.d.ts b/dist/cjs/client/index.d.ts
  2. index 1822bf749aec71d2bb295083d832114ee187bb67..58b859a7b32222fb5cb9f2011fdc5d010f3d05fb 100644
  3. --- a/dist/cjs/client/index.d.ts
  4. +++ b/dist/cjs/client/index.d.ts
  5. @@ -428,6 +428,8 @@ export declare class Client<RequestT extends Request = Request, NotificationT ex
  6. *
  7. * For task-based execution with streaming behavior, use client.experimental.tasks.callToolStream() instead.
  8. */
  9. + callTool(params: CallToolRequest['params'], resultSchema?: undefined, options?: RequestOptions): Promise<SchemaOutput<typeof CallToolResultSchema>>;
  10. + callTool<T extends typeof CallToolResultSchema | typeof CompatibilityCallToolResultSchema>(params: CallToolRequest['params'], resultSchema: T, options?: RequestOptions): Promise<SchemaOutput<T>>;
  11. callTool(params: CallToolRequest['params'], resultSchema?: typeof CallToolResultSchema | typeof CompatibilityCallToolResultSchema, options?: RequestOptions): Promise<{
  12. [x: string]: unknown;
  13. content: ({
  14. diff --git a/dist/esm/client/index.d.ts b/dist/esm/client/index.d.ts
  15. index 1822bf749aec71d2bb295083d832114ee187bb67..58b859a7b32222fb5cb9f2011fdc5d010f3d05fb 100644
  16. --- a/dist/esm/client/index.d.ts
  17. +++ b/dist/esm/client/index.d.ts
  18. @@ -428,6 +428,8 @@ export declare class Client<RequestT extends Request = Request, NotificationT ex
  19. *
  20. * For task-based execution with streaming behavior, use client.experimental.tasks.callToolStream() instead.
  21. */
  22. + callTool(params: CallToolRequest['params'], resultSchema?: undefined, options?: RequestOptions): Promise<SchemaOutput<typeof CallToolResultSchema>>;
  23. + callTool<T extends typeof CallToolResultSchema | typeof CompatibilityCallToolResultSchema>(params: CallToolRequest['params'], resultSchema: T, options?: RequestOptions): Promise<SchemaOutput<T>>;
  24. callTool(params: CallToolRequest['params'], resultSchema?: typeof CallToolResultSchema | typeof CompatibilityCallToolResultSchema, options?: RequestOptions): Promise<{
  25. [x: string]: unknown;
  26. content: ({
  27. diff --git a/dist/cjs/client/index.js b/dist/cjs/client/index.js
  28. index 6ac1da14dc7f6211ae70f7711c124b76098816d8..adb5b7bd45514a406a0f7e40b64631c101584c84 100644
  29. --- a/dist/cjs/client/index.js
  30. +++ b/dist/cjs/client/index.js
  31. @@ -288,41 +288,16 @@ class Client extends protocol_js_1.Protocol {
  32. }
  33. async connect(transport, options) {
  34. await super.connect(transport);
  35. + transport.onsessionexpired = async () => {
  36. + await this._initialize(transport);
  37. + };
  38. // When transport sessionId is already set this means we are trying to reconnect.
  39. // In this case we don't need to initialize again.
  40. if (transport.sessionId !== undefined) {
  41. return;
  42. }
  43. try {
  44. - const result = await this.request({
  45. - method: 'initialize',
  46. - params: {
  47. - protocolVersion: types_js_1.LATEST_PROTOCOL_VERSION,
  48. - capabilities: this._capabilities,
  49. - clientInfo: this._clientInfo
  50. - }
  51. - }, types_js_1.InitializeResultSchema, options);
  52. - if (result === undefined) {
  53. - throw new Error(`Server sent invalid initialize result: ${result}`);
  54. - }
  55. - if (!types_js_1.SUPPORTED_PROTOCOL_VERSIONS.includes(result.protocolVersion)) {
  56. - throw new Error(`Server's protocol version is not supported: ${result.protocolVersion}`);
  57. - }
  58. - this._serverCapabilities = result.capabilities;
  59. - this._serverVersion = result.serverInfo;
  60. - // HTTP transports must set the protocol version in each header after initialization.
  61. - if (transport.setProtocolVersion) {
  62. - transport.setProtocolVersion(result.protocolVersion);
  63. - }
  64. - this._instructions = result.instructions;
  65. - await this.notification({
  66. - method: 'notifications/initialized'
  67. - });
  68. - // Set up list changed handlers now that we know server capabilities
  69. - if (this._pendingListChangedConfig) {
  70. - this._setupListChangedHandlers(this._pendingListChangedConfig);
  71. - this._pendingListChangedConfig = undefined;
  72. - }
  73. + await this._initialize(transport, options);
  74. }
  75. catch (error) {
  76. // Disconnect if initialization fails.
  77. @@ -330,6 +305,37 @@ class Client extends protocol_js_1.Protocol {
  78. throw error;
  79. }
  80. }
  81. + async _initialize(transport, options) {
  82. + const result = await this.request({
  83. + method: 'initialize',
  84. + params: {
  85. + protocolVersion: types_js_1.LATEST_PROTOCOL_VERSION,
  86. + capabilities: this._capabilities,
  87. + clientInfo: this._clientInfo
  88. + }
  89. + }, types_js_1.InitializeResultSchema, options);
  90. + if (result === undefined) {
  91. + throw new Error(`Server sent invalid initialize result: ${result}`);
  92. + }
  93. + if (!types_js_1.SUPPORTED_PROTOCOL_VERSIONS.includes(result.protocolVersion)) {
  94. + throw new Error(`Server's protocol version is not supported: ${result.protocolVersion}`);
  95. + }
  96. + this._serverCapabilities = result.capabilities;
  97. + this._serverVersion = result.serverInfo;
  98. + // HTTP transports must set the protocol version in each header after initialization.
  99. + if (transport.setProtocolVersion) {
  100. + transport.setProtocolVersion(result.protocolVersion);
  101. + }
  102. + this._instructions = result.instructions;
  103. + await this.notification({
  104. + method: 'notifications/initialized'
  105. + });
  106. + // Set up list changed handlers now that we know server capabilities
  107. + if (this._pendingListChangedConfig) {
  108. + this._setupListChangedHandlers(this._pendingListChangedConfig);
  109. + this._pendingListChangedConfig = undefined;
  110. + }
  111. + }
  112. /**
  113. * After initialization has completed, this will be populated with the server's reported capabilities.
  114. */
  115. diff --git a/dist/cjs/client/streamableHttp.js b/dist/cjs/client/streamableHttp.js
  116. index a29a7d3a0f14d9cd800ef5b296485237350c666f..c362ae5fe6c62c8c8eae7e2e61de1eedff5443c9 100644
  117. --- a/dist/cjs/client/streamableHttp.js
  118. +++ b/dist/cjs/client/streamableHttp.js
  119. @@ -290,7 +290,38 @@ class StreamableHTTPClientTransport {
  120. this.onclose?.();
  121. }
  122. async send(message, options) {
  123. + return this._send(message, options, false);
  124. + }
  125. + async _recoverSession(expiredSessionId) {
  126. + if (this._sessionRecovery) {
  127. + await this._sessionRecovery;
  128. + return true;
  129. + }
  130. + if (this._sessionId !== expiredSessionId)
  131. + return true;
  132. + this._sessionId = undefined;
  133. + this._sessionRecovery = Promise.resolve().then(() => this.onsessionexpired?.());
  134. try {
  135. + await this._sessionRecovery;
  136. + }
  137. + catch (error) {
  138. + this._sessionId = undefined;
  139. + await this.close();
  140. + throw error;
  141. + }
  142. + finally {
  143. + this._sessionRecovery = undefined;
  144. + }
  145. + return true;
  146. + }
  147. + async _send(message, options, isSessionRetry) {
  148. + try {
  149. + if (this._sessionRecovery && !(0, types_js_1.isInitializeRequest)(message) && !(0, types_js_1.isInitializedNotification)(message)) {
  150. + await this._sessionRecovery;
  151. + if (options?.isRequestActive?.() === false) {
  152. + throw new Error('Request is no longer active');
  153. + }
  154. + }
  155. const { resumptionToken, onresumptiontoken } = options || {};
  156. if (resumptionToken) {
  157. // If we have at last event ID, we need to reconnect the SSE stream
  158. @@ -298,6 +329,7 @@ class StreamableHTTPClientTransport {
  159. return;
  160. }
  161. const headers = await this._commonHeaders();
  162. + const requestSessionId = headers.get('mcp-session-id') ?? undefined;
  163. headers.set('content-type', 'application/json');
  164. headers.set('accept', 'application/json, text/event-stream');
  165. const init = {
  166. @@ -310,11 +342,20 @@ class StreamableHTTPClientTransport {
  167. const response = await (this._fetch ?? fetch)(this._url, init);
  168. // Handle session ID received during initialization
  169. const sessionId = response.headers.get('mcp-session-id');
  170. - if (sessionId) {
  171. + if (sessionId && (requestSessionId === undefined || this._sessionId === requestSessionId)) {
  172. this._sessionId = sessionId;
  173. }
  174. if (!response.ok) {
  175. const text = await response.text().catch(() => null);
  176. + if (response.status === 404 && requestSessionId && !isSessionRetry && !(0, types_js_1.isInitializedNotification)(message)) {
  177. + const recovered = await this._recoverSession(requestSessionId);
  178. + if (options?.isRequestActive?.() === false) {
  179. + throw new Error('Request is no longer active');
  180. + }
  181. + if (recovered) {
  182. + return this._send(message, options, true);
  183. + }
  184. + }
  185. if (response.status === 401 && this._authProvider) {
  186. // Prevent infinite recursion when server returns 401 after successful auth
  187. if (this._hasCompletedAuthFlow) {
  188. @@ -335,7 +376,7 @@ class StreamableHTTPClientTransport {
  189. // Mark that we completed auth flow
  190. this._hasCompletedAuthFlow = true;
  191. // Purposely _not_ awaited, so we don't call onerror twice
  192. - return this.send(message);
  193. + return this._send(message, options, isSessionRetry);
  194. }
  195. if (response.status === 403 && this._authProvider) {
  196. const { resourceMetadataUrl, scope, error } = (0, auth_js_1.extractWWWAuthenticateParams)(response);
  197. @@ -362,7 +403,7 @@ class StreamableHTTPClientTransport {
  198. if (result !== 'AUTHORIZED') {
  199. throw new auth_js_1.UnauthorizedError();
  200. }
  201. - return this.send(message);
  202. + return this._send(message, options, isSessionRetry);
  203. }
  204. }
  205. throw new StreamableHTTPError(response.status, `Error POSTing to endpoint: ${text}`);
  206. diff --git a/dist/cjs/shared/protocol.js b/dist/cjs/shared/protocol.js
  207. index 3617e787f0ba70447c99501aee7aa67584d89758..4a96d6a0328fa348b96f3869ab7e0bb77538182b 100644
  208. --- a/dist/cjs/shared/protocol.js
  209. +++ b/dist/cjs/shared/protocol.js
  210. @@ -744,7 +744,12 @@ class Protocol {
  211. }
  212. else {
  213. // No related task - send through transport normally
  214. - this._transport.send(jsonrpcRequest, { relatedRequestId, resumptionToken, onresumptiontoken }).catch(error => {
  215. + this._transport.send(jsonrpcRequest, {
  216. + relatedRequestId,
  217. + resumptionToken,
  218. + onresumptiontoken,
  219. + isRequestActive: () => this._responseHandlers.has(messageId)
  220. + }).catch(error => {
  221. this._cleanupTimeout(messageId);
  222. reject(error);
  223. });
  224. diff --git a/dist/esm/client/index.js b/dist/esm/client/index.js
  225. index 49b12c6cd918c457420fef7ad5528a9443d1a191..2afe2e22e960f26c9d516ef135d89f8eb9e4caff 100644
  226. --- a/dist/esm/client/index.js
  227. +++ b/dist/esm/client/index.js
  228. @@ -284,41 +284,16 @@ export class Client extends Protocol {
  229. }
  230. async connect(transport, options) {
  231. await super.connect(transport);
  232. + transport.onsessionexpired = async () => {
  233. + await this._initialize(transport);
  234. + };
  235. // When transport sessionId is already set this means we are trying to reconnect.
  236. // In this case we don't need to initialize again.
  237. if (transport.sessionId !== undefined) {
  238. return;
  239. }
  240. try {
  241. - const result = await this.request({
  242. - method: 'initialize',
  243. - params: {
  244. - protocolVersion: LATEST_PROTOCOL_VERSION,
  245. - capabilities: this._capabilities,
  246. - clientInfo: this._clientInfo
  247. - }
  248. - }, InitializeResultSchema, options);
  249. - if (result === undefined) {
  250. - throw new Error(`Server sent invalid initialize result: ${result}`);
  251. - }
  252. - if (!SUPPORTED_PROTOCOL_VERSIONS.includes(result.protocolVersion)) {
  253. - throw new Error(`Server's protocol version is not supported: ${result.protocolVersion}`);
  254. - }
  255. - this._serverCapabilities = result.capabilities;
  256. - this._serverVersion = result.serverInfo;
  257. - // HTTP transports must set the protocol version in each header after initialization.
  258. - if (transport.setProtocolVersion) {
  259. - transport.setProtocolVersion(result.protocolVersion);
  260. - }
  261. - this._instructions = result.instructions;
  262. - await this.notification({
  263. - method: 'notifications/initialized'
  264. - });
  265. - // Set up list changed handlers now that we know server capabilities
  266. - if (this._pendingListChangedConfig) {
  267. - this._setupListChangedHandlers(this._pendingListChangedConfig);
  268. - this._pendingListChangedConfig = undefined;
  269. - }
  270. + await this._initialize(transport, options);
  271. }
  272. catch (error) {
  273. // Disconnect if initialization fails.
  274. @@ -326,6 +301,37 @@ export class Client extends Protocol {
  275. throw error;
  276. }
  277. }
  278. + async _initialize(transport, options) {
  279. + const result = await this.request({
  280. + method: 'initialize',
  281. + params: {
  282. + protocolVersion: LATEST_PROTOCOL_VERSION,
  283. + capabilities: this._capabilities,
  284. + clientInfo: this._clientInfo
  285. + }
  286. + }, InitializeResultSchema, options);
  287. + if (result === undefined) {
  288. + throw new Error(`Server sent invalid initialize result: ${result}`);
  289. + }
  290. + if (!SUPPORTED_PROTOCOL_VERSIONS.includes(result.protocolVersion)) {
  291. + throw new Error(`Server's protocol version is not supported: ${result.protocolVersion}`);
  292. + }
  293. + this._serverCapabilities = result.capabilities;
  294. + this._serverVersion = result.serverInfo;
  295. + // HTTP transports must set the protocol version in each header after initialization.
  296. + if (transport.setProtocolVersion) {
  297. + transport.setProtocolVersion(result.protocolVersion);
  298. + }
  299. + this._instructions = result.instructions;
  300. + await this.notification({
  301. + method: 'notifications/initialized'
  302. + });
  303. + // Set up list changed handlers now that we know server capabilities
  304. + if (this._pendingListChangedConfig) {
  305. + this._setupListChangedHandlers(this._pendingListChangedConfig);
  306. + this._pendingListChangedConfig = undefined;
  307. + }
  308. + }
  309. /**
  310. * After initialization has completed, this will be populated with the server's reported capabilities.
  311. */
  312. diff --git a/dist/esm/client/streamableHttp.js b/dist/esm/client/streamableHttp.js
  313. index 624172aa24ae255a67c083f9c19053343e4a0581..ac75b14545fda44aff7ff4d97cc5da884fcc627a 100644
  314. --- a/dist/esm/client/streamableHttp.js
  315. +++ b/dist/esm/client/streamableHttp.js
  316. @@ -1,5 +1,5 @@
  317. import { createFetchWithInit, normalizeHeaders } from '../shared/transport.js';
  318. -import { isInitializedNotification, isJSONRPCRequest, isJSONRPCResultResponse, JSONRPCMessageSchema } from '../types.js';
  319. +import { isInitializedNotification, isInitializeRequest, isJSONRPCRequest, isJSONRPCResultResponse, JSONRPCMessageSchema } from '../types.js';
  320. import { auth, extractWWWAuthenticateParams, UnauthorizedError } from './auth.js';
  321. import { EventSourceParserStream } from 'eventsource-parser/stream';
  322. // Default reconnection options for StreamableHTTP connections
  323. @@ -286,7 +286,38 @@ export class StreamableHTTPClientTransport {
  324. this.onclose?.();
  325. }
  326. async send(message, options) {
  327. + return this._send(message, options, false);
  328. + }
  329. + async _recoverSession(expiredSessionId) {
  330. + if (this._sessionRecovery) {
  331. + await this._sessionRecovery;
  332. + return true;
  333. + }
  334. + if (this._sessionId !== expiredSessionId)
  335. + return true;
  336. + this._sessionId = undefined;
  337. + this._sessionRecovery = Promise.resolve().then(() => this.onsessionexpired?.());
  338. try {
  339. + await this._sessionRecovery;
  340. + }
  341. + catch (error) {
  342. + this._sessionId = undefined;
  343. + await this.close();
  344. + throw error;
  345. + }
  346. + finally {
  347. + this._sessionRecovery = undefined;
  348. + }
  349. + return true;
  350. + }
  351. + async _send(message, options, isSessionRetry) {
  352. + try {
  353. + if (this._sessionRecovery && !isInitializeRequest(message) && !isInitializedNotification(message)) {
  354. + await this._sessionRecovery;
  355. + if (options?.isRequestActive?.() === false) {
  356. + throw new Error('Request is no longer active');
  357. + }
  358. + }
  359. const { resumptionToken, onresumptiontoken } = options || {};
  360. if (resumptionToken) {
  361. // If we have at last event ID, we need to reconnect the SSE stream
  362. @@ -294,6 +325,7 @@ export class StreamableHTTPClientTransport {
  363. return;
  364. }
  365. const headers = await this._commonHeaders();
  366. + const requestSessionId = headers.get('mcp-session-id') ?? undefined;
  367. headers.set('content-type', 'application/json');
  368. headers.set('accept', 'application/json, text/event-stream');
  369. const init = {
  370. @@ -306,11 +338,20 @@ export class StreamableHTTPClientTransport {
  371. const response = await (this._fetch ?? fetch)(this._url, init);
  372. // Handle session ID received during initialization
  373. const sessionId = response.headers.get('mcp-session-id');
  374. - if (sessionId) {
  375. + if (sessionId && (requestSessionId === undefined || this._sessionId === requestSessionId)) {
  376. this._sessionId = sessionId;
  377. }
  378. if (!response.ok) {
  379. const text = await response.text().catch(() => null);
  380. + if (response.status === 404 && requestSessionId && !isSessionRetry && !isInitializedNotification(message)) {
  381. + const recovered = await this._recoverSession(requestSessionId);
  382. + if (options?.isRequestActive?.() === false) {
  383. + throw new Error('Request is no longer active');
  384. + }
  385. + if (recovered) {
  386. + return this._send(message, options, true);
  387. + }
  388. + }
  389. if (response.status === 401 && this._authProvider) {
  390. // Prevent infinite recursion when server returns 401 after successful auth
  391. if (this._hasCompletedAuthFlow) {
  392. @@ -331,7 +372,7 @@ export class StreamableHTTPClientTransport {
  393. // Mark that we completed auth flow
  394. this._hasCompletedAuthFlow = true;
  395. // Purposely _not_ awaited, so we don't call onerror twice
  396. - return this.send(message);
  397. + return this._send(message, options, isSessionRetry);
  398. }
  399. if (response.status === 403 && this._authProvider) {
  400. const { resourceMetadataUrl, scope, error } = extractWWWAuthenticateParams(response);
  401. @@ -358,7 +399,7 @@ export class StreamableHTTPClientTransport {
  402. if (result !== 'AUTHORIZED') {
  403. throw new UnauthorizedError();
  404. }
  405. - return this.send(message);
  406. + return this._send(message, options, isSessionRetry);
  407. }
  408. }
  409. throw new StreamableHTTPError(response.status, `Error POSTing to endpoint: ${text}`);
  410. diff --git a/dist/esm/shared/protocol.js b/dist/esm/shared/protocol.js
  411. index bfa2b7120a0f50c569364ea5264e6f811076f44f..abd8dfd707c155f71dae7aeeeeaf7547368ac749 100644
  412. --- a/dist/esm/shared/protocol.js
  413. +++ b/dist/esm/shared/protocol.js
  414. @@ -740,7 +740,12 @@ export class Protocol {
  415. }
  416. else {
  417. // No related task - send through transport normally
  418. - this._transport.send(jsonrpcRequest, { relatedRequestId, resumptionToken, onresumptiontoken }).catch(error => {
  419. + this._transport.send(jsonrpcRequest, {
  420. + relatedRequestId,
  421. + resumptionToken,
  422. + onresumptiontoken,
  423. + isRequestActive: () => this._responseHandlers.has(messageId)
  424. + }).catch(error => {
  425. this._cleanupTimeout(messageId);
  426. reject(error);
  427. });