@1mcp/agent 0.29.0 → 0.29.1
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/build/auth/sessionTypes.d.ts +16 -0
- package/build/auth/sessionTypes.d.ts.map +1 -1
- package/build/commands/serve/index.d.ts +5 -0
- package/build/commands/serve/index.d.ts.map +1 -1
- package/build/commands/serve/index.js +5 -0
- package/build/commands/serve/index.js.map +1 -1
- package/build/commands/serve/serve.d.ts +1 -0
- package/build/commands/serve/serve.d.ts.map +1 -1
- package/build/commands/serve/serve.js +1 -0
- package/build/commands/serve/serve.js.map +1 -1
- package/build/constants/mcp.d.ts +1 -1
- package/build/constants/mcp.js +1 -1
- package/build/core/server/agentConfig.d.ts +2 -0
- package/build/core/server/agentConfig.d.ts.map +1 -1
- package/build/core/server/agentConfig.js +4 -0
- package/build/core/server/agentConfig.js.map +1 -1
- package/build/transport/http/loggingSseTransport.d.ts +13 -0
- package/build/transport/http/loggingSseTransport.d.ts.map +1 -0
- package/build/transport/http/loggingSseTransport.js +43 -0
- package/build/transport/http/loggingSseTransport.js.map +1 -0
- package/build/transport/http/middlewares/tagsExtractor.d.ts.map +1 -1
- package/build/transport/http/middlewares/tagsExtractor.js +21 -62
- package/build/transport/http/middlewares/tagsExtractor.js.map +1 -1
- package/build/transport/http/restorableStreamableTransport.d.ts +16 -59
- package/build/transport/http/restorableStreamableTransport.d.ts.map +1 -1
- package/build/transport/http/restorableStreamableTransport.js +18 -114
- package/build/transport/http/restorableStreamableTransport.js.map +1 -1
- package/build/transport/http/routes/sseRoutes.d.ts.map +1 -1
- package/build/transport/http/routes/sseRoutes.js +2 -1
- package/build/transport/http/routes/sseRoutes.js.map +1 -1
- package/build/transport/http/routes/streamableHttpRoutes.d.ts.map +1 -1
- package/build/transport/http/routes/streamableHttpRoutes.js +169 -60
- package/build/transport/http/routes/streamableHttpRoutes.js.map +1 -1
- package/build/transport/http/storage/streamableSessionRepository.d.ts +28 -0
- package/build/transport/http/storage/streamableSessionRepository.d.ts.map +1 -1
- package/build/transport/http/storage/streamableSessionRepository.js +103 -0
- package/build/transport/http/storage/streamableSessionRepository.js.map +1 -1
- package/build/transport/http/utils/httpErrorHandler.d.ts +36 -0
- package/build/transport/http/utils/httpErrorHandler.d.ts.map +1 -0
- package/build/transport/http/utils/httpErrorHandler.js +83 -0
- package/build/transport/http/utils/httpErrorHandler.js.map +1 -0
- package/build/transport/http/utils/sessionService.d.ts +30 -0
- package/build/transport/http/utils/sessionService.d.ts.map +1 -1
- package/build/transport/http/utils/sessionService.js +130 -20
- package/build/transport/http/utils/sessionService.js.map +1 -1
- package/build/transport/http/utils/unifiedLogger.d.ts +52 -0
- package/build/transport/http/utils/unifiedLogger.d.ts.map +1 -0
- package/build/transport/http/utils/unifiedLogger.js +80 -0
- package/build/transport/http/utils/unifiedLogger.js.map +1 -0
- package/package.json +1 -1
- package/build/transport/http/utils/sdkInternals.d.ts +0 -32
- package/build/transport/http/utils/sdkInternals.d.ts.map +0 -1
- package/build/transport/http/utils/sdkInternals.js +0 -6
- package/build/transport/http/utils/sdkInternals.js.map +0 -1
|
@@ -7,84 +7,41 @@ export interface RestorationInfo {
|
|
|
7
7
|
isRestored: boolean;
|
|
8
8
|
sessionId?: string;
|
|
9
9
|
}
|
|
10
|
-
/**
|
|
11
|
-
* Result type for operations that may fail.
|
|
12
|
-
* Uses discriminated union to make invalid states unrepresentable.
|
|
13
|
-
*/
|
|
14
|
-
export type OperationResult = {
|
|
15
|
-
success: true;
|
|
16
|
-
error?: never;
|
|
17
|
-
} | {
|
|
18
|
-
success: false;
|
|
19
|
-
error: string;
|
|
20
|
-
};
|
|
21
10
|
/**
|
|
22
11
|
* RestorableStreamableHTTPServerTransport extends the MCP SDK's StreamableHTTPServerTransport
|
|
23
|
-
* to
|
|
12
|
+
* to track whether a transport was restored from persistent storage.
|
|
24
13
|
*
|
|
25
|
-
* This wrapper
|
|
26
|
-
*
|
|
14
|
+
* This is a minimal wrapper that only adds restoration tracking. Session ID management
|
|
15
|
+
* is handled entirely through the SDK's public `sessionIdGenerator` callback - when
|
|
16
|
+
* restoring a session, we pass a generator that returns the stored session ID.
|
|
27
17
|
*
|
|
28
18
|
* @remarks
|
|
29
|
-
* The
|
|
30
|
-
* 1.
|
|
31
|
-
* 2.
|
|
32
|
-
* 3.
|
|
19
|
+
* The restoration flow works as follows:
|
|
20
|
+
* 1. Create transport with `sessionIdGenerator: () => storedSessionId`
|
|
21
|
+
* 2. Connect transport to ServerManager
|
|
22
|
+
* 3. Call virtualInitialize() which triggers SDK's handleRequest
|
|
23
|
+
* 4. SDK calls sessionIdGenerator() internally to get the session ID
|
|
24
|
+
* 5. Call markAsRestored() to indicate successful restoration
|
|
33
25
|
*
|
|
34
26
|
* @example
|
|
35
27
|
* ```typescript
|
|
36
|
-
* // SessionService handles the full restoration flow:
|
|
37
28
|
* const transport = new RestorableStreamableHTTPServerTransport({
|
|
38
29
|
* sessionIdGenerator: () => originalSessionId,
|
|
39
30
|
* });
|
|
40
|
-
*
|
|
41
|
-
* transport.
|
|
31
|
+
* // After successful virtual initialize...
|
|
32
|
+
* transport.markAsRestored();
|
|
42
33
|
* // Now transport.isRestored() returns true
|
|
43
34
|
* ```
|
|
44
35
|
*/
|
|
45
36
|
export declare class RestorableStreamableHTTPServerTransport extends StreamableHTTPServerTransport {
|
|
46
37
|
private _isRestored;
|
|
47
|
-
private _restoredSessionId?;
|
|
48
|
-
/**
|
|
49
|
-
* Sets the sessionId for a restored session.
|
|
50
|
-
*
|
|
51
|
-
* When restoring a session, we need to ensure the sessionId is available immediately
|
|
52
|
-
* without waiting for the sessionIdGenerator to be called by the SDK. This method
|
|
53
|
-
* directly sets the sessionId on the underlying transport.
|
|
54
|
-
*
|
|
55
|
-
* @param sessionId - The sessionId to set for the restored session
|
|
56
|
-
* @returns OperationResult indicating success or failure with error details
|
|
57
|
-
*/
|
|
58
|
-
setSessionId(sessionId: string): OperationResult;
|
|
59
38
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* This ensures that even if the underlying transport hasn't generated the sessionId yet,
|
|
63
|
-
* we return the correct restored sessionId. If no restored session ID exists, delegates
|
|
64
|
-
* to the parent class's getter by manually accessing the property descriptor.
|
|
65
|
-
*
|
|
66
|
-
* @remarks
|
|
67
|
-
* We cannot use `super.sessionId` directly because this override shadows the parent's
|
|
68
|
-
* getter. Instead, we use Object.getOwnPropertyDescriptor to access the parent's
|
|
69
|
-
* property descriptor and call its getter with the correct context.
|
|
70
|
-
*
|
|
71
|
-
* Type assertion to `object` is safe here because we know the prototype chain exists:
|
|
72
|
-
* this -> RestorableStreamableHTTPServerTransport -> StreamableHTTPServerTransport -> Object.prototype
|
|
73
|
-
* Object.getOwnPropertyDescriptor requires an object type, and TypeScript cannot infer this
|
|
74
|
-
* from the double getPrototypeOf call.
|
|
75
|
-
*/
|
|
76
|
-
get sessionId(): string | undefined;
|
|
77
|
-
/**
|
|
78
|
-
* Marks the transport as initialized for restored sessions.
|
|
79
|
-
*
|
|
80
|
-
* When restoring a session, the client won't send an initialize request again
|
|
81
|
-
* because from the client's perspective, the session is already initialized.
|
|
82
|
-
* The MCP SDK checks the _initialized flag and rejects requests if it's false.
|
|
83
|
-
* This method safely sets that flag to allow the restored session to work.
|
|
39
|
+
* Marks this transport as having been restored from persistent storage.
|
|
84
40
|
*
|
|
85
|
-
*
|
|
41
|
+
* This should be called after successful virtual initialization to indicate
|
|
42
|
+
* that the transport is ready to handle requests for a restored session.
|
|
86
43
|
*/
|
|
87
|
-
|
|
44
|
+
markAsRestored(): void;
|
|
88
45
|
/**
|
|
89
46
|
* Returns whether this transport was created for a restored session.
|
|
90
47
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restorableStreamableTransport.d.ts","sourceRoot":"","sources":["../../../src/transport/http/restorableStreamableTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;
|
|
1
|
+
{"version":3,"file":"restorableStreamableTransport.d.ts","sourceRoot":"","sources":["../../../src/transport/http/restorableStreamableTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAEnG;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,uCAAwC,SAAQ,6BAA6B;IACxF,OAAO,CAAC,WAAW,CAAS;IAE5B;;;;;OAKG;IACH,cAAc,IAAI,IAAI;IAItB;;;;OAIG;IACH,UAAU,IAAI,OAAO;IAIrB;;;;OAIG;IACH,kBAAkB,IAAI,eAAe;CAMtC"}
|
|
@@ -1,133 +1,40 @@
|
|
|
1
1
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
2
|
-
import logger from '../../logger/logger.js';
|
|
3
2
|
/**
|
|
4
3
|
* RestorableStreamableHTTPServerTransport extends the MCP SDK's StreamableHTTPServerTransport
|
|
5
|
-
* to
|
|
4
|
+
* to track whether a transport was restored from persistent storage.
|
|
6
5
|
*
|
|
7
|
-
* This wrapper
|
|
8
|
-
*
|
|
6
|
+
* This is a minimal wrapper that only adds restoration tracking. Session ID management
|
|
7
|
+
* is handled entirely through the SDK's public `sessionIdGenerator` callback - when
|
|
8
|
+
* restoring a session, we pass a generator that returns the stored session ID.
|
|
9
9
|
*
|
|
10
10
|
* @remarks
|
|
11
|
-
* The
|
|
12
|
-
* 1.
|
|
13
|
-
* 2.
|
|
14
|
-
* 3.
|
|
11
|
+
* The restoration flow works as follows:
|
|
12
|
+
* 1. Create transport with `sessionIdGenerator: () => storedSessionId`
|
|
13
|
+
* 2. Connect transport to ServerManager
|
|
14
|
+
* 3. Call virtualInitialize() which triggers SDK's handleRequest
|
|
15
|
+
* 4. SDK calls sessionIdGenerator() internally to get the session ID
|
|
16
|
+
* 5. Call markAsRestored() to indicate successful restoration
|
|
15
17
|
*
|
|
16
18
|
* @example
|
|
17
19
|
* ```typescript
|
|
18
|
-
* // SessionService handles the full restoration flow:
|
|
19
20
|
* const transport = new RestorableStreamableHTTPServerTransport({
|
|
20
21
|
* sessionIdGenerator: () => originalSessionId,
|
|
21
22
|
* });
|
|
22
|
-
*
|
|
23
|
-
* transport.
|
|
23
|
+
* // After successful virtual initialize...
|
|
24
|
+
* transport.markAsRestored();
|
|
24
25
|
* // Now transport.isRestored() returns true
|
|
25
26
|
* ```
|
|
26
27
|
*/
|
|
27
28
|
export class RestorableStreamableHTTPServerTransport extends StreamableHTTPServerTransport {
|
|
28
29
|
_isRestored = false;
|
|
29
|
-
_restoredSessionId;
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
31
|
+
* Marks this transport as having been restored from persistent storage.
|
|
32
32
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* directly sets the sessionId on the underlying transport.
|
|
36
|
-
*
|
|
37
|
-
* @param sessionId - The sessionId to set for the restored session
|
|
38
|
-
* @returns OperationResult indicating success or failure with error details
|
|
39
|
-
*/
|
|
40
|
-
setSessionId(sessionId) {
|
|
41
|
-
this._restoredSessionId = sessionId;
|
|
42
|
-
try {
|
|
43
|
-
// Access the underlying _webStandardTransport where sessionId is stored
|
|
44
|
-
// StreamableHTTPServerTransport is a wrapper with getter-only sessionId
|
|
45
|
-
// The actual sessionId is on _webStandardTransport which allows setting
|
|
46
|
-
const internalTransport = this;
|
|
47
|
-
const webStandardTransport = internalTransport._webStandardTransport;
|
|
48
|
-
if (webStandardTransport) {
|
|
49
|
-
webStandardTransport.sessionId = sessionId;
|
|
50
|
-
return { success: true };
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
success: false,
|
|
54
|
-
error: 'No webStandardTransport found - SDK internal structure may have changed or transport is not properly initialized',
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
59
|
-
logger.warn(`Failed to set sessionId "${sessionId}" on underlying transport: ${errorMessage}`);
|
|
60
|
-
return { success: false, error: errorMessage };
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Override sessionId getter to return restored sessionId if available.
|
|
65
|
-
*
|
|
66
|
-
* This ensures that even if the underlying transport hasn't generated the sessionId yet,
|
|
67
|
-
* we return the correct restored sessionId. If no restored session ID exists, delegates
|
|
68
|
-
* to the parent class's getter by manually accessing the property descriptor.
|
|
69
|
-
*
|
|
70
|
-
* @remarks
|
|
71
|
-
* We cannot use `super.sessionId` directly because this override shadows the parent's
|
|
72
|
-
* getter. Instead, we use Object.getOwnPropertyDescriptor to access the parent's
|
|
73
|
-
* property descriptor and call its getter with the correct context.
|
|
74
|
-
*
|
|
75
|
-
* Type assertion to `object` is safe here because we know the prototype chain exists:
|
|
76
|
-
* this -> RestorableStreamableHTTPServerTransport -> StreamableHTTPServerTransport -> Object.prototype
|
|
77
|
-
* Object.getOwnPropertyDescriptor requires an object type, and TypeScript cannot infer this
|
|
78
|
-
* from the double getPrototypeOf call.
|
|
79
|
-
*/
|
|
80
|
-
get sessionId() {
|
|
81
|
-
// First check if we have a restored sessionId
|
|
82
|
-
if (this._restoredSessionId) {
|
|
83
|
-
return this._restoredSessionId;
|
|
84
|
-
}
|
|
85
|
-
// Otherwise delegate to parent class's getter directly
|
|
86
|
-
// Use Object.getOwnPropertyDescriptor to get the parent's property descriptor
|
|
87
|
-
// and call the getter with the parent's context
|
|
88
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
89
|
-
const proto = Object.getPrototypeOf(Object.getPrototypeOf(this));
|
|
90
|
-
if (!proto)
|
|
91
|
-
return undefined;
|
|
92
|
-
const descriptor = Object.getOwnPropertyDescriptor(proto, 'sessionId');
|
|
93
|
-
if (descriptor?.get) {
|
|
94
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
95
|
-
return descriptor.get.call(this);
|
|
96
|
-
}
|
|
97
|
-
return undefined;
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Marks the transport as initialized for restored sessions.
|
|
101
|
-
*
|
|
102
|
-
* When restoring a session, the client won't send an initialize request again
|
|
103
|
-
* because from the client's perspective, the session is already initialized.
|
|
104
|
-
* The MCP SDK checks the _initialized flag and rejects requests if it's false.
|
|
105
|
-
* This method safely sets that flag to allow the restored session to work.
|
|
106
|
-
*
|
|
107
|
-
* @returns OperationResult indicating success or failure with error details
|
|
33
|
+
* This should be called after successful virtual initialization to indicate
|
|
34
|
+
* that the transport is ready to handle requests for a restored session.
|
|
108
35
|
*/
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// Use type-safe interface to access internal SDK properties
|
|
112
|
-
// MCP SDK private property _initialized is not exposed in public types
|
|
113
|
-
const internalTransport = this;
|
|
114
|
-
// Accessing private SDK property for session restoration functionality
|
|
115
|
-
if (internalTransport._initialized !== undefined) {
|
|
116
|
-
// Setting private SDK property to mark session as initialized for restoration
|
|
117
|
-
internalTransport._initialized = true;
|
|
118
|
-
}
|
|
119
|
-
// Note: If _initialized is undefined, we still mark as restored since the SDK may have changed
|
|
120
|
-
// This provides graceful degradation while allowing session restoration to proceed
|
|
121
|
-
this._isRestored = true;
|
|
122
|
-
logger.debug('Transport marked as initialized for restored session');
|
|
123
|
-
return { success: true };
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
127
|
-
logger.warn(`Failed to mark transport as initialized: ${errorMessage}`);
|
|
128
|
-
this._isRestored = false;
|
|
129
|
-
return { success: false, error: errorMessage };
|
|
130
|
-
}
|
|
36
|
+
markAsRestored() {
|
|
37
|
+
this._isRestored = true;
|
|
131
38
|
}
|
|
132
39
|
/**
|
|
133
40
|
* Returns whether this transport was created for a restored session.
|
|
@@ -143,12 +50,9 @@ export class RestorableStreamableHTTPServerTransport extends StreamableHTTPServe
|
|
|
143
50
|
* @returns Object containing restoration metadata
|
|
144
51
|
*/
|
|
145
52
|
getRestorationInfo() {
|
|
146
|
-
// Use type-safe interface to access potentially private sessionId
|
|
147
|
-
const internalTransport = this;
|
|
148
53
|
return {
|
|
149
54
|
isRestored: this._isRestored,
|
|
150
|
-
|
|
151
|
-
sessionId: internalTransport.sessionId,
|
|
55
|
+
sessionId: this.sessionId,
|
|
152
56
|
};
|
|
153
57
|
}
|
|
154
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restorableStreamableTransport.js","sourceRoot":"","sources":["../../../src/transport/http/restorableStreamableTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;
|
|
1
|
+
{"version":3,"file":"restorableStreamableTransport.js","sourceRoot":"","sources":["../../../src/transport/http/restorableStreamableTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAWnG;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,OAAO,uCAAwC,SAAQ,6BAA6B;IAChF,WAAW,GAAG,KAAK,CAAC;IAE5B;;;;;OAKG;IACH,cAAc;QACZ,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACH,UAAU;QACR,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACH,kBAAkB;QAChB,OAAO;YACL,UAAU,EAAE,IAAI,CAAC,WAAW;YAC5B,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sseRoutes.d.ts","sourceRoot":"","sources":["../../../../src/transport/http/routes/sseRoutes.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,wBAAwB,EAAE,MAAM,oDAAoD,CAAC;AAC9F,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"sseRoutes.d.ts","sourceRoot":"","sources":["../../../../src/transport/http/routes/sseRoutes.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,wBAAwB,EAAE,MAAM,oDAAoD,CAAC;AAC9F,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAalE,OAAO,EAAW,cAAc,EAAY,MAAM,EAAE,MAAM,SAAS,CAAC;AAEpE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa,EAC5B,cAAc,EAAE,cAAc,EAC9B,sBAAsB,CAAC,EAAE,cAAc,EACvC,iBAAiB,CAAC,EAAE,wBAAwB,EAC5C,cAAc,CAAC,EAAE,MAAM,GACtB,IAAI,CA6GN"}
|
|
@@ -3,6 +3,7 @@ import { ErrorCode } from '@modelcontextprotocol/sdk/types.js';
|
|
|
3
3
|
import { MESSAGES_ENDPOINT, SSE_ENDPOINT } from '../../../constants.js';
|
|
4
4
|
import { ServerStatus } from '../../../core/types/index.js';
|
|
5
5
|
import logger from '../../../logger/logger.js';
|
|
6
|
+
import { LoggingSSEServerTransport } from '../../../transport/http/loggingSseTransport.js';
|
|
6
7
|
import { getPresetName, getTagExpression, getTagFilterMode, getTagQuery, getValidatedTags, } from '../../../transport/http/middlewares/scopeAuthMiddleware.js';
|
|
7
8
|
import tagsExtractor from '../../../transport/http/middlewares/tagsExtractor.js';
|
|
8
9
|
export function setupSseRoutes(router, serverManager, authMiddleware, availabilityMiddleware, asyncOrchestrator, customTemplate) {
|
|
@@ -13,7 +14,7 @@ export function setupSseRoutes(router, serverManager, authMiddleware, availabili
|
|
|
13
14
|
}
|
|
14
15
|
router.get(SSE_ENDPOINT, ...middlewares, async (req, res) => {
|
|
15
16
|
try {
|
|
16
|
-
const transport = new
|
|
17
|
+
const transport = new LoggingSSEServerTransport(MESSAGES_ENDPOINT, res);
|
|
17
18
|
// Use validated tags and tag expression from scope auth middleware
|
|
18
19
|
const tags = getValidatedTags(res);
|
|
19
20
|
const tagExpression = getTagExpression(res);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sseRoutes.js","sourceRoot":"","sources":["../../../../src/transport/http/routes/sseRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAE/D,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGpE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,MAAM,MAAM,uBAAuB,CAAC;AAC3C,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,gBAAgB,GACjB,MAAM,wDAAwD,CAAC;AAChE,OAAO,aAAa,MAAM,kDAAkD,CAAC;AAI7E,MAAM,UAAU,cAAc,CAC5B,MAAc,EACd,aAA4B,EAC5B,cAA8B,EAC9B,sBAAuC,EACvC,iBAA4C,EAC5C,cAAuB;IAEvB,MAAM,WAAW,GAAG,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;IAEpD,0CAA0C;IAC1C,IAAI,sBAAsB,EAAE,CAAC;QAC3B,WAAW,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,WAAW,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QAC7E,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"sseRoutes.js","sourceRoot":"","sources":["../../../../src/transport/http/routes/sseRoutes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streamableHttpRoutes.d.ts","sourceRoot":"","sources":["../../../../src/transport/http/routes/streamableHttpRoutes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"streamableHttpRoutes.d.ts","sourceRoot":"","sources":["../../../../src/transport/http/routes/streamableHttpRoutes.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,wBAAwB,EAAE,MAAM,oDAAoD,CAAC;AAC9F,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAWlE,OAAO,EAAE,2BAA2B,EAAE,MAAM,4DAA4D,CAAC;AAGzG,OAAO,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAG7E,OAAO,EAAW,cAAc,EAAY,MAAM,EAAE,MAAM,SAAS,CAAC;AA2JpE,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa,EAC5B,iBAAiB,EAAE,2BAA2B,EAC9C,cAAc,EAAE,cAAc,EAC9B,sBAAsB,CAAC,EAAE,cAAc,EACvC,iBAAiB,CAAC,EAAE,wBAAwB,EAC5C,cAAc,CAAC,EAAE,MAAM,EACvB,sBAAsB,CAAC,EAAE,cAAc,GACtC,IAAI,CAyKN"}
|
|
@@ -1,11 +1,100 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import {
|
|
3
|
-
import { AUTH_CONFIG, STREAMABLE_HTTP_ENDPOINT } from '../../../constants.js';
|
|
2
|
+
import { AUTH_CONFIG, MCP_SERVER_NAME, MCP_SERVER_VERSION, STREAMABLE_HTTP_ENDPOINT } from '../../../constants.js';
|
|
4
3
|
import logger from '../../../logger/logger.js';
|
|
5
4
|
import { getPresetName, getTagExpression, getTagFilterMode, getTagQuery, getValidatedTags, } from '../../../transport/http/middlewares/scopeAuthMiddleware.js';
|
|
6
5
|
import tagsExtractor from '../../../transport/http/middlewares/tagsExtractor.js';
|
|
6
|
+
import { RestorableStreamableHTTPServerTransport } from '../../../transport/http/restorableStreamableTransport.js';
|
|
7
7
|
import { extractContextFromMeta } from '../../../transport/http/utils/contextExtractor.js';
|
|
8
|
+
import { sendBadRequest, sendInternalError, sendNotFound } from '../../../transport/http/utils/httpErrorHandler.js';
|
|
8
9
|
import { SessionService } from '../../../transport/http/utils/sessionService.js';
|
|
10
|
+
import { logError, logWarn } from '../../../transport/http/utils/unifiedLogger.js';
|
|
11
|
+
/**
|
|
12
|
+
* Type guard to check if a request body is an initialize request.
|
|
13
|
+
*/
|
|
14
|
+
function isInitializeRequest(body) {
|
|
15
|
+
return (typeof body === 'object' &&
|
|
16
|
+
body !== null &&
|
|
17
|
+
'method' in body &&
|
|
18
|
+
body.method === 'initialize' &&
|
|
19
|
+
'jsonrpc' in body &&
|
|
20
|
+
body.jsonrpc === '2.0');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Extract protocol version from initialize request body.
|
|
24
|
+
* Similar to contextExtractor pattern - extract directly from req.body.params
|
|
25
|
+
*/
|
|
26
|
+
function extractProtocolVersion(body) {
|
|
27
|
+
try {
|
|
28
|
+
const reqBody = body;
|
|
29
|
+
if (reqBody?.params?.protocolVersion && typeof reqBody.params.protocolVersion === 'string') {
|
|
30
|
+
return reqBody.params.protocolVersion;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
logWarn('Failed to extract protocol version from request body', {
|
|
36
|
+
reason: 'Request body structure incompatible',
|
|
37
|
+
context: { error },
|
|
38
|
+
});
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Wraps an Express response to log when status >= 400 is set.
|
|
44
|
+
* This catches SDK responses that don't throw errors.
|
|
45
|
+
*/
|
|
46
|
+
function wrapResponseForLogging(req, res, sessionId) {
|
|
47
|
+
const originalStatus = res.status.bind(res);
|
|
48
|
+
const originalJson = res.json.bind(res);
|
|
49
|
+
let logged = false;
|
|
50
|
+
const logIfNeeded = (responseBody) => {
|
|
51
|
+
if (logged || res.statusCode < 400)
|
|
52
|
+
return;
|
|
53
|
+
logged = true;
|
|
54
|
+
const level = res.statusCode >= 500 ? 'error' : 'warn';
|
|
55
|
+
const logFn = level === 'error' ? logError : logWarn;
|
|
56
|
+
logFn(`HTTP error ${res.statusCode}`, {
|
|
57
|
+
method: req.method,
|
|
58
|
+
path: req.path,
|
|
59
|
+
sessionId,
|
|
60
|
+
statusCode: res.statusCode,
|
|
61
|
+
reason: 'SDK request validation failed',
|
|
62
|
+
});
|
|
63
|
+
// Log request/response details at debug level for troubleshooting
|
|
64
|
+
logger.debug('SDK error details', {
|
|
65
|
+
sessionId,
|
|
66
|
+
requestBody: req.body,
|
|
67
|
+
responseBody,
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
// Intercept res.status() to detect when error codes are set
|
|
71
|
+
res.status = function (code) {
|
|
72
|
+
const result = originalStatus(code);
|
|
73
|
+
if (code >= 400) {
|
|
74
|
+
logIfNeeded();
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
};
|
|
78
|
+
// Intercept res.json() to capture response body for error logging
|
|
79
|
+
res.json = function (body) {
|
|
80
|
+
if (res.statusCode >= 400) {
|
|
81
|
+
logIfNeeded(body);
|
|
82
|
+
}
|
|
83
|
+
return originalJson(body);
|
|
84
|
+
};
|
|
85
|
+
// Intercept res.writeHead() - SDK uses this via Hono adapter
|
|
86
|
+
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument */
|
|
87
|
+
const origWriteHead = res.writeHead.bind(res);
|
|
88
|
+
res.writeHead = function (statusCode, ...rest) {
|
|
89
|
+
if (statusCode >= 400) {
|
|
90
|
+
res.statusCode = statusCode;
|
|
91
|
+
logIfNeeded();
|
|
92
|
+
}
|
|
93
|
+
return origWriteHead(statusCode, ...rest);
|
|
94
|
+
};
|
|
95
|
+
/* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument */
|
|
96
|
+
return res;
|
|
97
|
+
}
|
|
9
98
|
/**
|
|
10
99
|
* Builds the inbound connection config from request data.
|
|
11
100
|
*
|
|
@@ -63,10 +152,12 @@ export function setupStreamableHttpRoutes(router, serverManager, sessionReposito
|
|
|
63
152
|
router.post(STREAMABLE_HTTP_ENDPOINT, ...middlewares, async (req, res) => {
|
|
64
153
|
try {
|
|
65
154
|
let transport;
|
|
155
|
+
let actualSessionId;
|
|
66
156
|
const sessionId = req.headers['mcp-session-id'];
|
|
67
157
|
if (!sessionId) {
|
|
68
158
|
// Generate new session ID
|
|
69
159
|
const id = AUTH_CONFIG.SERVER.STREAMABLE_SESSION.ID_PREFIX + randomUUID();
|
|
160
|
+
actualSessionId = id;
|
|
70
161
|
const config = buildConfigFromRequest(res, req, customTemplate);
|
|
71
162
|
// Extract context from _meta field (from STDIO proxy)
|
|
72
163
|
const context = extractContextFromMeta(req);
|
|
@@ -78,31 +169,69 @@ export function setupStreamableHttpRoutes(router, serverManager, sessionReposito
|
|
|
78
169
|
}
|
|
79
170
|
}
|
|
80
171
|
else {
|
|
172
|
+
actualSessionId = sessionId;
|
|
81
173
|
transport = await sessionService.getSession(sessionId);
|
|
82
174
|
if (!transport) {
|
|
83
|
-
// Session
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
175
|
+
// Session doesn't exist - only allow creation via initialize request
|
|
176
|
+
if (isInitializeRequest(req.body)) {
|
|
177
|
+
// Allow creating new session with specific ID via initialize
|
|
178
|
+
logger.info(`Creating new session ${sessionId} via initialize request`);
|
|
179
|
+
const config = buildConfigFromRequest(res, req, customTemplate);
|
|
180
|
+
// Extract context from _meta field (from STDIO proxy)
|
|
181
|
+
const context = extractContextFromMeta(req);
|
|
182
|
+
const createResult = await sessionService.createSession(config, context || undefined, sessionId);
|
|
183
|
+
transport = createResult.transport;
|
|
184
|
+
// Log warning if session was not persisted
|
|
185
|
+
if (!createResult.persisted) {
|
|
186
|
+
logger.warn(`New session ${sessionId} was created but not persisted: ${createResult.persistenceError}`);
|
|
187
|
+
}
|
|
93
188
|
}
|
|
189
|
+
else {
|
|
190
|
+
// Non-initialize request for non-existent session → 404
|
|
191
|
+
sendNotFound(res, 'Session not found. Send an initialize request first to create a new session.', {
|
|
192
|
+
sessionId,
|
|
193
|
+
});
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Wrap response to catch SDK errors
|
|
199
|
+
const wrappedRes = wrapResponseForLogging(req, res, actualSessionId);
|
|
200
|
+
// Check if this is an initialize request
|
|
201
|
+
const isInitialize = isInitializeRequest(req.body);
|
|
202
|
+
const protocolVersion = isInitialize ? extractProtocolVersion(req.body) : null;
|
|
203
|
+
// Log request details for debugging restored sessions
|
|
204
|
+
if (transport instanceof RestorableStreamableHTTPServerTransport && transport.isRestored()) {
|
|
205
|
+
logger.debug('Handling request for restored session', {
|
|
206
|
+
sessionId: actualSessionId,
|
|
207
|
+
isInitialize,
|
|
208
|
+
method: req.body?.method,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
await transport.handleRequest(req, wrappedRes, req.body);
|
|
212
|
+
// After handleRequest completes for initialize, store the response data
|
|
213
|
+
if (isInitialize && protocolVersion) {
|
|
214
|
+
try {
|
|
215
|
+
// Store minimal initialize response data for session restoration
|
|
216
|
+
// The exact capabilities don't matter for restoration - we just need protocol version and server info
|
|
217
|
+
sessionService.storeInitializeResponse(actualSessionId, {
|
|
218
|
+
protocolVersion,
|
|
219
|
+
capabilities: {}, // Minimal capabilities - actual capabilities are aggregated dynamically
|
|
220
|
+
serverInfo: { name: MCP_SERVER_NAME, version: MCP_SERVER_VERSION },
|
|
221
|
+
});
|
|
222
|
+
logger.debug(`Stored initialize response for session ${actualSessionId}`);
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
logger.warn(`Failed to store initialize response for ${actualSessionId}:`, err);
|
|
94
226
|
}
|
|
95
227
|
}
|
|
96
|
-
await transport.handleRequest(req, res, req.body);
|
|
97
228
|
}
|
|
98
229
|
catch (error) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
message: 'An internal server error occurred while processing the request',
|
|
105
|
-
},
|
|
230
|
+
sendInternalError(res, error, {
|
|
231
|
+
method: req.method,
|
|
232
|
+
path: req.path,
|
|
233
|
+
sessionId: req.headers['mcp-session-id'],
|
|
234
|
+
phase: 'handleRequest',
|
|
106
235
|
});
|
|
107
236
|
}
|
|
108
237
|
});
|
|
@@ -110,36 +239,26 @@ export function setupStreamableHttpRoutes(router, serverManager, sessionReposito
|
|
|
110
239
|
try {
|
|
111
240
|
const sessionId = req.headers['mcp-session-id'];
|
|
112
241
|
if (!sessionId) {
|
|
113
|
-
res
|
|
114
|
-
error: {
|
|
115
|
-
code: ErrorCode.InvalidParams,
|
|
116
|
-
message: 'Invalid params: sessionId is required',
|
|
117
|
-
},
|
|
118
|
-
});
|
|
242
|
+
sendBadRequest(res, 'Invalid params: sessionId is required');
|
|
119
243
|
return;
|
|
120
244
|
}
|
|
121
245
|
const transport = await sessionService.getSession(sessionId);
|
|
122
246
|
if (!transport) {
|
|
123
|
-
res
|
|
124
|
-
error: {
|
|
125
|
-
code: ErrorCode.InvalidParams,
|
|
126
|
-
message: 'No active streamable HTTP session found for the provided sessionId',
|
|
127
|
-
},
|
|
128
|
-
});
|
|
247
|
+
sendNotFound(res, 'No active streamable HTTP session found for the provided sessionId', { sessionId });
|
|
129
248
|
return;
|
|
130
249
|
}
|
|
131
250
|
// Set up disconnect detection for SSE stream (GET endpoint maintains persistent connection)
|
|
132
251
|
setupDisconnectDetection(req, res, sessionId, serverManager);
|
|
133
|
-
|
|
252
|
+
// Wrap response to catch SDK errors
|
|
253
|
+
const wrappedRes = wrapResponseForLogging(req, res, sessionId);
|
|
254
|
+
await transport.handleRequest(req, wrappedRes, req.body);
|
|
134
255
|
}
|
|
135
256
|
catch (error) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
message: 'An internal server error occurred while processing the request',
|
|
142
|
-
},
|
|
257
|
+
sendInternalError(res, error, {
|
|
258
|
+
method: req.method,
|
|
259
|
+
path: req.path,
|
|
260
|
+
sessionId: req.headers['mcp-session-id'],
|
|
261
|
+
phase: 'handleRequest',
|
|
143
262
|
});
|
|
144
263
|
}
|
|
145
264
|
});
|
|
@@ -147,36 +266,26 @@ export function setupStreamableHttpRoutes(router, serverManager, sessionReposito
|
|
|
147
266
|
try {
|
|
148
267
|
const sessionId = req.headers['mcp-session-id'];
|
|
149
268
|
if (!sessionId) {
|
|
150
|
-
res
|
|
151
|
-
error: {
|
|
152
|
-
code: ErrorCode.InvalidParams,
|
|
153
|
-
message: 'Invalid params: sessionId is required',
|
|
154
|
-
},
|
|
155
|
-
});
|
|
269
|
+
sendBadRequest(res, 'Invalid params: sessionId is required');
|
|
156
270
|
return;
|
|
157
271
|
}
|
|
158
272
|
const transport = await sessionService.getSession(sessionId);
|
|
159
273
|
if (!transport) {
|
|
160
|
-
res
|
|
161
|
-
error: {
|
|
162
|
-
code: ErrorCode.InvalidParams,
|
|
163
|
-
message: 'No active streamable HTTP session found for the provided sessionId',
|
|
164
|
-
},
|
|
165
|
-
});
|
|
274
|
+
sendNotFound(res, 'No active streamable HTTP session found for the provided sessionId', { sessionId });
|
|
166
275
|
return;
|
|
167
276
|
}
|
|
168
|
-
|
|
277
|
+
// Wrap response to catch SDK errors
|
|
278
|
+
const wrappedRes = wrapResponseForLogging(req, res, sessionId);
|
|
279
|
+
await transport.handleRequest(req, wrappedRes);
|
|
169
280
|
// Delete session from storage after explicit delete request
|
|
170
281
|
await sessionService.deleteSession(sessionId);
|
|
171
282
|
}
|
|
172
283
|
catch (error) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
message: 'An internal server error occurred while processing the request',
|
|
179
|
-
},
|
|
284
|
+
sendInternalError(res, error, {
|
|
285
|
+
method: req.method,
|
|
286
|
+
path: req.path,
|
|
287
|
+
sessionId: req.headers['mcp-session-id'],
|
|
288
|
+
phase: 'handleRequest',
|
|
180
289
|
});
|
|
181
290
|
}
|
|
182
291
|
});
|