@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.
- package/README.md +371 -290
- package/dist/adapters/agui-adapter.d.mts +3 -3
- package/dist/adapters/agui-adapter.d.ts +3 -3
- package/dist/adapters/agui-middleware.d.mts +3 -3
- package/dist/adapters/agui-middleware.d.ts +3 -3
- package/dist/adapters/ai-adapter.d.mts +3 -3
- package/dist/adapters/ai-adapter.d.ts +3 -3
- package/dist/adapters/langchain-adapter.d.mts +3 -3
- package/dist/adapters/langchain-adapter.d.ts +3 -3
- package/dist/adapters/mastra-adapter.d.mts +3 -3
- package/dist/adapters/mastra-adapter.d.ts +3 -3
- package/dist/client/index.d.mts +10 -66
- package/dist/client/index.d.ts +10 -66
- package/dist/client/index.js +91 -173
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +91 -173
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/react.d.mts +15 -5
- package/dist/client/react.d.ts +15 -5
- package/dist/client/react.js +130 -182
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +130 -182
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.d.mts +27 -7
- package/dist/client/vue.d.ts +27 -7
- package/dist/client/vue.js +131 -182
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +131 -182
- package/dist/client/vue.mjs.map +1 -1
- package/dist/{events-BgeztGYZ.d.mts → events-CK3N--3g.d.mts} +2 -0
- package/dist/{events-BgeztGYZ.d.ts → events-CK3N--3g.d.ts} +2 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +224 -258
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +224 -258
- package/dist/index.mjs.map +1 -1
- package/dist/{multi-session-client-CxogNckF.d.mts → multi-session-client-DzjmT7FX.d.mts} +4 -10
- package/dist/{multi-session-client-cox_WXUj.d.ts → multi-session-client-FAFpUzZ4.d.ts} +4 -10
- package/dist/server/index.d.mts +18 -23
- package/dist/server/index.d.ts +18 -23
- package/dist/server/index.js +133 -85
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +133 -85
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +3 -3
- package/dist/shared/index.d.ts +3 -3
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs.map +1 -1
- package/dist/{types-CLccx9wW.d.mts → types-CW6lghof.d.mts} +6 -0
- package/dist/{types-CLccx9wW.d.ts → types-CW6lghof.d.ts} +6 -0
- package/package.json +1 -1
- package/src/client/core/sse-client.ts +354 -493
- package/src/client/react/index.ts +16 -16
- package/src/client/react/use-mcp-apps.tsx +214 -214
- package/src/client/react/use-mcp.ts +84 -19
- package/src/client/vue/use-mcp.ts +119 -44
- package/src/server/handlers/nextjs-handler.ts +207 -217
- package/src/server/handlers/sse-handler.ts +14 -0
- package/src/server/mcp/oauth-client.ts +48 -46
- package/src/server/storage/types.ts +12 -5
- package/src/shared/events.ts +2 -0
- 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-
|
|
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-
|
|
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;
|
package/dist/server/index.d.mts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export { M as MCPClient, a as MultiSessionClient, S as StorageOAuthClientProvider } from '../multi-session-client-
|
|
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-
|
|
6
|
-
export { D as Disposable, E as Emitter, b as Event, c as McpConnectionState } from '../events-
|
|
7
|
-
import { q as McpRpcResponse, p as McpRpcRequest } from '../types-
|
|
8
|
-
export { a as CallToolRequest, b as CallToolResponse, f as ConnectRequest, g as ConnectResponse, m as ListToolsResponse, T as ToolInfo } from '../types-
|
|
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
|
|
224
|
-
*
|
|
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
|
|
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: (
|
|
264
|
+
GET: () => Promise<Response>;
|
|
270
265
|
POST: (request: Request) => Promise<Response>;
|
|
271
266
|
};
|
|
272
267
|
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export { M as MCPClient, a as MultiSessionClient, S as StorageOAuthClientProvider } from '../multi-session-client-
|
|
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-
|
|
6
|
-
export { D as Disposable, E as Emitter, b as Event, c as McpConnectionState } from '../events-
|
|
7
|
-
import { q as McpRpcResponse, p as McpRpcRequest } from '../types-
|
|
8
|
-
export { a as CallToolRequest, b as CallToolResponse, f as ConnectRequest, g as ConnectResponse, m as ListToolsResponse, T as ToolInfo } from '../types-
|
|
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
|
|
224
|
-
*
|
|
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
|
|
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: (
|
|
264
|
+
GET: () => Promise<Response>;
|
|
270
265
|
POST: (request: Request) => Promise<Response>;
|
|
271
266
|
};
|
|
272
267
|
|
package/dist/server/index.js
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
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
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
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
|
-
|
|
2531
|
-
const
|
|
2532
|
-
if (!
|
|
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: "
|
|
2537
|
-
message: "
|
|
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
|
|
2544
|
-
|
|
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:
|
|
2598
|
+
message: err.message
|
|
2551
2599
|
}
|
|
2552
2600
|
},
|
|
2553
2601
|
{ status: 500 }
|