@frontmcp/sdk 0.6.2 → 0.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/sdk",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "FrontMCP SDK",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -50,6 +50,16 @@
50
50
  "default": "./esm/mcp-apps/index.mjs"
51
51
  }
52
52
  },
53
+ "./transport": {
54
+ "require": {
55
+ "types": "./transport/index.d.ts",
56
+ "default": "./transport/index.js"
57
+ },
58
+ "import": {
59
+ "types": "./transport/index.d.ts",
60
+ "default": "./esm/transport/index.mjs"
61
+ }
62
+ },
53
63
  "./esm": null
54
64
  },
55
65
  "peerDependencies": {
@@ -66,7 +76,7 @@
66
76
  }
67
77
  },
68
78
  "dependencies": {
69
- "@frontmcp/uipack": "0.6.2",
79
+ "@frontmcp/uipack": "0.6.3",
70
80
  "@modelcontextprotocol/sdk": "1.25.1",
71
81
  "ioredis": "^5.8.0",
72
82
  "jose": "^6.1.3",
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Custom SSEServerTransport that supports session recreation.
3
+ *
4
+ * When recreating a session from Redis (e.g., in serverless environments
5
+ * or after client reconnection), we need to restore the transport's state
6
+ * including the event ID counter for proper SSE reconnection support.
7
+ *
8
+ * This class extends our custom SSEServerTransport to expose a public API
9
+ * for session recreation, maintaining the event ID sequence across reconnections.
10
+ */
11
+ import { SSEServerTransport, SSEServerTransportOptions } from '../legacy/legacy.sse.tranporter';
12
+ import type { ServerResponse } from 'http';
13
+ export interface RecreateableSSEServerTransportOptions extends SSEServerTransportOptions {
14
+ /**
15
+ * Initial event ID counter value for session recreation.
16
+ * Use this when recreating a session to restore the event ID sequence.
17
+ */
18
+ initialEventId?: number;
19
+ }
20
+ /**
21
+ * SSEServerTransport with session recreation support.
22
+ *
23
+ * This is a drop-in replacement for SSEServerTransport that adds the ability
24
+ * to recreate a session with preserved state. This is essential for:
25
+ * - Serverless environments where the transport may be evicted from memory
26
+ * - Client reconnection scenarios where event ID continuity is required
27
+ * - Distributed systems where sessions are stored in Redis
28
+ *
29
+ * It extends SSEServerTransport to maintain full compatibility while
30
+ * adding public methods to set initialization state and restore event IDs.
31
+ */
32
+ export declare class RecreateableSSEServerTransport extends SSEServerTransport {
33
+ private _isRecreatedSession;
34
+ constructor(endpoint: string, res: ServerResponse, options?: RecreateableSSEServerTransportOptions);
35
+ /**
36
+ * Validates that an event ID is a valid non-negative integer.
37
+ * Protects against negative values, NaN, Infinity, and non-integer values.
38
+ */
39
+ private isValidEventId;
40
+ /**
41
+ * Returns whether this is a recreated session.
42
+ */
43
+ get isRecreatedSession(): boolean;
44
+ /**
45
+ * Returns the current event ID counter value.
46
+ * Alias for lastEventId for consistency with the recreation API.
47
+ */
48
+ get eventIdCounter(): number;
49
+ /**
50
+ * Sets the event ID counter for session recreation.
51
+ * Use this when recreating a session from stored state to maintain
52
+ * event ID continuity for SSE reconnection support.
53
+ *
54
+ * @param eventId - The event ID to restore (must be a non-negative integer)
55
+ */
56
+ setEventIdCounter(eventId: number): void;
57
+ /**
58
+ * Sets the transport to a recreated session state.
59
+ * Use this when recreating a transport from a stored session.
60
+ *
61
+ * @param sessionId - The session ID (for verification, should match constructor)
62
+ * @param lastEventId - The last event ID that was sent to the client
63
+ */
64
+ setSessionState(sessionId: string, lastEventId?: number): void;
65
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Custom StreamableHTTPServerTransport that supports session recreation.
3
+ *
4
+ * The MCP SDK's transport sets `_initialized` and `sessionId` only during
5
+ * the initialize request handshake. When recreating a transport from Redis
6
+ * (e.g., in serverless environments), we need to set these values directly.
7
+ *
8
+ * This class extends the MCP SDK's transport to expose a public API for
9
+ * session recreation, avoiding the need to access private properties.
10
+ */
11
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
12
+ export interface StreamableHTTPServerTransportOptions {
13
+ /**
14
+ * A function that generates a session ID for the transport.
15
+ * If provided, sessions are stateful and require the mcp-session-id header.
16
+ * If undefined, the transport operates in stateless mode.
17
+ */
18
+ sessionIdGenerator?: () => string;
19
+ /**
20
+ * If true, responses are sent as JSON instead of SSE.
21
+ * Default: false (SSE streaming mode)
22
+ */
23
+ enableJsonResponse?: boolean;
24
+ /**
25
+ * Event store for resumability support.
26
+ * Type uses `any` because the EventStore interface is not exported from the MCP SDK
27
+ * and varies between SDK versions. The actual type is defined internally by
28
+ * StreamableHTTPServerTransport.
29
+ */
30
+ eventStore?: any;
31
+ /**
32
+ * Callback when a session is initialized.
33
+ */
34
+ onsessioninitialized?: (sessionId: string) => void | Promise<void>;
35
+ /**
36
+ * Callback when a session is closed.
37
+ */
38
+ onsessionclosed?: (sessionId?: string) => void | Promise<void>;
39
+ }
40
+ /**
41
+ * StreamableHTTPServerTransport with session recreation support.
42
+ *
43
+ * This is a drop-in replacement for the MCP SDK's StreamableHTTPServerTransport
44
+ * that adds the ability to recreate a session without replaying the initialization
45
+ * handshake. This is essential for serverless environments where the transport
46
+ * may be evicted from memory and needs to be recreated from a stored session.
47
+ *
48
+ * It extends StreamableHTTPServerTransport to maintain full compatibility while
49
+ * adding public methods to set initialization state.
50
+ */
51
+ export declare class RecreateableStreamableHTTPServerTransport extends StreamableHTTPServerTransport {
52
+ constructor(options?: StreamableHTTPServerTransportOptions);
53
+ /**
54
+ * Returns whether the transport has been initialized.
55
+ */
56
+ get isInitialized(): boolean;
57
+ /**
58
+ * Sets the transport to an initialized state with the given session ID.
59
+ * Use this when recreating a transport from a stored session.
60
+ *
61
+ * This method allows you to "restore" a session without replaying the
62
+ * initialization handshake. After calling this method, the transport
63
+ * will accept requests with the given session ID.
64
+ *
65
+ * @param sessionId - The session ID that was previously assigned to this session
66
+ * @throws Error if sessionId is empty or invalid
67
+ */
68
+ setInitializationState(sessionId: string): void;
69
+ }
@@ -6,10 +6,19 @@ import { InMemoryEventStore } from '../transport.event-store';
6
6
  import { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
7
7
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
8
8
  import { SSEServerTransport } from '../legacy/legacy.sse.tranporter';
9
+ import { RecreateableStreamableHTTPServerTransport } from './streamable-http-transport';
10
+ import { RecreateableSSEServerTransport } from './sse-transport';
9
11
  import { ZodType } from 'zod';
10
12
  import { FrontMcpLogger, ServerResponse } from '../../common';
11
13
  import { Scope } from '../../scope';
12
- export declare abstract class LocalTransportAdapter<T extends StreamableHTTPServerTransport | SSEServerTransport> {
14
+ /**
15
+ * Base transport type that includes all supported transports.
16
+ * RecreateableStreamableHTTPServerTransport extends StreamableHTTPServerTransport
17
+ * and RecreateableSSEServerTransport extends SSEServerTransport,
18
+ * so they're also included in this union.
19
+ */
20
+ export type SupportedTransport = StreamableHTTPServerTransport | SSEServerTransport | RecreateableStreamableHTTPServerTransport | RecreateableSSEServerTransport;
21
+ export declare abstract class LocalTransportAdapter<T extends SupportedTransport> {
13
22
  #private;
14
23
  protected readonly scope: Scope;
15
24
  protected readonly key: TransportKey;
@@ -28,6 +37,11 @@ export declare abstract class LocalTransportAdapter<T extends StreamableHTTPServ
28
37
  abstract initialize(req: AuthenticatedServerRequest, res: ServerResponse): Promise<void>;
29
38
  abstract sendElicitRequest<T extends ZodType>(relatedRequestId: RequestId, message: string, requestedSchema: T): Promise<TypedElicitResult<T>>;
30
39
  abstract handleRequest(req: AuthenticatedServerRequest, res: ServerResponse): Promise<void>;
40
+ /**
41
+ * Marks this transport as pre-initialized for session recreation.
42
+ * Override in subclasses that need to set the MCP SDK's _initialized flag.
43
+ */
44
+ markAsInitialized(): void;
31
45
  connectServer(): Promise<void>;
32
46
  get newRequestId(): RequestId;
33
47
  destroy(reason?: string): Promise<void>;
@@ -1,13 +1,26 @@
1
1
  import { AuthenticatedServerRequest } from '../../server/server.types';
2
2
  import { TypedElicitResult } from '../transport.types';
3
- import { SSEServerTransport } from '../legacy/legacy.sse.tranporter';
3
+ import { RecreateableSSEServerTransport } from './sse-transport';
4
4
  import { LocalTransportAdapter } from './transport.local.adapter';
5
5
  import { RequestId } from '@modelcontextprotocol/sdk/types.js';
6
6
  import { ZodType } from 'zod';
7
7
  import { ServerResponse } from '../../common';
8
- export declare class TransportSSEAdapter extends LocalTransportAdapter<SSEServerTransport> {
8
+ export declare class TransportSSEAdapter extends LocalTransportAdapter<RecreateableSSEServerTransport> {
9
9
  sessionId: string;
10
- createTransport(sessionId: string, res: ServerResponse): SSEServerTransport;
10
+ /**
11
+ * Configures common error and close handlers for SSE transports.
12
+ */
13
+ private configureTransportHandlers;
14
+ createTransport(sessionId: string, res: ServerResponse): RecreateableSSEServerTransport;
15
+ /**
16
+ * Recreates a transport with preserved session state.
17
+ * Use this when restoring a session from Redis or other storage.
18
+ *
19
+ * @param sessionId - The session ID to restore
20
+ * @param res - The new response stream for SSE
21
+ * @param lastEventId - The last event ID that was sent (for reconnection support)
22
+ */
23
+ createTransportFromSession(sessionId: string, res: ServerResponse, lastEventId?: number): RecreateableSSEServerTransport;
11
24
  initialize(req: AuthenticatedServerRequest, res: ServerResponse): Promise<void>;
12
25
  handleRequest(req: AuthenticatedServerRequest, res: ServerResponse): Promise<void>;
13
26
  sendElicitRequest<T extends ZodType>(relatedRequestId: RequestId, message: string, requestedSchema: T): Promise<TypedElicitResult<T>>;
@@ -1,10 +1,10 @@
1
- import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
2
1
  import { TransportType, TypedElicitResult } from '../transport.types';
3
2
  import { AuthenticatedServerRequest } from '../../server/server.types';
4
3
  import { LocalTransportAdapter } from './transport.local.adapter';
5
4
  import { RequestId } from '@modelcontextprotocol/sdk/types.js';
6
5
  import { ZodType } from 'zod';
7
6
  import { ServerResponse } from '../../common';
7
+ import { RecreateableStreamableHTTPServerTransport } from './streamable-http-transport';
8
8
  /**
9
9
  * Stateless HTTP requests must be able to send multiple initialize calls without
10
10
  * tripping the MCP transport's "already initialized" guard. The upstream SDK
@@ -12,9 +12,18 @@ import { ServerResponse } from '../../common';
12
12
  * session generation entirely for stateless transports.
13
13
  */
14
14
  export declare const resolveSessionIdGenerator: (transportType: TransportType, sessionId: string) => (() => string) | undefined;
15
- export declare class TransportStreamableHttpAdapter extends LocalTransportAdapter<StreamableHTTPServerTransport> {
16
- createTransport(sessionId: string, response: ServerResponse): StreamableHTTPServerTransport;
15
+ export declare class TransportStreamableHttpAdapter extends LocalTransportAdapter<RecreateableStreamableHTTPServerTransport> {
16
+ createTransport(sessionId: string, response: ServerResponse): RecreateableStreamableHTTPServerTransport;
17
17
  initialize(req: AuthenticatedServerRequest, res: ServerResponse): Promise<void>;
18
18
  handleRequest(req: AuthenticatedServerRequest, res: ServerResponse): Promise<void>;
19
19
  sendElicitRequest<T extends ZodType>(relatedRequestId: RequestId, message: string, requestedSchema: T): Promise<TypedElicitResult<T>>;
20
+ /**
21
+ * Marks this transport as pre-initialized for session recreation.
22
+ * This is needed when recreating a transport from Redis because the
23
+ * original initialize request was processed by a different transport instance.
24
+ *
25
+ * Uses the RecreateableStreamableHTTPServerTransport's public API to set
26
+ * initialization state, avoiding access to private properties.
27
+ */
28
+ markAsInitialized(): void;
20
29
  }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @file index.ts
3
+ * @description Transport module barrel exports.
4
+ *
5
+ * Provides recreateable transport classes for session recreation support
6
+ * in serverless environments and distributed systems.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import {
11
+ * RecreateableStreamableHTTPServerTransport,
12
+ * RecreateableSSEServerTransport,
13
+ * } from '@frontmcp/sdk/transport';
14
+ * ```
15
+ *
16
+ * @module @frontmcp/sdk/transport
17
+ */
18
+ export { RecreateableStreamableHTTPServerTransport, StreamableHTTPServerTransportOptions, } from './adapters/streamable-http-transport';
19
+ export { RecreateableSSEServerTransport, RecreateableSSEServerTransportOptions } from './adapters/sse-transport';
20
+ export { SSEServerTransport, SSEServerTransportOptions } from './legacy/legacy.sse.tranporter';
21
+ export { SupportedTransport } from './adapters/transport.local.adapter';
@@ -13,5 +13,11 @@ export declare class LocalTransporter implements Transporter {
13
13
  handleRequest(req: AuthenticatedServerRequest, res: ServerResponse): Promise<void>;
14
14
  ready(): Promise<void>;
15
15
  initialize(req: AuthenticatedServerRequest, res: ServerResponse): Promise<void>;
16
+ /**
17
+ * Marks this transport as pre-initialized for session recreation.
18
+ * This is needed when recreating a transport from Redis because the
19
+ * original initialize request was processed by a different transport instance.
20
+ */
21
+ markAsInitialized(): void;
16
22
  destroy(reason: string): Promise<void>;
17
23
  }
@@ -47,9 +47,15 @@ export declare class TransportService {
47
47
  * @param type - Transport type
48
48
  * @param token - Authorization token
49
49
  * @param sessionId - Session ID
50
+ * @param options - Optional validation options
51
+ * @param options.clientFingerprint - Client fingerprint for additional validation
52
+ * @param options.warnOnFingerprintMismatch - If true, log warning on mismatch but still return session
50
53
  * @returns Stored session data if exists and token matches, undefined otherwise
51
54
  */
52
- getStoredSession(type: TransportType, token: string, sessionId: string): Promise<StoredSession | undefined>;
55
+ getStoredSession(type: TransportType, token: string, sessionId: string, options?: {
56
+ clientFingerprint?: string;
57
+ warnOnFingerprintMismatch?: boolean;
58
+ }): Promise<StoredSession | undefined>;
53
59
  /**
54
60
  * Recreate a transport from stored session data.
55
61
  * Must be called with a valid response object to create the actual transport.
@@ -12,4 +12,5 @@ export declare class RemoteTransporter implements Transporter {
12
12
  initialize(req: AuthenticatedServerRequest, res: ServerResponse): Promise<void>;
13
13
  handleRequest(_req: AuthenticatedServerRequest, _res: ServerResponse): Promise<void>;
14
14
  destroy(_reason?: string): Promise<void>;
15
+ markAsInitialized(): void;
15
16
  }
@@ -40,6 +40,12 @@ export interface Transporter {
40
40
  handleRequest(req: AuthenticatedServerRequest, res: ServerResponse): Promise<void>;
41
41
  destroy(reason?: string): Promise<void>;
42
42
  ping(timeoutMs?: number): Promise<boolean>;
43
+ /**
44
+ * Marks this transport as pre-initialized for session recreation.
45
+ * This is needed when recreating a transport from Redis because the
46
+ * original initialize request was processed by a different transport instance.
47
+ */
48
+ markAsInitialized(): void;
43
49
  }
44
50
  export interface TransportRegistryOptions {
45
51
  distributed?: boolean;