@mcp-ts/sdk 1.3.1 → 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 (63) hide show
  1. package/README.md +371 -290
  2. package/dist/adapters/agui-adapter.d.mts +3 -3
  3. package/dist/adapters/agui-adapter.d.ts +3 -3
  4. package/dist/adapters/agui-middleware.d.mts +3 -3
  5. package/dist/adapters/agui-middleware.d.ts +3 -3
  6. package/dist/adapters/ai-adapter.d.mts +3 -3
  7. package/dist/adapters/ai-adapter.d.ts +3 -3
  8. package/dist/adapters/langchain-adapter.d.mts +3 -3
  9. package/dist/adapters/langchain-adapter.d.ts +3 -3
  10. package/dist/adapters/mastra-adapter.d.mts +3 -3
  11. package/dist/adapters/mastra-adapter.d.ts +3 -3
  12. package/dist/client/index.d.mts +10 -66
  13. package/dist/client/index.d.ts +10 -66
  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 +15 -5
  19. package/dist/client/react.d.ts +15 -5
  20. package/dist/client/react.js +130 -182
  21. package/dist/client/react.js.map +1 -1
  22. package/dist/client/react.mjs +130 -182
  23. package/dist/client/react.mjs.map +1 -1
  24. package/dist/client/vue.d.mts +27 -7
  25. package/dist/client/vue.d.ts +27 -7
  26. package/dist/client/vue.js +131 -182
  27. package/dist/client/vue.js.map +1 -1
  28. package/dist/client/vue.mjs +131 -182
  29. package/dist/client/vue.mjs.map +1 -1
  30. package/dist/{events-BgeztGYZ.d.mts → events-CK3N--3g.d.mts} +2 -0
  31. package/dist/{events-BgeztGYZ.d.ts → events-CK3N--3g.d.ts} +2 -0
  32. package/dist/index.d.mts +3 -3
  33. package/dist/index.d.ts +3 -3
  34. package/dist/index.js +224 -258
  35. package/dist/index.js.map +1 -1
  36. package/dist/index.mjs +224 -258
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/{multi-session-client-CxogNckF.d.mts → multi-session-client-DzjmT7FX.d.mts} +4 -10
  39. package/dist/{multi-session-client-cox_WXUj.d.ts → multi-session-client-FAFpUzZ4.d.ts} +4 -10
  40. package/dist/server/index.d.mts +18 -23
  41. package/dist/server/index.d.ts +18 -23
  42. package/dist/server/index.js +133 -85
  43. package/dist/server/index.js.map +1 -1
  44. package/dist/server/index.mjs +133 -85
  45. package/dist/server/index.mjs.map +1 -1
  46. package/dist/shared/index.d.mts +3 -3
  47. package/dist/shared/index.d.ts +3 -3
  48. package/dist/shared/index.js.map +1 -1
  49. package/dist/shared/index.mjs.map +1 -1
  50. package/dist/{types-CLccx9wW.d.mts → types-CW6lghof.d.mts} +6 -0
  51. package/dist/{types-CLccx9wW.d.ts → types-CW6lghof.d.ts} +6 -0
  52. package/package.json +1 -1
  53. package/src/client/core/sse-client.ts +354 -493
  54. package/src/client/react/index.ts +16 -16
  55. package/src/client/react/use-mcp-apps.tsx +214 -214
  56. package/src/client/react/use-mcp.ts +84 -19
  57. package/src/client/vue/use-mcp.ts +119 -44
  58. package/src/server/handlers/nextjs-handler.ts +207 -217
  59. package/src/server/handlers/sse-handler.ts +14 -0
  60. package/src/server/mcp/oauth-client.ts +48 -46
  61. package/src/server/storage/types.ts +12 -5
  62. package/src/shared/events.ts +2 -0
  63. package/src/shared/types.ts +6 -0
