@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
@@ -189,6 +189,7 @@ declare class MCPClient {
189
189
  * Saves current session state to storage
190
190
  * Creates new session if it doesn't exist, updates if it does
191
191
  * @param ttl - Time-to-live in seconds (defaults to 12hr for connected sessions)
192
+ * @param active - Session status marker used to avoid unnecessary TTL rewrites
192
193
  * @private
193
194
  */
194
195
  private saveSession;
@@ -189,6 +189,7 @@ declare class MCPClient {
189
189
  * Saves current session state to storage
190
190
  * Creates new session if it doesn't exist, updates if it does
191
191
  * @param ttl - Time-to-live in seconds (defaults to 12hr for connected sessions)
192
+ * @param active - Session status marker used to avoid unnecessary TTL rewrites
192
193
  * @private
193
194
  */
194
195
  private saveSession;
@@ -1,11 +1,11 @@
1
- export { M as MCPClient, a as MultiSessionClient, S as StorageOAuthClientProvider } from '../multi-session-client-B1DBx5yR.mjs';
1
+ export { M as MCPClient, a as MultiSessionClient, S as StorageOAuthClientProvider } from '../multi-session-client-DzjmT7FX.mjs';
2
2
  export { U as UnauthorizedError, s as sanitizeServerLabel } from '../utils-0qmYrqoa.mjs';
3
3
  import { OAuthClientInformationMixed, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
4
4
  export { OAuthClientInformation, OAuthClientInformationFull, OAuthClientMetadata, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
5
5
  import { M as McpConnectionEvent, d as McpObservabilityEvent } from '../events-CK3N--3g.mjs';
6
6
  export { D as Disposable, E as Emitter, b as Event, c as McpConnectionState } from '../events-CK3N--3g.mjs';
7
- import { q as McpRpcResponse, p as McpRpcRequest } from '../types-PjM1W07s.mjs';
8
- export { a as CallToolRequest, b as CallToolResponse, f as ConnectRequest, g as ConnectResponse, m as ListToolsResponse, T as ToolInfo } from '../types-PjM1W07s.mjs';
7
+ import { q as McpRpcResponse, p as McpRpcRequest } from '../types-CW6lghof.mjs';
8
+ export { a as CallToolRequest, b as CallToolResponse, f as ConnectRequest, g as ConnectResponse, m as ListToolsResponse, T as ToolInfo } from '../types-CW6lghof.mjs';
9
9
  export { CallToolResult, ListToolsResult, Tool } from '@modelcontextprotocol/sdk/types.js';
10
10
  import '@modelcontextprotocol/sdk/client/auth.js';
11
11
 
@@ -19,6 +19,13 @@ interface SessionData {
19
19
  createdAt: number;
20
20
  identity: string;
21
21
  headers?: Record<string, string>;
22
+ /**
23
+ * Session status marker used for TTL transitions:
24
+ * - false: short-lived intermediate/error/auth-pending session state
25
+ * (keep this value when connection/auth is incomplete or failed)
26
+ * - true: active long-lived session state after successful connection/auth completion
27
+ */
28
+ active?: boolean;
22
29
  clientInformation?: OAuthClientInformationMixed;
23
30
  tokens?: OAuthTokens;
24
31
  codeVerifier?: string;
@@ -220,8 +227,10 @@ declare function createSSEHandler(options: SSEHandlerOptions): (req: {
220
227
  }) => Promise<void>;
221
228
 
222
229
  /**
223
- * Next.js App Router Handler for MCP SSE
224
- * Provides a clean, zero-boilerplate API for Next.js applications
230
+ * Next.js App Router Handler for MCP
231
+ * Stateless transport for serverless environments:
232
+ * - POST + `Accept: text/event-stream` streams progress + rpc-response
233
+ * - POST + JSON accepts direct RPC result response
225
234
  */
226
235
 
227
236
  interface NextMcpHandlerOptions {
@@ -244,29 +253,15 @@ interface NextMcpHandlerOptions {
244
253
  heartbeatInterval?: number;
245
254
  /**
246
255
  * Static OAuth client metadata defaults (for all connections)
247
- * Use this for single-tenant applications with fixed branding
248
256
  */
249
257
  clientDefaults?: ClientMetadata;
250
258
  /**
251
- * Dynamic OAuth client metadata getter (per-request, useful for multi-tenant)
252
- * Use this when you need different branding based on request (tenant, domain, etc.)
253
- * Takes precedence over clientDefaults
259
+ * Dynamic OAuth client metadata getter (per-request)
254
260
  */
255
261
  getClientMetadata?: (request: Request) => ClientMetadata | Promise<ClientMetadata>;
256
262
  }
257
- /**
258
- * Creates Next.js App Router handlers (GET and POST) for MCP SSE endpoint
259
- *
260
- * @example
261
- * ```typescript
262
- * // app/api/mcp/route.ts
263
- * import { createNextMcpHandler } from '@mcp-ts/core/server';
264
- *
265
- * export const { GET, POST } = createNextMcpHandler();
266
- * ```
267
- */
268
263
  declare function createNextMcpHandler(options?: NextMcpHandlerOptions): {
269
- GET: (request: Request) => Promise<Response>;
264
+ GET: () => Promise<Response>;
270
265
  POST: (request: Request) => Promise<Response>;
271
266
  };
272
267
 
@@ -1,11 +1,11 @@
1
- export { M as MCPClient, a as MultiSessionClient, S as StorageOAuthClientProvider } from '../multi-session-client-DyFzyJUx.js';
1
+ export { M as MCPClient, a as MultiSessionClient, S as StorageOAuthClientProvider } from '../multi-session-client-FAFpUzZ4.js';
2
2
  export { U as UnauthorizedError, s as sanitizeServerLabel } from '../utils-0qmYrqoa.js';
3
3
  import { OAuthClientInformationMixed, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
4
4
  export { OAuthClientInformation, OAuthClientInformationFull, OAuthClientMetadata, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
5
5
  import { M as McpConnectionEvent, d as McpObservabilityEvent } from '../events-CK3N--3g.js';
6
6
  export { D as Disposable, E as Emitter, b as Event, c as McpConnectionState } from '../events-CK3N--3g.js';
7
- import { q as McpRpcResponse, p as McpRpcRequest } from '../types-PjM1W07s.js';
8
- export { a as CallToolRequest, b as CallToolResponse, f as ConnectRequest, g as ConnectResponse, m as ListToolsResponse, T as ToolInfo } from '../types-PjM1W07s.js';
7
+ import { q as McpRpcResponse, p as McpRpcRequest } from '../types-CW6lghof.js';
8
+ export { a as CallToolRequest, b as CallToolResponse, f as ConnectRequest, g as ConnectResponse, m as ListToolsResponse, T as ToolInfo } from '../types-CW6lghof.js';
9
9
  export { CallToolResult, ListToolsResult, Tool } from '@modelcontextprotocol/sdk/types.js';
10
10
  import '@modelcontextprotocol/sdk/client/auth.js';
11
11
 
@@ -19,6 +19,13 @@ interface SessionData {
19
19
  createdAt: number;
20
20
  identity: string;
21
21
  headers?: Record<string, string>;
22
+ /**
23
+ * Session status marker used for TTL transitions:
24
+ * - false: short-lived intermediate/error/auth-pending session state
25
+ * (keep this value when connection/auth is incomplete or failed)
26
+ * - true: active long-lived session state after successful connection/auth completion
27
+ */
28
+ active?: boolean;
22
29
  clientInformation?: OAuthClientInformationMixed;
23
30
  tokens?: OAuthTokens;
24
31
  codeVerifier?: string;
@@ -220,8 +227,10 @@ declare function createSSEHandler(options: SSEHandlerOptions): (req: {
220
227
  }) => Promise<void>;
221
228
 
222
229
  /**
223
- * Next.js App Router Handler for MCP SSE
224
- * Provides a clean, zero-boilerplate API for Next.js applications
230
+ * Next.js App Router Handler for MCP
231
+ * Stateless transport for serverless environments:
232
+ * - POST + `Accept: text/event-stream` streams progress + rpc-response
233
+ * - POST + JSON accepts direct RPC result response
225
234
  */
226
235
 
227
236
  interface NextMcpHandlerOptions {
@@ -244,29 +253,15 @@ interface NextMcpHandlerOptions {
244
253
  heartbeatInterval?: number;
245
254
  /**
246
255
  * Static OAuth client metadata defaults (for all connections)
247
- * Use this for single-tenant applications with fixed branding
248
256
  */
249
257
  clientDefaults?: ClientMetadata;
250
258
  /**
251
- * Dynamic OAuth client metadata getter (per-request, useful for multi-tenant)
252
- * Use this when you need different branding based on request (tenant, domain, etc.)
253
- * Takes precedence over clientDefaults
259
+ * Dynamic OAuth client metadata getter (per-request)
254
260
  */
255
261
  getClientMetadata?: (request: Request) => ClientMetadata | Promise<ClientMetadata>;
256
262
  }
257
- /**
258
- * Creates Next.js App Router handlers (GET and POST) for MCP SSE endpoint
259
- *
260
- * @example
261
- * ```typescript
262
- * // app/api/mcp/route.ts
263
- * import { createNextMcpHandler } from '@mcp-ts/core/server';
264
- *
265
- * export const { GET, POST } = createNextMcpHandler();
266
- * ```
267
- */
268
263
  declare function createNextMcpHandler(options?: NextMcpHandlerOptions): {
269
- GET: (request: Request) => Promise<Response>;
264
+ GET: () => Promise<Response>;
270
265
  POST: (request: Request) => Promise<Response>;
271
266
  };
272
267
 
@@ -1254,7 +1254,8 @@ var MCPClient = class _MCPClient {
1254
1254
  serverUrl: this.serverUrl,
1255
1255
  callbackUrl: this.callbackUrl,
1256
1256
  transportType: this.transportType || "streamable_http",
1257
- createdAt: this.createdAt
1257
+ createdAt: this.createdAt,
1258
+ active: false
1258
1259
  }, Math.floor(STATE_EXPIRATION_MS / 1e3));
1259
1260
  }
1260
1261
  }
@@ -1262,9 +1263,10 @@ var MCPClient = class _MCPClient {
1262
1263
  * Saves current session state to storage
1263
1264
  * Creates new session if it doesn't exist, updates if it does
1264
1265
  * @param ttl - Time-to-live in seconds (defaults to 12hr for connected sessions)
1266
+ * @param active - Session status marker used to avoid unnecessary TTL rewrites
1265
1267
  * @private
1266
1268
  */
1267
- async saveSession(ttl = SESSION_TTL_SECONDS) {
1269
+ async saveSession(ttl = SESSION_TTL_SECONDS, active = true) {
1268
1270
  if (!this.sessionId || !this.serverId || !this.serverUrl || !this.callbackUrl) {
1269
1271
  return;
1270
1272
  }
@@ -1276,7 +1278,8 @@ var MCPClient = class _MCPClient {
1276
1278
  serverUrl: this.serverUrl,
1277
1279
  callbackUrl: this.callbackUrl,
1278
1280
  transportType: this.transportType || "streamable_http",
1279
- createdAt: this.createdAt || Date.now()
1281
+ createdAt: this.createdAt || Date.now(),
1282
+ active
1280
1283
  };
1281
1284
  const existingSession = await storage.getSession(this.identity, this.sessionId);
1282
1285
  if (existingSession) {
@@ -1350,15 +1353,17 @@ var MCPClient = class _MCPClient {
1350
1353
  this.emitStateChange("CONNECTED");
1351
1354
  this.emitProgress("Connected successfully");
1352
1355
  const existingSession = await storage.getSession(this.identity, this.sessionId);
1353
- if (!existingSession || existingSession.transportType !== this.transportType) {
1354
- console.log(`[MCPClient] Saving session ${this.sessionId} (new or transport changed)`);
1355
- await this.saveSession(SESSION_TTL_SECONDS);
1356
+ const needsTransportUpdate = !existingSession || existingSession.transportType !== this.transportType;
1357
+ const needsTtlPromotion = !existingSession || existingSession.active !== true;
1358
+ if (needsTransportUpdate || needsTtlPromotion) {
1359
+ console.log(`[MCPClient] Saving session ${this.sessionId} with 12hr TTL (connect success)`);
1360
+ await this.saveSession(SESSION_TTL_SECONDS, true);
1356
1361
  }
1357
1362
  } catch (error) {
1358
1363
  if (error instanceof auth_js.UnauthorizedError || error instanceof Error && error.message.toLowerCase().includes("unauthorized")) {
1359
1364
  this.emitStateChange("AUTHENTICATING");
1360
1365
  console.log(`[MCPClient] Saving session ${this.sessionId} with 10min TTL (OAuth pending)`);
1361
- await this.saveSession(Math.floor(STATE_EXPIRATION_MS / 1e3));
1366
+ await this.saveSession(Math.floor(STATE_EXPIRATION_MS / 1e3), false);
1362
1367
  let authUrl = "";
1363
1368
  if (this.oauthProvider) {
1364
1369
  authUrl = this.oauthProvider.authUrl || "";
@@ -1436,7 +1441,7 @@ var MCPClient = class _MCPClient {
1436
1441
  await this.client.connect(this.transport);
1437
1442
  this.emitStateChange("CONNECTED");
1438
1443
  console.log(`[MCPClient] Updating session ${this.sessionId} to 12hr TTL (OAuth complete)`);
1439
- await this.saveSession(SESSION_TTL_SECONDS);
1444
+ await this.saveSession(SESSION_TTL_SECONDS, true);
1440
1445
  return;
1441
1446
  } catch (error) {
1442
1447
  lastError = error;
@@ -2101,7 +2106,8 @@ var SSEConnectionManager = class {
2101
2106
  serverName: s.serverName,
2102
2107
  serverUrl: s.serverUrl,
2103
2108
  transport: s.transportType,
2104
- createdAt: s.createdAt
2109
+ createdAt: s.createdAt,
2110
+ active: s.active !== false
2105
2111
  }))
2106
2112
  };
2107
2113
  }
@@ -2116,6 +2122,13 @@ var SSEConnectionManager = class {
2116
2122
  (s) => s.serverId === serverId || s.serverUrl === serverUrl
2117
2123
  );
2118
2124
  if (duplicate) {
2125
+ if (duplicate.active === false) {
2126
+ await this.restoreSession({ sessionId: duplicate.sessionId });
2127
+ return {
2128
+ sessionId: duplicate.sessionId,
2129
+ success: true
2130
+ };
2131
+ }
2119
2132
  throw new Error(`Connection already exists for server: ${duplicate.serverUrl || duplicate.serverId} (${duplicate.serverName})`);
2120
2133
  }
2121
2134
  const sessionId = await storage.generateSessionId();
@@ -2441,7 +2454,9 @@ function writeSSEEvent(res, event, data) {
2441
2454
  }
2442
2455
 
2443
2456
  // src/server/handlers/nextjs-handler.ts
2444
- var managers = /* @__PURE__ */ new Map();
2457
+ function isRpcResponseEvent(event) {
2458
+ return "id" in event && ("result" in event || "error" in event);
2459
+ }
2445
2460
  function createNextMcpHandler(options = {}) {
2446
2461
  const {
2447
2462
  getIdentity = (request) => new URL(request.url).searchParams.get("identity"),
@@ -2454,72 +2469,29 @@ function createNextMcpHandler(options = {}) {
2454
2469
  clientDefaults,
2455
2470
  getClientMetadata
2456
2471
  } = options;
2457
- async function GET(request) {
2458
- const identity = getIdentity(request);
2459
- const authToken = getAuthToken(request);
2460
- if (!identity) {
2461
- return new Response("Missing identity", { status: 400 });
2462
- }
2463
- const isAuthorized = await authenticate(identity, authToken);
2464
- if (!isAuthorized) {
2465
- return new Response("Unauthorized", { status: 401 });
2466
- }
2467
- const stream = new TransformStream();
2468
- const writer = stream.writable.getWriter();
2469
- const encoder = new TextEncoder();
2470
- const sendSSE = (event, data) => {
2471
- const message = `event: ${event}
2472
- data: ${JSON.stringify(data)}
2473
-
2474
- `;
2475
- writer.write(encoder.encode(message)).catch(() => {
2476
- });
2477
- };
2478
- const previousManager = managers.get(identity);
2479
- if (previousManager) {
2480
- previousManager.dispose();
2481
- }
2482
- const resolvedClientMetadata = getClientMetadata ? await getClientMetadata(request) : clientDefaults;
2483
- const manager = new SSEConnectionManager(
2472
+ const toManagerOptions = (identity, resolvedClientMetadata) => ({
2473
+ identity,
2474
+ heartbeatInterval,
2475
+ clientDefaults: resolvedClientMetadata
2476
+ });
2477
+ async function resolveClientMetadata(request) {
2478
+ return getClientMetadata ? await getClientMetadata(request) : clientDefaults;
2479
+ }
2480
+ async function GET() {
2481
+ return Response.json(
2484
2482
  {
2485
- identity,
2486
- heartbeatInterval,
2487
- clientDefaults: resolvedClientMetadata
2488
- // Pass resolved metadata
2489
- },
2490
- (event) => {
2491
- if ("id" in event) {
2492
- sendSSE("rpc-response", event);
2493
- } else if ("type" in event && "sessionId" in event) {
2494
- sendSSE("connection", event);
2495
- } else {
2496
- sendSSE("observability", event);
2483
+ error: {
2484
+ code: "METHOD_NOT_ALLOWED",
2485
+ message: "Use POST /api/mcp. For streaming use Accept: text/event-stream."
2497
2486
  }
2498
- }
2487
+ },
2488
+ { status: 405 }
2499
2489
  );
2500
- managers.set(identity, manager);
2501
- sendSSE("connected", { timestamp: Date.now() });
2502
- const abortController = new AbortController();
2503
- request.signal?.addEventListener("abort", () => {
2504
- manager.dispose();
2505
- managers.delete(identity);
2506
- writer.close().catch(() => {
2507
- });
2508
- abortController.abort();
2509
- });
2510
- return new Response(stream.readable, {
2511
- status: 200,
2512
- headers: {
2513
- "Content-Type": "text/event-stream",
2514
- "Cache-Control": "no-cache, no-transform",
2515
- "Connection": "keep-alive",
2516
- "X-Accel-Buffering": "no"
2517
- }
2518
- });
2519
2490
  }
2520
2491
  async function POST(request) {
2521
2492
  const identity = getIdentity(request);
2522
2493
  const authToken = getAuthToken(request);
2494
+ const acceptsEventStream = (request.headers.get("accept") || "").toLowerCase().includes("text/event-stream");
2523
2495
  if (!identity) {
2524
2496
  return Response.json({ error: { code: "MISSING_IDENTITY", message: "Missing identity" } }, { status: 400 });
2525
2497
  }
@@ -2527,28 +2499,103 @@ data: ${JSON.stringify(data)}
2527
2499
  if (!isAuthorized) {
2528
2500
  return Response.json({ error: { code: "UNAUTHORIZED", message: "Unauthorized" } }, { status: 401 });
2529
2501
  }
2502
+ let rawBody = "";
2530
2503
  try {
2531
- const body = await request.json();
2532
- const manager = managers.get(identity);
2533
- if (!manager) {
2504
+ rawBody = await request.text();
2505
+ const body = rawBody ? JSON.parse(rawBody) : null;
2506
+ if (!body || typeof body !== "object") {
2534
2507
  return Response.json(
2535
2508
  {
2536
2509
  error: {
2537
- code: "NO_CONNECTION",
2538
- message: "No SSE connection found. Please establish SSE connection first."
2510
+ code: "INVALID_REQUEST",
2511
+ message: "Invalid JSON-RPC request body"
2539
2512
  }
2540
2513
  },
2541
2514
  { status: 400 }
2542
2515
  );
2543
2516
  }
2544
- const response = await manager.handleRequest(body);
2545
- return Response.json(response);
2517
+ const resolvedClientMetadata = await resolveClientMetadata(request);
2518
+ if (!acceptsEventStream) {
2519
+ const manager2 = new SSEConnectionManager(
2520
+ toManagerOptions(identity, resolvedClientMetadata),
2521
+ () => {
2522
+ }
2523
+ );
2524
+ try {
2525
+ const response = await manager2.handleRequest(body);
2526
+ return Response.json(response);
2527
+ } finally {
2528
+ manager2.dispose();
2529
+ }
2530
+ }
2531
+ const stream = new TransformStream();
2532
+ const writer = stream.writable.getWriter();
2533
+ const encoder = new TextEncoder();
2534
+ let streamWritable = true;
2535
+ const sendSSE = (event, data) => {
2536
+ if (!streamWritable) return;
2537
+ const message = `event: ${event}
2538
+ data: ${JSON.stringify(data)}
2539
+
2540
+ `;
2541
+ writer.write(encoder.encode(message)).catch(() => {
2542
+ streamWritable = false;
2543
+ });
2544
+ };
2545
+ const manager = new SSEConnectionManager(
2546
+ toManagerOptions(identity, resolvedClientMetadata),
2547
+ (event) => {
2548
+ if (isRpcResponseEvent(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);
2554
+ }
2555
+ }
2556
+ );
2557
+ sendSSE("connected", { timestamp: Date.now() });
2558
+ void (async () => {
2559
+ try {
2560
+ await manager.handleRequest(body);
2561
+ } catch (error) {
2562
+ const err = error instanceof Error ? error : new Error("Unknown error");
2563
+ sendSSE("rpc-response", {
2564
+ id: body.id || "unknown",
2565
+ error: {
2566
+ code: "EXECUTION_ERROR",
2567
+ message: err.message
2568
+ }
2569
+ });
2570
+ } finally {
2571
+ streamWritable = false;
2572
+ manager.dispose();
2573
+ writer.close().catch(() => {
2574
+ });
2575
+ }
2576
+ })();
2577
+ return new Response(stream.readable, {
2578
+ status: 200,
2579
+ headers: {
2580
+ "Content-Type": "text/event-stream",
2581
+ "Cache-Control": "no-cache, no-transform",
2582
+ "Connection": "keep-alive",
2583
+ "X-Accel-Buffering": "no"
2584
+ }
2585
+ });
2546
2586
  } catch (error) {
2587
+ const err = error instanceof Error ? error : new Error("Unknown error");
2588
+ console.error("[MCP Next Handler] Failed to handle RPC", {
2589
+ identity,
2590
+ message: err.message,
2591
+ stack: err.stack,
2592
+ rawBody: rawBody.slice(0, 500)
2593
+ });
2547
2594
  return Response.json(
2548
2595
  {
2549
2596
  error: {
2550
2597
  code: "EXECUTION_ERROR",
2551
- message: error instanceof Error ? error.message : "Unknown error"
2598
+ message: err.message
2552
2599
  }
2553
2600
  },
2554
2601
  { status: 500 }