@mcp-ts/sdk 1.3.2 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +405 -406
  2. package/dist/adapters/agui-adapter.d.mts +1 -1
  3. package/dist/adapters/agui-adapter.d.ts +1 -1
  4. package/dist/adapters/agui-middleware.d.mts +1 -1
  5. package/dist/adapters/agui-middleware.d.ts +1 -1
  6. package/dist/adapters/ai-adapter.d.mts +1 -1
  7. package/dist/adapters/ai-adapter.d.ts +1 -1
  8. package/dist/adapters/langchain-adapter.d.mts +1 -1
  9. package/dist/adapters/langchain-adapter.d.ts +1 -1
  10. package/dist/adapters/mastra-adapter.d.mts +1 -1
  11. package/dist/adapters/mastra-adapter.d.ts +1 -1
  12. package/dist/client/index.d.mts +8 -64
  13. package/dist/client/index.d.ts +8 -64
  14. package/dist/client/index.js +91 -173
  15. package/dist/client/index.js.map +1 -1
  16. package/dist/client/index.mjs +91 -173
  17. package/dist/client/index.mjs.map +1 -1
  18. package/dist/client/react.d.mts +12 -2
  19. package/dist/client/react.d.ts +12 -2
  20. package/dist/client/react.js +119 -182
  21. package/dist/client/react.js.map +1 -1
  22. package/dist/client/react.mjs +119 -182
  23. package/dist/client/react.mjs.map +1 -1
  24. package/dist/client/vue.d.mts +24 -4
  25. package/dist/client/vue.d.ts +24 -4
  26. package/dist/client/vue.js +121 -182
  27. package/dist/client/vue.js.map +1 -1
  28. package/dist/client/vue.mjs +121 -182
  29. package/dist/client/vue.mjs.map +1 -1
  30. package/dist/index.d.mts +2 -2
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +215 -250
  33. package/dist/index.js.map +1 -1
  34. package/dist/index.mjs +215 -250
  35. package/dist/index.mjs.map +1 -1
  36. package/dist/{multi-session-client-B1DBx5yR.d.mts → multi-session-client-DzjmT7FX.d.mts} +1 -0
  37. package/dist/{multi-session-client-DyFzyJUx.d.ts → multi-session-client-FAFpUzZ4.d.ts} +1 -0
  38. package/dist/server/index.d.mts +16 -21
  39. package/dist/server/index.d.ts +16 -21
  40. package/dist/server/index.js +124 -77
  41. package/dist/server/index.js.map +1 -1
  42. package/dist/server/index.mjs +124 -77
  43. package/dist/server/index.mjs.map +1 -1
  44. package/dist/shared/index.d.mts +2 -2
  45. package/dist/shared/index.d.ts +2 -2
  46. package/dist/shared/index.js.map +1 -1
  47. package/dist/shared/index.mjs.map +1 -1
  48. package/dist/{types-PjM1W07s.d.mts → types-CW6lghof.d.mts} +5 -0
  49. package/dist/{types-PjM1W07s.d.ts → types-CW6lghof.d.ts} +5 -0
  50. package/package.json +1 -1
  51. package/src/client/core/sse-client.ts +354 -493
  52. package/src/client/react/use-mcp.ts +75 -23
  53. package/src/client/vue/use-mcp.ts +111 -48
  54. package/src/server/handlers/nextjs-handler.ts +207 -217
  55. package/src/server/handlers/sse-handler.ts +10 -0
  56. package/src/server/mcp/oauth-client.ts +41 -32
  57. package/src/server/storage/types.ts +12 -5
  58. package/src/shared/types.ts +5 -0
package/dist/index.mjs CHANGED
@@ -1311,7 +1311,8 @@ var MCPClient = class _MCPClient {
1311
1311
  serverUrl: this.serverUrl,
1312
1312
  callbackUrl: this.callbackUrl,
1313
1313
  transportType: this.transportType || "streamable_http",
1314
- createdAt: this.createdAt
1314
+ createdAt: this.createdAt,
1315
+ active: false
1315
1316
  }, Math.floor(STATE_EXPIRATION_MS / 1e3));