@@ -1,7 +1,7 @@
1
- import { b as Event, M as McpConnectionEvent, d as McpObservabilityEvent, c as McpConnectionState } from './events-BgeztGYZ.mjs';
1
+ import { b as Event, M as McpConnectionEvent, d as McpObservabilityEvent, c as McpConnectionState } from './events-CK3N--3g.mjs';
2
2
  import { ListToolsResult, CallToolResult, ListPromptsResult, GetPromptResult, ListResourcesResult, ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';
3
- import { OAuthClientMetadata, OAuthClientInformation, OAuthClientInformationFull, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
4
3
  import { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js';
4
+ import { OAuthClientMetadata, OAuthClientInformation, OAuthClientInformationFull, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
5
5
 
6
6
  /**
7
7
  * Extension of OAuthClientProvider interface with additional methods
@@ -105,12 +105,8 @@ interface MCPOAuthClientOptions {
105
105
  serverId?: string; /** Optional - loaded from session if not provided */
106
106
  sessionId: string; /** Required - primary key for session lookup */
107
107
  transportType?: TransportType;
108
- tokens?: OAuthTokens;
109
- tokenExpiresAt?: number;
110
- clientInformation?: OAuthClientInformationFull;
111
108
  clientId?: string;
112
109
  clientSecret?: string;
113
- onSaveTokens?: (tokens: OAuthTokens) => void;
114
110
  headers?: Record<string, string>;
115
111
  /** OAuth Client Metadata (optional - user application info) */
116
112
  clientName?: string;
@@ -135,18 +131,15 @@ declare class MCPClient {
135
131
  private serverUrl;
136
132
  private callbackUrl;
137
133
  private onRedirect;
138
- private tokens?;
139
- private tokenExpiresAt?;
140
- private clientInformation?;
141
134
  private clientId?;
142
135
  private clientSecret?;
143
- private onSaveTokens?;
144
136
  private headers?;
145
137
  /** OAuth Client Metadata */
146
138
  private clientName?;
147
139
  private clientUri?;
148
140
  private logoUri?;
149
141
  private policyUri?;
142
+ private createdAt?;
150
143
  /** Event emitters for connection lifecycle */
151
144
  private readonly _onConnectionEvent;
152
145
  readonly onConnectionEvent: Event<McpConnectionEvent>;
@@ -196,6 +189,7 @@ declare class MCPClient {
196
189
  * Saves current session state to storage
197
190
  * Creates new session if it doesn't exist, updates if it does
198
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
199
193
  * @private
200
194
  */
201
195
  private saveSession;
@@ -1,7 +1,7 @@
1
- import { b as Event, M as McpConnectionEvent, d as McpObservabilityEvent, c as McpConnectionState } from './events-BgeztGYZ.js';
1
+ import { b as Event, M as McpConnectionEvent, d as McpObservabilityEvent, c as McpConnectionState } from './events-CK3N--3g.js';
2
2
  import { ListToolsResult, CallToolResult, ListPromptsResult, GetPromptResult, ListResourcesResult, ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';
3
- import { OAuthClientMetadata, OAuthClientInformation, OAuthClientInformationFull, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
4
3
  import { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js';
4
+ import { OAuthClientMetadata, OAuthClientInformation, OAuthClientInformationFull, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
5
5
 
6
6
  /**
7
7
  * Extension of OAuthClientProvider interface with additional methods
@@ -105,12 +105,8 @@ interface MCPOAuthClientOptions {
105
105
  serverId?: string; /** Optional - loaded from session if not provided */
106
106
  sessionId: string; /** Required - primary key for session lookup */
107
107
  transportType?: TransportType;
108
- tokens?: OAuthTokens;
109
- tokenExpiresAt?: number;
110
- clientInformation?: OAuthClientInformationFull;
111
108
  clientId?: string;
112
109
  clientSecret?: string;
113
- onSaveTokens?: (tokens: OAuthTokens) => void;
114
110
  headers?: Record<string, string>;
115
111
  /** OAuth Client Metadata (optional - user application info) */
116
112
  clientName?: string;
@@ -135,18 +131,15 @@ declare class MCPClient {
135
131
  private serverUrl;
136
132
  private callbackUrl;
137
133
  private onRedirect;
138
- private tokens?;
139
- private tokenExpiresAt?;
140
- private clientInformation?;
141
134
  private clientId?;
142
135
  private clientSecret?;
143
- private onSaveTokens?;
144
136
  private headers?;
145
137
  /** OAuth Client Metadata */
146
138
  private clientName?;
147
139
  private clientUri?;
148
140
  private logoUri?;
149
141
  private policyUri?;
142
+ private createdAt?;
150
143
  /** Event emitters for connection lifecycle */
151
144
  private readonly _onConnectionEvent;
152
145
  readonly onConnectionEvent: Event<McpConnectionEvent>;
@@ -196,6 +189,7 @@ declare class MCPClient {
196
189
  * Saves current session state to storage
197
190
  * Creates new session if it doesn't exist, updates if it does
198
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
199
193
  * @private
200
194
  */
201
195
  private saveSession;
@@ -1,11 +1,11 @@
1
- export { M as MCPClient, a as MultiSessionClient, S as StorageOAuthClientProvider } from '../multi-session-client-CxogNckF.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
- import { M as McpConnectionEvent, d as McpObservabilityEvent } from '../events-BgeztGYZ.mjs';
6
- export { D as Disposable, E as Emitter, b as Event, c as McpConnectionState } from '../events-BgeztGYZ.mjs';
7
- import { q as McpRpcResponse, p as McpRpcRequest } from '../types-CLccx9wW.mjs';
8
- export { a as CallToolRequest, b as CallToolResponse, f as ConnectRequest, g as ConnectResponse, m as ListToolsResponse, T as ToolInfo } from '../types-CLccx9wW.mjs';
5
+ import { M as McpConnectionEvent, d as McpObservabilityEvent } from '../events-CK3N--3g.mjs';
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-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-cox_WXUj.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
- import { M as McpConnectionEvent, d as McpObservabilityEvent } from '../events-BgeztGYZ.js';
6
- export { D as Disposable, E as Emitter, b as Event, c as McpConnectionState } from '../events-BgeztGYZ.js';
7
- import { q as McpRpcResponse, p as McpRpcRequest } from '../types-CLccx9wW.js';
8
- export { a as CallToolRequest, b as CallToolResponse, f as ConnectRequest, g as ConnectResponse, m as ListToolsResponse, T as ToolInfo } from '../types-CLccx9wW.js';
5
+ import { M as McpConnectionEvent, d as McpObservabilityEvent } from '../events-CK3N--3g.js';
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-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
 
@@ -1024,18 +1024,15 @@ var MCPClient = class _MCPClient {
1024
1024
  __publicField(this, "serverUrl");
1025
1025
  __publicField(this, "callbackUrl");
1026
1026
  __publicField(this, "onRedirect");
1027
- __publicField(this, "tokens");
1028
- __publicField(this, "tokenExpiresAt");
1029
- __publicField(this, "clientInformation");
1030
1027
  __publicField(this, "clientId");
1031
1028
  __publicField(this, "clientSecret");
1032
- __publicField(this, "onSaveTokens");
1033
1029
  __publicField(this, "headers");
1034
1030
  /** OAuth Client Metadata */
1035
1031
  __publicField(this, "clientName");
1036
1032
  __publicField(this, "clientUri");
1037
1033
  __publicField(this, "logoUri");
1038
1034
  __publicField(this, "policyUri");
1035
+ __publicField(this, "createdAt");
1039
1036
  /** Event emitters for connection lifecycle */
1040
1037
  __publicField(this, "_onConnectionEvent", new Emitter());
1041
1038
  __publicField(this, "onConnectionEvent", this._onConnectionEvent.event);
@@ -1050,12 +1047,8 @@ var MCPClient = class _MCPClient {
1050
1047
  this.serverId = options.serverId;
1051
1048
  this.sessionId = options.sessionId;
1052
1049
  this.transportType = options.transportType;
1053
- this.tokens = options.tokens;
1054
- this.tokenExpiresAt = options.tokenExpiresAt;
1055
- this.clientInformation = options.clientInformation;
1056
1050
  this.clientId = options.clientId;
1057
1051
  this.clientSecret = options.clientSecret;
1058
- this.onSaveTokens = options.onSaveTokens;
1059
1052
  this.headers = options.headers;
1060
1053
  this.clientName = options.clientName;
1061
1054
  this.clientUri = options.clientUri;
@@ -1075,6 +1068,8 @@ var MCPClient = class _MCPClient {
1075
1068
  sessionId: this.sessionId,
1076
1069
  serverId: this.serverId,
1077
1070
  serverName: this.serverName || this.serverId,
1071
+ serverUrl: this.serverUrl || "",
1072
+ createdAt: this.createdAt,
1078
1073
  state: newState,
1079
1074
  previousState,
1080
1075
  timestamp: Date.now()
@@ -1195,6 +1190,7 @@ var MCPClient = class _MCPClient {
1195
1190
  this.serverName = this.serverName || sessionData.serverName;
1196
1191
  this.serverId = this.serverId || sessionData.serverId || "unknown";
1197
1192
  this.headers = this.headers || sessionData.headers;
1193
+ this.createdAt = sessionData.createdAt;
1198
1194
  }
1199
1195
  if (!this.serverUrl || !this.callbackUrl || !this.serverId) {
1200
1196
  throw new Error("Missing required connection metadata");
@@ -1248,6 +1244,7 @@ var MCPClient = class _MCPClient {
1248
1244
  }
1249
1245
  const existingSession = await storage.getSession(this.identity, this.sessionId);
1250
1246
  if (!existingSession && this.serverId && this.serverUrl && this.callbackUrl) {
1247
+ this.createdAt = Date.now();
1251
1248
  console.log(`[MCPClient] Creating initial session ${this.sessionId} for OAuth flow`);
1252
1249
  await storage.createSession({
1253
1250
  sessionId: this.sessionId,
@@ -1257,7 +1254,8 @@ var MCPClient = class _MCPClient {
1257
1254
  serverUrl: this.serverUrl,
1258
1255
  callbackUrl: this.callbackUrl,
1259
1256
  transportType: this.transportType || "streamable_http",
1260
- createdAt: Date.now()
1257
+ createdAt: this.createdAt,
1258
+ active: false
1261
1259
  }, Math.floor(STATE_EXPIRATION_MS / 1e3));
1262
1260
  }
1263
1261
  }
@@ -1265,9 +1263,10 @@ var MCPClient = class _MCPClient {
1265
1263
  * Saves current session state to storage
1266
1264
  * Creates new session if it doesn't exist, updates if it does
1267
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
1268
1267
  * @private
1269
1268
  */
1270
- async saveSession(ttl = SESSION_TTL_SECONDS) {
1269
+ async saveSession(ttl = SESSION_TTL_SECONDS, active = true) {
1271
1270
  if (!this.sessionId || !this.serverId || !this.serverUrl || !this.callbackUrl) {
1272
1271
  return;
1273
1272
  }
@@ -1279,7 +1278,8 @@ var MCPClient = class _MCPClient {
1279
1278
  serverUrl: this.serverUrl,
1280
1279
  callbackUrl: this.callbackUrl,
1281
1280
  transportType: this.transportType || "streamable_http",
1282
- createdAt: Date.now()
1281
+ createdAt: this.createdAt || Date.now(),
1282
+ active
1283
1283
  };
1284
1284
  const existingSession = await storage.getSession(this.identity, this.sessionId);
1285
1285
  if (existingSession) {
@@ -1353,15 +1353,17 @@ var MCPClient = class _MCPClient {
1353
1353
  this.emitStateChange("CONNECTED");
1354
1354
  this.emitProgress("Connected successfully");
1355
1355
  const existingSession = await storage.getSession(this.identity, this.sessionId);
1356
- if (!existingSession || existingSession.transportType !== this.transportType) {
1357
- console.log(`[MCPClient] Saving session ${this.sessionId} (new or transport changed)`);
1358
- 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);
1359
1361
  }
1360
1362
  } catch (error) {
1361
1363
  if (error instanceof auth_js.UnauthorizedError || error instanceof Error && error.message.toLowerCase().includes("unauthorized")) {
1362
1364
  this.emitStateChange("AUTHENTICATING");
1363
1365
  console.log(`[MCPClient] Saving session ${this.sessionId} with 10min TTL (OAuth pending)`);
1364
- await this.saveSession(Math.floor(STATE_EXPIRATION_MS / 1e3));
1366
+ await this.saveSession(Math.floor(STATE_EXPIRATION_MS / 1e3), false);
1365
1367
  let authUrl = "";
1366
1368
  if (this.oauthProvider) {
1367
1369
  authUrl = this.oauthProvider.authUrl || "";
@@ -1439,7 +1441,7 @@ var MCPClient = class _MCPClient {
1439
1441
  await this.client.connect(this.transport);
1440
1442
  this.emitStateChange("CONNECTED");
1441
1443
  console.log(`[MCPClient] Updating session ${this.sessionId} to 12hr TTL (OAuth complete)`);
1442
- await this.saveSession(SESSION_TTL_SECONDS);
1444
+ await this.saveSession(SESSION_TTL_SECONDS, true);
1443
1445
  return;
1444
1446
  } catch (error) {
1445
1447
  lastError = error;
@@ -2103,7 +2105,9 @@ var SSEConnectionManager = class {
2103
2105
  serverId: s.serverId,
2104
2106
  serverName: s.serverName,
2105
2107
  serverUrl: s.serverUrl,
2106
- transport: s.transportType
2108
+ transport: s.transportType,
2109
+ createdAt: s.createdAt,
2110
+ active: s.active !== false
2107
2111
  }))
2108
2112
  };
2109
2113
  }
@@ -2118,6 +2122,13 @@ var SSEConnectionManager = class {
2118
2122
  (s) => s.serverId === serverId || s.serverUrl === serverUrl
2119
2123
  );
2120
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
+ }
2121
2132
  throw new Error(`Connection already exists for server: ${duplicate.serverUrl || duplicate.serverId} (${duplicate.serverName})`);
2122
2133
  }
2123
2134
  const sessionId = await storage.generateSessionId();
@@ -2126,6 +2137,7 @@ var SSEConnectionManager = class {
2126
2137
  sessionId,
2127
2138
  serverId,
2128
2139
  serverName,
2140
+ serverUrl,
2129
2141
  state: "CONNECTING",
2130
2142
  previousState: "DISCONNECTED",
2131
2143
  timestamp: Date.now()
@@ -2258,6 +2270,7 @@ var SSEConnectionManager = class {
2258
2270
  sessionId,
2259
2271
  serverId: session.serverId ?? "unknown",
2260
2272
  serverName: session.serverName ?? "Unknown",
2273
+ serverUrl: session.serverUrl,
2261
2274
  state: "VALIDATING",
2262
2275
  previousState: "DISCONNECTED",
2263
2276
  timestamp: Date.now()
@@ -2309,6 +2322,7 @@ var SSEConnectionManager = class {
2309
2322
  sessionId,
2310
2323
  serverId: session.serverId ?? "unknown",
2311
2324
  serverName: session.serverName ?? "Unknown",
2325
+ serverUrl: session.serverUrl,
2312
2326
  state: "AUTHENTICATING",
2313
2327
  previousState: "DISCONNECTED",
2314
2328
  timestamp: Date.now()
@@ -2440,7 +2454,9 @@ function writeSSEEvent(res, event, data) {
2440
2454
  }
2441
2455
 
2442
2456
  // src/server/handlers/nextjs-handler.ts
2443
- var managers = /* @__PURE__ */ new Map();
2457
+ function isRpcResponseEvent(event) {
2458
+ return "id" in event && ("result" in event || "error" in event);
2459
+ }
2444
2460
  function createNextMcpHandler(options = {}) {
2445
2461
  const {
2446
2462
  getIdentity = (request) => new URL(request.url).searchParams.get("identity"),
@@ -2453,72 +2469,29 @@ function createNextMcpHandler(options = {}) {
2453
2469
  clientDefaults,
2454
2470
  getClientMetadata
2455
2471
  } = options;
2456
- async function GET(request) {
2457
- const identity = getIdentity(request);
2458
- const authToken = getAuthToken(request);
2459
- if (!identity) {
2460
- return new Response("Missing identity", { status: 400 });
2461
- }
2462
- const isAuthorized = await authenticate(identity, authToken);
2463
- if (!isAuthorized) {
2464
- return new Response("Unauthorized", { status: 401 });
2465
- }
2466
- const stream = new TransformStream();
2467
- const writer = stream.writable.getWriter();
2468
- const encoder = new TextEncoder();
2469
- const sendSSE = (event, data) => {
2470
- const message = `event: ${event}
2471
- data: ${JSON.stringify(data)}
2472
-
2473
- `;
2474
- writer.write(encoder.encode(message)).catch(() => {
2475
- });
2476
- };
2477
- const previousManager = managers.get(identity);
2478
- if (previousManager) {
2479
- previousManager.dispose();
2480
- }
2481
- const resolvedClientMetadata = getClientMetadata ? await getClientMetadata(request) : clientDefaults;
2482
- 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(
2483
2482
  {
2484
- identity,
2485
- heartbeatInterval,
2486
- clientDefaults: resolvedClientMetadata
2487
- // Pass resolved metadata
2488
- },
2489
- (event) => {
2490
- if ("id" in event) {
2491
- sendSSE("rpc-response", event);
2492
- } else if ("type" in event && "sessionId" in event) {
2493
- sendSSE("connection", event);
2494
- } else {
2495
- sendSSE("observability", event);
2483
+ error: {
2484
+ code: "METHOD_NOT_ALLOWED",
2485
+ message: "Use POST /api/mcp. For streaming use Accept: text/event-stream."
2496
2486
  }
2497
- }
2487
+ },
2488
+ { status: 405 }
2498
2489
  );
2499
- managers.set(identity, manager);
2500
- sendSSE("connected", { timestamp: Date.now() });
2501
- const abortController = new AbortController();
2502
- request.signal?.addEventListener("abort", () => {
2503
- manager.dispose();
2504
- managers.delete(identity);
2505
- writer.close().catch(() => {
2506
- });
2507
- abortController.abort();
2508
- });
2509
- return new Response(stream.readable, {
2510
- status: 200,
2511
- headers: {
2512
- "Content-Type": "text/event-stream",
2513
- "Cache-Control": "no-cache, no-transform",
2514
- "Connection": "keep-alive",
2515
- "X-Accel-Buffering": "no"
2516
- }
2517
- });
2518
2490
  }
2519
2491
  async function POST(request) {
2520
2492
  const identity = getIdentity(request);
2521
2493
  const authToken = getAuthToken(request);
2494
+ const acceptsEventStream = (request.headers.get("accept") || "").toLowerCase().includes("text/event-stream");
2522
2495
  if (!identity) {
2523
2496
  return Response.json({ error: { code: "MISSING_IDENTITY", message: "Missing identity" } }, { status: 400 });
2524
2497
  }
@@ -2526,28 +2499,103 @@ data: ${JSON.stringify(data)}
2526
2499
  if (!isAuthorized) {
2527
2500
  return Response.json({ error: { code: "UNAUTHORIZED", message: "Unauthorized" } }, { status: 401 });
2528
2501
  }
2502
+ let rawBody = "";
2529
2503
  try {
2530
- const body = await request.json();
2531
- const manager = managers.get(identity);
2532
- if (!manager) {
2504
+ rawBody = await request.text();
2505
+ const body = rawBody ? JSON.parse(rawBody) : null;
2506
+ if (!body || typeof body !== "object") {
2533
2507
  return Response.json(
2534
2508
  {
2535
2509
  error: {
2536
- code: "NO_CONNECTION",
2537
- message: "No SSE connection found. Please establish SSE connection first."
2510
+ code: "INVALID_REQUEST",
2511
+ message: "Invalid JSON-RPC request body"
2538
2512
  }
2539
2513
  },
2540
2514
  { status: 400 }
2541
2515
  );
2542
2516
  }
2543
- const response = await manager.handleRequest(body);
2544
- 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
+ });
2545
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
+ });
2546
2594
  return Response.json(
2547
2595
  {
2548
2596
  error: {
2549
2597
  code: "EXECUTION_ERROR",
2550
- message: error instanceof Error ? error.message : "Unknown error"
2598
+ message: err.message
2551
2599
  }
2552
2600
  },
2553
2601
  { status: 500 }