1316
1317
  }
1317
1318
  }
@@ -1319,9 +1320,10 @@ var MCPClient = class _MCPClient {
1319
1320
  * Saves current session state to storage
1320
1321
  * Creates new session if it doesn't exist, updates if it does
1321
1322
  * @param ttl - Time-to-live in seconds (defaults to 12hr for connected sessions)
1323
+ * @param active - Session status marker used to avoid unnecessary TTL rewrites
1322
1324
  * @private
1323
1325
  */
1324
- async saveSession(ttl = SESSION_TTL_SECONDS) {
1326
+ async saveSession(ttl = SESSION_TTL_SECONDS, active = true) {
1325
1327
  if (!this.sessionId || !this.serverId || !this.serverUrl || !this.callbackUrl) {
1326
1328
  return;
1327
1329
  }
@@ -1333,7 +1335,8 @@ var MCPClient = class _MCPClient {
1333
1335
  serverUrl: this.serverUrl,
1334
1336
  callbackUrl: this.callbackUrl,
1335
1337
  transportType: this.transportType || "streamable_http",
1336
- createdAt: this.createdAt || Date.now()
1338
+ createdAt: this.createdAt || Date.now(),
1339
+ active
1337
1340
  };
1338
1341
  const existingSession = await storage.getSession(this.identity, this.sessionId);
1339
1342
  if (existingSession) {
@@ -1407,15 +1410,17 @@ var MCPClient = class _MCPClient {
1407
1410
  this.emitStateChange("CONNECTED");
1408
1411
  this.emitProgress("Connected successfully");
1409
1412
  const existingSession = await storage.getSession(this.identity, this.sessionId);
1410
- if (!existingSession || existingSession.transportType !== this.transportType) {
1411
- console.log(`[MCPClient] Saving session ${this.sessionId} (new or transport changed)`);
1412
- await this.saveSession(SESSION_TTL_SECONDS);
1413
+ const needsTransportUpdate = !existingSession || existingSession.transportType !== this.transportType;
1414
+ const needsTtlPromotion = !existingSession || existingSession.active !== true;
1415
+ if (needsTransportUpdate || needsTtlPromotion) {
1416
+ console.log(`[MCPClient] Saving session ${this.sessionId} with 12hr TTL (connect success)`);
1417
+ await this.saveSession(SESSION_TTL_SECONDS, true);
1413
1418
  }
1414
1419
  } catch (error) {
1415
1420
  if (error instanceof UnauthorizedError$1 || error instanceof Error && error.message.toLowerCase().includes("unauthorized")) {
1416
1421
  this.emitStateChange("AUTHENTICATING");
1417
1422
  console.log(`[MCPClient] Saving session ${this.sessionId} with 10min TTL (OAuth pending)`);
1418
- await this.saveSession(Math.floor(STATE_EXPIRATION_MS / 1e3));
1423
+ await this.saveSession(Math.floor(STATE_EXPIRATION_MS / 1e3), false);
1419
1424
  let authUrl = "";
1420
1425
  if (this.oauthProvider) {
1421
1426
  authUrl = this.oauthProvider.authUrl || "";
@@ -1493,7 +1498,7 @@ var MCPClient = class _MCPClient {
1493
1498
  await this.client.connect(this.transport);
1494
1499
  this.emitStateChange("CONNECTED");
1495
1500
  console.log(`[MCPClient] Updating session ${this.sessionId} to 12hr TTL (OAuth complete)`);
1496
- await this.saveSession(SESSION_TTL_SECONDS);
1501
+ await this.saveSession(SESSION_TTL_SECONDS, true);
1497
1502
  return;
1498
1503
  } catch (error) {
1499
1504
  lastError = error;
@@ -2158,7 +2163,8 @@ var SSEConnectionManager = class {
2158
2163
  serverName: s.serverName,
2159
2164
  serverUrl: s.serverUrl,
2160
2165
  transport: s.transportType,
2161
- createdAt: s.createdAt
2166
+ createdAt: s.createdAt,
2167
+ active: s.active !== false
2162
2168
  }))
2163
2169
  };
2164
2170
  }
@@ -2173,6 +2179,13 @@ var SSEConnectionManager = class {
2173
2179
  (s) => s.serverId === serverId || s.serverUrl === serverUrl
2174
2180
  );
2175
2181
  if (duplicate) {
2182
+ if (duplicate.active === false) {
2183
+ await this.restoreSession({ sessionId: duplicate.sessionId });
2184
+ return {
2185
+ sessionId: duplicate.sessionId,
2186
+ success: true
2187
+ };
2188
+ }
2176
2189
  throw new Error(`Connection already exists for server: ${duplicate.serverUrl || duplicate.serverId} (${duplicate.serverName})`);
2177
2190
  }
2178
2191
  const sessionId = await storage.generateSessionId();
@@ -2498,7 +2511,9 @@ function writeSSEEvent(res, event, data) {
2498
2511
  }
2499
2512
 
2500
2513
  // src/server/handlers/nextjs-handler.ts
2501
- var managers = /* @__PURE__ */ new Map();
2514
+ function isRpcResponseEvent(event) {
2515
+ return "id" in event && ("result" in event || "error" in event);
2516
+ }
2502
2517
  function createNextMcpHandler(options = {}) {
2503
2518
  const {
2504
2519
  getIdentity = (request) => new URL(request.url).searchParams.get("identity"),
@@ -2511,72 +2526,29 @@ function createNextMcpHandler(options = {}) {
2511
2526
  clientDefaults,
2512
2527
  getClientMetadata
2513
2528
  } = options;
2514
- async function GET(request) {
2515
- const identity = getIdentity(request);
2516
- const authToken = getAuthToken(request);
2517
- if (!identity) {
2518
- return new Response("Missing identity", { status: 400 });
2519
- }
2520
- const isAuthorized = await authenticate(identity, authToken);
2521
- if (!isAuthorized) {
2522
- return new Response("Unauthorized", { status: 401 });
2523
- }
2524
- const stream = new TransformStream();
2525
- const writer = stream.writable.getWriter();
2526
- const encoder = new TextEncoder();
2527
- const sendSSE = (event, data) => {
2528
- const message = `event: ${event}
2529
- data: ${JSON.stringify(data)}
2530
-
2531
- `;
2532
- writer.write(encoder.encode(message)).catch(() => {
2533
- });
2534
- };
2535
- const previousManager = managers.get(identity);
2536
- if (previousManager) {
2537
- previousManager.dispose();
2538
- }
2539
- const resolvedClientMetadata = getClientMetadata ? await getClientMetadata(request) : clientDefaults;
2540
- const manager = new SSEConnectionManager(
2529
+ const toManagerOptions = (identity, resolvedClientMetadata) => ({
2530
+ identity,
2531
+ heartbeatInterval,
2532
+ clientDefaults: resolvedClientMetadata
2533
+ });
2534
+ async function resolveClientMetadata(request) {
2535
+ return getClientMetadata ? await getClientMetadata(request) : clientDefaults;
2536
+ }
2537
+ async function GET() {
2538
+ return Response.json(
2541
2539
  {
2542
- identity,
2543
- heartbeatInterval,
2544
- clientDefaults: resolvedClientMetadata
2545
- // Pass resolved metadata
2546
- },
2547
- (event) => {
2548
- if ("id" in event) {
2549
- sendSSE("rpc-response", event);
2550
- } else if ("type" in event && "sessionId" in event) {
2551
- sendSSE("connection", event);
2552
- } else {
2553
- sendSSE("observability", event);
2540
+ error: {
2541
+ code: "METHOD_NOT_ALLOWED",
2542
+ message: "Use POST /api/mcp. For streaming use Accept: text/event-stream."
2554
2543
  }
2555
- }
2544
+ },
2545
+ { status: 405 }
2556
2546
  );
2557
- managers.set(identity, manager);
2558
- sendSSE("connected", { timestamp: Date.now() });
2559
- const abortController = new AbortController();
2560
- request.signal?.addEventListener("abort", () => {
2561
- manager.dispose();
2562
- managers.delete(identity);
2563
- writer.close().catch(() => {
2564
- });
2565
- abortController.abort();
2566
- });
2567
- return new Response(stream.readable, {
2568
- status: 200,
2569
- headers: {
2570
- "Content-Type": "text/event-stream",
2571
- "Cache-Control": "no-cache, no-transform",
2572
- "Connection": "keep-alive",
2573
- "X-Accel-Buffering": "no"
2574
- }
2575
- });
2576
2547
  }
2577
2548
  async function POST(request) {
2578
2549
  const identity = getIdentity(request);
2579
2550
  const authToken = getAuthToken(request);
2551
+ const acceptsEventStream = (request.headers.get("accept") || "").toLowerCase().includes("text/event-stream");
2580
2552
  if (!identity) {
2581
2553
  return Response.json({ error: { code: "MISSING_IDENTITY", message: "Missing identity" } }, { status: 400 });
2582
2554
  }
@@ -2584,28 +2556,103 @@ data: ${JSON.stringify(data)}
2584
2556
  if (!isAuthorized) {
2585
2557
  return Response.json({ error: { code: "UNAUTHORIZED", message: "Unauthorized" } }, { status: 401 });
2586
2558
  }
2559
+ let rawBody = "";
2587
2560
  try {
2588
- const body = await request.json();
2589
- const manager = managers.get(identity);
2590
- if (!manager) {
2561
+ rawBody = await request.text();
2562
+ const body = rawBody ? JSON.parse(rawBody) : null;
2563
+ if (!body || typeof body !== "object") {
2591
2564
  return Response.json(
2592
2565
  {
2593
2566
  error: {
2594
- code: "NO_CONNECTION",
2595
- message: "No SSE connection found. Please establish SSE connection first."
2567
+ code: "INVALID_REQUEST",
2568
+ message: "Invalid JSON-RPC request body"
2596
2569
  }
2597
2570
  },
2598
2571
  { status: 400 }
2599
2572
  );
2600
2573
  }
2601
- const response = await manager.handleRequest(body);
2602
- return Response.json(response);
2574
+ const resolvedClientMetadata = await resolveClientMetadata(request);
2575
+ if (!acceptsEventStream) {
2576
+ const manager2 = new SSEConnectionManager(
2577
+ toManagerOptions(identity, resolvedClientMetadata),
2578
+ () => {
2579
+ }
2580
+ );
2581
+ try {
2582
+ const response = await manager2.handleRequest(body);
2583
+ return Response.json(response);
2584
+ } finally {
2585
+ manager2.dispose();
2586
+ }
2587
+ }
2588
+ const stream = new TransformStream();
2589
+ const writer = stream.writable.getWriter();
2590
+ const encoder = new TextEncoder();
2591
+ let streamWritable = true;
2592
+ const sendSSE = (event, data) => {
2593
+ if (!streamWritable) return;
2594
+ const message = `event: ${event}
2595
+ data: ${JSON.stringify(data)}
2596
+
2597
+ `;
2598
+ writer.write(encoder.encode(message)).catch(() => {
2599
+ streamWritable = false;
2600
+ });
2601
+ };
2602
+ const manager = new SSEConnectionManager(
2603
+ toManagerOptions(identity, resolvedClientMetadata),
2604
+ (event) => {
2605
+ if (isRpcResponseEvent(event)) {
2606
+ sendSSE("rpc-response", event);
2607
+ } else if ("type" in event && "sessionId" in event) {
2608
+ sendSSE("connection", event);
2609
+ } else {
2610
+ sendSSE("observability", event);
2611
+ }
2612
+ }
2613
+ );
2614
+ sendSSE("connected", { timestamp: Date.now() });
2615
+ void (async () => {
2616
+ try {
2617
+ await manager.handleRequest(body);
2618
+ } catch (error) {
2619
+ const err = error instanceof Error ? error : new Error("Unknown error");
2620
+ sendSSE("rpc-response", {
2621
+ id: body.id || "unknown",
2622
+ error: {
2623
+ code: "EXECUTION_ERROR",
2624
+ message: err.message
2625
+ }
2626
+ });
2627
+ } finally {
2628
+ streamWritable = false;
2629
+ manager.dispose();
2630
+ writer.close().catch(() => {
2631
+ });
2632
+ }
2633
+ })();
2634
+ return new Response(stream.readable, {
2635
+ status: 200,
2636
+ headers: {
2637
+ "Content-Type": "text/event-stream",
2638
+ "Cache-Control": "no-cache, no-transform",
2639
+ "Connection": "keep-alive",
2640
+ "X-Accel-Buffering": "no"
2641
+ }
2642
+ });
2603
2643
  } catch (error) {
2644
+ const err = error instanceof Error ? error : new Error("Unknown error");
2645
+ console.error("[MCP Next Handler] Failed to handle RPC", {
2646
+ identity,
2647
+ message: err.message,
2648
+ stack: err.stack,
2649
+ rawBody: rawBody.slice(0, 500)
2650
+ });
2604
2651
  return Response.json(
2605
2652
  {
2606
2653
  error: {
2607
2654
  code: "EXECUTION_ERROR",
2608
- message: error instanceof Error ? error.message : "Unknown error"
2655
+ message: err.message
2609
2656
  }
2610
2657
  },
2611
2658
  { status: 500 }
@@ -2614,62 +2661,27 @@ data: ${JSON.stringify(data)}
2614
2661
  }
2615
2662
  return { GET, POST };
2616
2663
  }
2617
- var DEFAULT_REQUEST_TIMEOUT = 6e4;
2618
- var MAX_RECONNECT_ATTEMPTS = 5;
2619
- var BASE_RECONNECT_DELAY = 1e3;
2620
2664
  var SSEClient = class {
2621
2665
  constructor(options) {
2622
2666
  this.options = options;
2623
- __publicField(this, "eventSource", null);
2624
- __publicField(this, "pendingRequests", /* @__PURE__ */ new Map());
2625
2667
  __publicField(this, "resourceCache", /* @__PURE__ */ new Map());
2626
- __publicField(this, "reconnectAttempts", 0);
2627
- __publicField(this, "isManuallyDisconnected", false);
2628
- __publicField(this, "connectionPromise", null);
2629
- __publicField(this, "connectionResolver", null);
2668
+ __publicField(this, "connected", false);
2630
2669
  }
2631
- // ============================================
2632
- // Connection Management
2633
- // ============================================
2634
- /**
2635
- * Connect to the SSE endpoint
2636
- */
2637
2670
  connect() {
2638
- if (this.eventSource) {
2671
+ if (this.connected) {
2639
2672
  return;
2640
2673
  }
2641
- this.isManuallyDisconnected = false;
2642
- this.options.onStatusChange?.("connecting");
2643
- this.connectionPromise = new Promise((resolve) => {
2644
- this.connectionResolver = resolve;
2645
- });
2646
- const url = this.buildUrl();
2647
- this.eventSource = new EventSource(url);
2648
- this.setupEventListeners();
2674
+ this.connected = true;
2675
+ this.options.onStatusChange?.("connected");
2676
+ this.log("RPC mode: post_stream");
2649
2677
  }
2650
- /**
2651
- * Disconnect from the SSE endpoint
2652
- */
2653
2678
  disconnect() {
2654
- this.isManuallyDisconnected = true;
2655
- if (this.eventSource) {
2656
- this.eventSource.close();
2657
- this.eventSource = null;
2658
- }
2659
- this.connectionPromise = null;
2660
- this.connectionResolver = null;
2661
- this.rejectAllPendingRequests(new Error("Connection closed"));
2679
+ this.connected = false;
2662
2680
  this.options.onStatusChange?.("disconnected");
2663
2681
  }
2664
- /**
2665
- * Check if connected to the SSE endpoint
2666
- */
2667
2682
  isConnected() {
2668
- return this.eventSource?.readyState === EventSource.OPEN;
2683
+ return this.connected;
2669
2684
  }
2670
- // ============================================
2671
- // RPC Methods
2672
- // ============================================
2673
2685
  async getSessions() {
2674
2686
  return this.sendRequest("getSessions");
2675
2687
  }
@@ -2705,22 +2717,10 @@ var SSEClient = class {
2705
2717
  async readResource(sessionId, uri) {
2706
2718
  return this.sendRequest("readResource", { sessionId, uri });
2707
2719
  }
2708
- // ============================================
2709
- // Resource Preloading (for instant UI loading)
2710
- // ============================================
2711
- /**
2712
- * Preload UI resources for tools that have UI metadata.
2713
- * Call this when tools are discovered to enable instant MCP App UI loading.
2714
- */
2715
2720
  preloadToolUiResources(sessionId, tools) {
2716
2721
  for (const tool of tools) {
2717
2722
  const uri = this.extractUiResourceUri(tool);
2718
- if (!uri) continue;
2719
- if (this.resourceCache.has(uri)) {
2720
- this.log(`Resource already cached: ${uri}`);
2721
- continue;
2722
- }
2723
- this.log(`Preloading UI resource for tool "${tool.name}": ${uri}`);
2723
+ if (!uri || this.resourceCache.has(uri)) continue;
2724
2724
  const promise = this.sendRequest("readResource", { sessionId, uri }).catch((err) => {
2725
2725
  this.log(`Failed to preload resource ${uri}: ${err.message}`, "warn");
2726
2726
  this.resourceCache.delete(uri);
@@ -2729,43 +2729,24 @@ var SSEClient = class {
2729
2729
  this.resourceCache.set(uri, promise);
2730
2730
  }
2731
2731
  }
2732
- /**
2733
- * Get a preloaded resource from cache, or fetch if not cached.
2734
- */
2735
2732
  getOrFetchResource(sessionId, uri) {
2736
2733
  const cached = this.resourceCache.get(uri);
2737
- if (cached) {
2738
- this.log(`Cache hit for resource: ${uri}`);
2739
- return cached;
2740
- }
2741
- this.log(`Cache miss, fetching resource: ${uri}`);
2734
+ if (cached) return cached;
2742
2735
  const promise = this.sendRequest("readResource", { sessionId, uri });
2743
2736
  this.resourceCache.set(uri, promise);
2744
2737
  return promise;
2745
2738
  }
2746
- /**
2747
- * Check if a resource is already cached
2748
- */
2749
2739
  hasPreloadedResource(uri) {
2750
2740
  return this.resourceCache.has(uri);
2751
2741
  }
2752
- /**
2753
- * Clear the resource cache
2754
- */
2755
2742
  clearResourceCache() {
2756
2743
  this.resourceCache.clear();
2757
2744
  }
2758
- // ============================================
2759
- // Private: Request Handling
2760
- // ============================================
2761
- /**
2762
- * Send an RPC request and return the response directly from HTTP.
2763
- * This bypasses SSE latency by returning results in the HTTP response body.
2764
- */
2765
2745
  async sendRequest(method, params) {
2766
- if (this.connectionPromise) {
2767
- await this.connectionPromise;
2746
+ if (!this.connected) {
2747
+ this.connect();
2768
2748
  }
2749
+ this.log(`RPC request via post_stream: ${method}`);
2769
2750
  const request = {
2770
2751
  id: `rpc_${nanoid(10)}`,
2771
2752
  method,
@@ -2779,103 +2760,93 @@ var SSEClient = class {
2779
2760
  if (!response.ok) {
2780
2761
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
2781
2762
  }
2782
- const data = await response.json();
2783
- return this.parseRpcResponse(data, request.id);
2763
+ const contentType = (response.headers.get("content-type") || "").toLowerCase();
2764
+ if (!contentType.includes("text/event-stream")) {
2765
+ const data2 = await response.json();
2766
+ return this.parseRpcResponse(data2);
2767
+ }
2768
+ const data = await this.readRpcResponseFromStream(response);
2769
+ return this.parseRpcResponse(data);
2770
+ }
2771
+ async readRpcResponseFromStream(response) {
2772
+ if (!response.body) {
2773
+ throw new Error("Streaming response body is missing");
2774
+ }
2775
+ const reader = response.body.getReader();
2776
+ const decoder = new TextDecoder();
2777
+ let buffer = "";
2778
+ let rpcResponse = null;
2779
+ const dispatchBlock = (block) => {
2780
+ const lines = block.split("\n");
2781
+ let eventName = "message";
2782
+ const dataLines = [];
2783
+ for (const rawLine of lines) {
2784
+ const line = rawLine.replace(/\r$/, "");
2785
+ if (!line || line.startsWith(":")) continue;
2786
+ if (line.startsWith("event:")) {
2787
+ eventName = line.slice("event:".length).trim();
2788
+ continue;
2789
+ }
2790
+ if (line.startsWith("data:")) {
2791
+ dataLines.push(line.slice("data:".length).trimStart());
2792
+ }
2793
+ }
2794
+ if (!dataLines.length) return;
2795
+ const payloadText = dataLines.join("\n");
2796
+ let payload = payloadText;
2797
+ try {
2798
+ payload = JSON.parse(payloadText);
2799
+ } catch {
2800
+ }
2801
+ switch (eventName) {
2802
+ case "connected":
2803
+ this.options.onStatusChange?.("connected");
2804
+ break;
2805
+ case "connection":
2806
+ this.options.onConnectionEvent?.(payload);
2807
+ break;
2808
+ case "observability":
2809
+ this.options.onObservabilityEvent?.(payload);
2810
+ break;
2811
+ case "rpc-response":
2812
+ rpcResponse = payload;
2813
+ break;
2814
+ }
2815
+ };
2816
+ while (true) {
2817
+ const { value, done } = await reader.read();
2818
+ if (done) break;
2819
+ buffer += decoder.decode(value, { stream: true });
2820
+ let separatorMatch = buffer.match(/\r?\n\r?\n/);
2821
+ while (separatorMatch && separatorMatch.index !== void 0) {
2822
+ const separatorIndex = separatorMatch.index;
2823
+ const separatorLength = separatorMatch[0].length;
2824
+ const block = buffer.slice(0, separatorIndex);
2825
+ buffer = buffer.slice(separatorIndex + separatorLength);
2826
+ dispatchBlock(block);
2827
+ separatorMatch = buffer.match(/\r?\n\r?\n/);
2828
+ }
2829
+ }
2830
+ if (buffer.trim()) {
2831
+ dispatchBlock(buffer);
2832
+ }
2833
+ if (!rpcResponse) {
2834
+ throw new Error("Missing rpc-response event in streamed RPC result");
2835
+ }
2836
+ return rpcResponse;
2784
2837
  }
2785
- /**
2786
- * Parse RPC response and handle different response formats
2787
- */
2788
- parseRpcResponse(data, requestId) {
2838
+ parseRpcResponse(data) {
2789
2839
  if ("result" in data) {
2790
2840
  return data.result;
2791
2841
  }
2792
2842
  if ("error" in data && data.error) {
2793
2843
  throw new Error(data.error.message || "Unknown RPC error");
2794
2844
  }
2795
- if ("acknowledged" in data) {
2796
- return this.waitForSseResponse(requestId);
2845
+ if (data && typeof data === "object" && "id" in data) {
2846
+ return void 0;
2797
2847
  }
2798
2848
  throw new Error("Invalid RPC response format");
2799
2849
  }
2800
- /**
2801
- * Wait for RPC response via SSE (legacy fallback)
2802
- */
2803
- waitForSseResponse(requestId) {
2804
- const timeoutMs = this.options.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT;
2805
- return new Promise((resolve, reject) => {
2806
- const timeoutId = setTimeout(() => {
2807
- this.pendingRequests.delete(requestId);
2808
- reject(new Error(`Request timeout after ${timeoutMs}ms`));
2809
- }, timeoutMs);
2810
- this.pendingRequests.set(requestId, {
2811
- resolve,
2812
- reject,
2813
- timeoutId
2814
- });
2815
- });
2816
- }
2817
- /**
2818
- * Handle RPC response received via SSE (legacy)
2819
- */
2820
- handleRpcResponse(response) {
2821
- const pending = this.pendingRequests.get(response.id);
2822
- if (!pending) return;
2823
- clearTimeout(pending.timeoutId);
2824
- this.pendingRequests.delete(response.id);
2825
- if (response.error) {
2826
- pending.reject(new Error(response.error.message));
2827
- } else {
2828
- pending.resolve(response.result);
2829
- }
2830
- }
2831
- // ============================================
2832
- // Private: Event Handling
2833
- // ============================================
2834
- setupEventListeners() {
2835
- if (!this.eventSource) return;
2836
- this.eventSource.addEventListener("open", () => {
2837
- this.log("Connected");
2838
- this.reconnectAttempts = 0;
2839
- this.options.onStatusChange?.("connected");
2840
- });
2841
- this.eventSource.addEventListener("connected", () => {
2842
- this.log("Server ready");
2843
- this.connectionResolver?.();
2844
- this.connectionResolver = null;
2845
- });
2846
- this.eventSource.addEventListener("connection", (e) => {
2847
- const event = JSON.parse(e.data);
2848
- this.options.onConnectionEvent?.(event);
2849
- });
2850
- this.eventSource.addEventListener("observability", (e) => {
2851
- const event = JSON.parse(e.data);
2852
- this.options.onObservabilityEvent?.(event);
2853
- });
2854
- this.eventSource.addEventListener("rpc-response", (e) => {
2855
- const response = JSON.parse(e.data);
2856
- this.handleRpcResponse(response);
2857
- });
2858
- this.eventSource.addEventListener("error", () => {
2859
- this.log("Connection error", "error");
2860
- this.options.onStatusChange?.("error");
2861
- this.attemptReconnect();
2862
- });
2863
- }
2864
- attemptReconnect() {
2865
- if (this.isManuallyDisconnected || this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
2866
- return;
2867
- }
2868
- this.reconnectAttempts++;
2869
- const delay = BASE_RECONNECT_DELAY * this.reconnectAttempts;
2870
- this.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`);
2871
- setTimeout(() => {
2872
- this.disconnect();
2873
- this.connect();
2874
- }, delay);
2875
- }
2876
- // ============================================
2877
- // Private: Utilities
2878
- // ============================================
2879
2850
  buildUrl() {
2880
2851
  const url = new URL(this.options.url, globalThis.location?.origin);
2881
2852
  url.searchParams.set("identity", this.options.identity);
@@ -2886,20 +2857,14 @@ var SSEClient = class {
2886
2857
  }
2887
2858
  buildHeaders() {
2888
2859
  const headers = {
2889
- "Content-Type": "application/json"
2860
+ "Content-Type": "application/json",
2861
+ "Accept": "text/event-stream"
2890
2862
  };
2891
2863
  if (this.options.authToken) {
2892
2864
  headers["Authorization"] = `Bearer ${this.options.authToken}`;
2893
2865
  }
2894
2866
  return headers;
2895
2867
  }
2896
- rejectAllPendingRequests(error) {
2897
- for (const [, pending] of this.pendingRequests) {
2898
- clearTimeout(pending.timeoutId);
2899
- pending.reject(error);
2900
- }
2901
- this.pendingRequests.clear();
2902
- }
2903
2868
  extractUiResourceUri(tool) {
2904
2869
  const meta = tool._meta?.ui;
2905
2870
  if (!meta || typeof meta !== "object") return void 0;