@juspay/neurolink 8.28.0 → 8.30.0

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 (83) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +23 -2
  3. package/dist/adapters/video/vertexVideoHandler.d.ts +12 -2
  4. package/dist/adapters/video/vertexVideoHandler.js +12 -2
  5. package/dist/core/baseProvider.d.ts +19 -0
  6. package/dist/core/baseProvider.js +174 -0
  7. package/dist/index.d.ts +3 -3
  8. package/dist/index.js +7 -1
  9. package/dist/lib/adapters/video/vertexVideoHandler.d.ts +12 -2
  10. package/dist/lib/adapters/video/vertexVideoHandler.js +12 -2
  11. package/dist/lib/core/baseProvider.d.ts +19 -0
  12. package/dist/lib/core/baseProvider.js +174 -0
  13. package/dist/lib/index.d.ts +3 -3
  14. package/dist/lib/index.js +7 -1
  15. package/dist/lib/mcp/auth/index.d.ts +6 -0
  16. package/dist/lib/mcp/auth/index.js +12 -0
  17. package/dist/lib/mcp/auth/oauthClientProvider.d.ts +93 -0
  18. package/dist/lib/mcp/auth/oauthClientProvider.js +326 -0
  19. package/dist/lib/mcp/auth/tokenStorage.d.ts +56 -0
  20. package/dist/lib/mcp/auth/tokenStorage.js +135 -0
  21. package/dist/lib/mcp/externalServerManager.d.ts +5 -1
  22. package/dist/lib/mcp/externalServerManager.js +84 -22
  23. package/dist/lib/mcp/httpRateLimiter.d.ts +152 -0
  24. package/dist/lib/mcp/httpRateLimiter.js +365 -0
  25. package/dist/lib/mcp/httpRetryHandler.d.ts +62 -0
  26. package/dist/lib/mcp/httpRetryHandler.js +154 -0
  27. package/dist/lib/mcp/index.d.ts +5 -0
  28. package/dist/lib/mcp/index.js +8 -0
  29. package/dist/lib/mcp/mcpClientFactory.d.ts +25 -2
  30. package/dist/lib/mcp/mcpClientFactory.js +206 -10
  31. package/dist/lib/mcp/toolRegistry.d.ts +1 -2
  32. package/dist/lib/mcp/toolRegistry.js +1 -5
  33. package/dist/lib/neurolink.js +3 -0
  34. package/dist/lib/providers/amazonBedrock.js +4 -1
  35. package/dist/lib/providers/ollama.js +4 -1
  36. package/dist/lib/sdk/toolRegistration.d.ts +3 -25
  37. package/dist/lib/types/cli.d.ts +42 -42
  38. package/dist/lib/types/externalMcp.d.ts +55 -3
  39. package/dist/lib/types/externalMcp.js +0 -1
  40. package/dist/lib/types/generateTypes.d.ts +37 -0
  41. package/dist/lib/types/hitlTypes.d.ts +38 -0
  42. package/dist/lib/types/index.d.ts +6 -8
  43. package/dist/lib/types/index.js +4 -4
  44. package/dist/lib/types/mcpTypes.d.ts +235 -27
  45. package/dist/lib/types/providers.d.ts +16 -16
  46. package/dist/lib/types/sdkTypes.d.ts +2 -2
  47. package/dist/lib/types/tools.d.ts +42 -3
  48. package/dist/lib/types/utilities.d.ts +19 -0
  49. package/dist/mcp/auth/index.d.ts +6 -0
  50. package/dist/mcp/auth/index.js +11 -0
  51. package/dist/mcp/auth/oauthClientProvider.d.ts +93 -0
  52. package/dist/mcp/auth/oauthClientProvider.js +325 -0
  53. package/dist/mcp/auth/tokenStorage.d.ts +56 -0
  54. package/dist/mcp/auth/tokenStorage.js +134 -0
  55. package/dist/mcp/externalServerManager.d.ts +5 -1
  56. package/dist/mcp/externalServerManager.js +84 -22
  57. package/dist/mcp/httpRateLimiter.d.ts +152 -0
  58. package/dist/mcp/httpRateLimiter.js +364 -0
  59. package/dist/mcp/httpRetryHandler.d.ts +62 -0
  60. package/dist/mcp/httpRetryHandler.js +153 -0
  61. package/dist/mcp/index.d.ts +5 -0
  62. package/dist/mcp/index.js +8 -0
  63. package/dist/mcp/mcpClientFactory.d.ts +25 -2
  64. package/dist/mcp/mcpClientFactory.js +206 -10
  65. package/dist/mcp/toolRegistry.d.ts +1 -2
  66. package/dist/mcp/toolRegistry.js +1 -5
  67. package/dist/neurolink.js +3 -0
  68. package/dist/providers/amazonBedrock.js +4 -1
  69. package/dist/providers/ollama.js +4 -1
  70. package/dist/sdk/toolRegistration.d.ts +3 -25
  71. package/dist/types/cli.d.ts +42 -42
  72. package/dist/types/externalMcp.d.ts +55 -3
  73. package/dist/types/externalMcp.js +0 -1
  74. package/dist/types/generateTypes.d.ts +37 -0
  75. package/dist/types/hitlTypes.d.ts +38 -0
  76. package/dist/types/index.d.ts +6 -8
  77. package/dist/types/index.js +4 -4
  78. package/dist/types/mcpTypes.d.ts +235 -27
  79. package/dist/types/providers.d.ts +16 -16
  80. package/dist/types/sdkTypes.d.ts +2 -2
  81. package/dist/types/tools.d.ts +42 -3
  82. package/dist/types/utilities.d.ts +19 -0
  83. package/package.json +2 -1
@@ -0,0 +1,93 @@
1
+ /**
2
+ * OAuth 2.1 Client Provider for MCP HTTP Transport
3
+ * Implements OAuth 2.1 authentication with PKCE support
4
+ */
5
+ import type { OAuthTokens, TokenStorage, MCPOAuthConfig, OAuthClientInformation, AuthorizationUrlResult, TokenExchangeRequest } from "../../types/mcpTypes.js";
6
+ /**
7
+ * NeuroLink OAuth Provider for MCP HTTP Transport
8
+ * Handles OAuth 2.1 authentication flow with optional PKCE support
9
+ */
10
+ export declare class NeuroLinkOAuthProvider {
11
+ private config;
12
+ private storage;
13
+ private pendingChallenges;
14
+ private pendingStates;
15
+ constructor(config: MCPOAuthConfig, storage?: TokenStorage);
16
+ /**
17
+ * Get stored tokens for a server
18
+ * Returns null if tokens are not available or expired (without refresh token)
19
+ */
20
+ tokens(serverId: string): Promise<OAuthTokens | null>;
21
+ /**
22
+ * Save tokens for a server
23
+ */
24
+ saveTokens(serverId: string, tokens: OAuthTokens): Promise<void>;
25
+ /**
26
+ * Delete tokens for a server
27
+ */
28
+ deleteTokens(serverId: string): Promise<void>;
29
+ /**
30
+ * Get client information for MCP SDK
31
+ */
32
+ clientInformation(): OAuthClientInformation;
33
+ /**
34
+ * Generate authorization URL for OAuth flow
35
+ * Returns the URL to redirect the user to for authorization
36
+ * @param _serverId - Server ID (reserved for future use in state management)
37
+ */
38
+ redirectToAuthorization(_serverId: string): AuthorizationUrlResult;
39
+ /**
40
+ * Exchange authorization code for tokens
41
+ */
42
+ exchangeCode(serverId: string, request: TokenExchangeRequest): Promise<OAuthTokens>;
43
+ /**
44
+ * Refresh tokens using refresh token
45
+ */
46
+ refreshTokens(serverId: string, refreshToken: string): Promise<OAuthTokens>;
47
+ /**
48
+ * Revoke tokens (if supported by the OAuth server)
49
+ */
50
+ revokeTokens(serverId: string, revocationUrl: string): Promise<void>;
51
+ /**
52
+ * Get authorization header value for API requests
53
+ */
54
+ getAuthorizationHeader(serverId: string): Promise<string | null>;
55
+ /**
56
+ * Check if a server has valid (non-expired) tokens
57
+ */
58
+ hasValidTokens(serverId: string): Promise<boolean>;
59
+ /**
60
+ * Generate a cryptographically secure state parameter
61
+ */
62
+ private generateState;
63
+ /**
64
+ * Generate PKCE code verifier and challenge
65
+ * Uses SHA-256 for code challenge method (required by OAuth 2.1)
66
+ */
67
+ private generatePKCE;
68
+ /**
69
+ * Get the OAuth configuration
70
+ */
71
+ getConfig(): Readonly<MCPOAuthConfig>;
72
+ /**
73
+ * Get the token storage instance
74
+ */
75
+ getStorage(): TokenStorage;
76
+ /**
77
+ * Clean up expired pending states and challenges
78
+ * Should be called periodically to prevent memory leaks
79
+ */
80
+ cleanupPendingRequests(): void;
81
+ }
82
+ /**
83
+ * Create an OAuth provider from MCP server auth configuration
84
+ */
85
+ export declare function createOAuthProviderFromConfig(authConfig: {
86
+ clientId: string;
87
+ clientSecret?: string;
88
+ authorizationUrl: string;
89
+ tokenUrl: string;
90
+ redirectUrl: string;
91
+ scope?: string;
92
+ usePKCE?: boolean;
93
+ }, storage?: TokenStorage): NeuroLinkOAuthProvider;
@@ -0,0 +1,325 @@
1
+ /**
2
+ * OAuth 2.1 Client Provider for MCP HTTP Transport
3
+ * Implements OAuth 2.1 authentication with PKCE support
4
+ */
5
+ import { randomBytes, createHash } from "crypto";
6
+ import { InMemoryTokenStorage, isTokenExpired, calculateExpiresAt, } from "./tokenStorage.js";
7
+ import { logger } from "../../utils/logger.js";
8
+ /**
9
+ * NeuroLink OAuth Provider for MCP HTTP Transport
10
+ * Handles OAuth 2.1 authentication flow with optional PKCE support
11
+ */
12
+ export class NeuroLinkOAuthProvider {
13
+ config;
14
+ storage;
15
+ pendingChallenges = new Map();
16
+ pendingStates = new Set();
17
+ constructor(config, storage) {
18
+ this.config = {
19
+ ...config,
20
+ usePKCE: config.usePKCE ?? true, // PKCE enabled by default for OAuth 2.1
21
+ };
22
+ this.storage = storage ?? new InMemoryTokenStorage();
23
+ }
24
+ /**
25
+ * Get stored tokens for a server
26
+ * Returns null if tokens are not available or expired (without refresh token)
27
+ */
28
+ async tokens(serverId) {
29
+ const tokens = await this.storage.getTokens(serverId);
30
+ if (!tokens) {
31
+ return null;
32
+ }
33
+ // Check if tokens are expired
34
+ if (isTokenExpired(tokens)) {
35
+ // Try to refresh if refresh token is available
36
+ if (tokens.refreshToken) {
37
+ try {
38
+ const refreshedTokens = await this.refreshTokens(serverId, tokens.refreshToken);
39
+ return refreshedTokens;
40
+ }
41
+ catch (error) {
42
+ logger.warn(`[NeuroLinkOAuthProvider] Token refresh failed: ${error instanceof Error ? error.message : String(error)}`);
43
+ // Delete expired tokens if refresh fails
44
+ await this.storage.deleteTokens(serverId);
45
+ return null;
46
+ }
47
+ }
48
+ // No refresh token, delete expired tokens
49
+ await this.storage.deleteTokens(serverId);
50
+ return null;
51
+ }
52
+ return tokens;
53
+ }
54
+ /**
55
+ * Save tokens for a server
56
+ */
57
+ async saveTokens(serverId, tokens) {
58
+ await this.storage.saveTokens(serverId, tokens);
59
+ }
60
+ /**
61
+ * Delete tokens for a server
62
+ */
63
+ async deleteTokens(serverId) {
64
+ await this.storage.deleteTokens(serverId);
65
+ }
66
+ /**
67
+ * Get client information for MCP SDK
68
+ */
69
+ clientInformation() {
70
+ return {
71
+ clientId: this.config.clientId,
72
+ clientSecret: this.config.clientSecret,
73
+ redirectUri: this.config.redirectUrl,
74
+ };
75
+ }
76
+ /**
77
+ * Generate authorization URL for OAuth flow
78
+ * Returns the URL to redirect the user to for authorization
79
+ * @param _serverId - Server ID (reserved for future use in state management)
80
+ */
81
+ redirectToAuthorization(_serverId) {
82
+ // Generate state parameter for CSRF protection
83
+ const state = this.generateState();
84
+ this.pendingStates.add(state);
85
+ // Build authorization URL
86
+ const url = new URL(this.config.authorizationUrl);
87
+ // Required OAuth 2.1 parameters
88
+ url.searchParams.set("response_type", "code");
89
+ url.searchParams.set("client_id", this.config.clientId);
90
+ url.searchParams.set("redirect_uri", this.config.redirectUrl);
91
+ url.searchParams.set("state", state);
92
+ // Optional scope
93
+ if (this.config.scope) {
94
+ url.searchParams.set("scope", this.config.scope);
95
+ }
96
+ // PKCE support
97
+ let codeVerifier;
98
+ if (this.config.usePKCE) {
99
+ const pkce = this.generatePKCE();
100
+ codeVerifier = pkce.codeVerifier;
101
+ // Store PKCE challenge for later verification
102
+ this.pendingChallenges.set(state, pkce);
103
+ url.searchParams.set("code_challenge", pkce.codeChallenge);
104
+ url.searchParams.set("code_challenge_method", pkce.codeChallengeMethod);
105
+ }
106
+ // Additional custom parameters
107
+ if (this.config.additionalParams) {
108
+ for (const [key, value] of Object.entries(this.config.additionalParams)) {
109
+ url.searchParams.set(key, value);
110
+ }
111
+ }
112
+ return {
113
+ url: url.toString(),
114
+ state,
115
+ codeVerifier,
116
+ };
117
+ }
118
+ /**
119
+ * Exchange authorization code for tokens
120
+ */
121
+ async exchangeCode(serverId, request) {
122
+ // Validate state
123
+ if (!this.pendingStates.has(request.state)) {
124
+ throw new Error("Invalid or expired state parameter");
125
+ }
126
+ this.pendingStates.delete(request.state);
127
+ // Get PKCE verifier if applicable
128
+ let codeVerifier = request.codeVerifier;
129
+ if (this.config.usePKCE && !codeVerifier) {
130
+ const pkce = this.pendingChallenges.get(request.state);
131
+ if (pkce) {
132
+ codeVerifier = pkce.codeVerifier;
133
+ this.pendingChallenges.delete(request.state);
134
+ }
135
+ }
136
+ // Build token request
137
+ const body = new URLSearchParams();
138
+ body.set("grant_type", "authorization_code");
139
+ body.set("code", request.code);
140
+ body.set("redirect_uri", this.config.redirectUrl);
141
+ body.set("client_id", this.config.clientId);
142
+ // Include client secret if available (confidential clients)
143
+ if (this.config.clientSecret) {
144
+ body.set("client_secret", this.config.clientSecret);
145
+ }
146
+ // Include PKCE verifier if applicable
147
+ if (codeVerifier) {
148
+ body.set("code_verifier", codeVerifier);
149
+ }
150
+ // Request tokens
151
+ const response = await fetch(this.config.tokenUrl, {
152
+ method: "POST",
153
+ headers: {
154
+ "Content-Type": "application/x-www-form-urlencoded",
155
+ Accept: "application/json",
156
+ },
157
+ body: body.toString(),
158
+ });
159
+ if (!response.ok) {
160
+ const errorText = await response.text();
161
+ throw new Error(`Token exchange failed: ${response.status} ${response.statusText} - ${errorText}`);
162
+ }
163
+ const tokenResponse = (await response.json());
164
+ // Convert to OAuthTokens format
165
+ const tokens = {
166
+ accessToken: tokenResponse.access_token,
167
+ refreshToken: tokenResponse.refresh_token,
168
+ expiresAt: tokenResponse.expires_in
169
+ ? calculateExpiresAt(tokenResponse.expires_in)
170
+ : undefined,
171
+ tokenType: tokenResponse.token_type ?? "Bearer",
172
+ scope: tokenResponse.scope,
173
+ };
174
+ // Save tokens
175
+ await this.saveTokens(serverId, tokens);
176
+ return tokens;
177
+ }
178
+ /**
179
+ * Refresh tokens using refresh token
180
+ */
181
+ async refreshTokens(serverId, refreshToken) {
182
+ const body = new URLSearchParams();
183
+ body.set("grant_type", "refresh_token");
184
+ body.set("refresh_token", refreshToken);
185
+ body.set("client_id", this.config.clientId);
186
+ if (this.config.clientSecret) {
187
+ body.set("client_secret", this.config.clientSecret);
188
+ }
189
+ const response = await fetch(this.config.tokenUrl, {
190
+ method: "POST",
191
+ headers: {
192
+ "Content-Type": "application/x-www-form-urlencoded",
193
+ Accept: "application/json",
194
+ },
195
+ body: body.toString(),
196
+ });
197
+ if (!response.ok) {
198
+ const errorText = await response.text();
199
+ throw new Error(`Token refresh failed: ${response.status} ${response.statusText} - ${errorText}`);
200
+ }
201
+ const tokenResponse = (await response.json());
202
+ const tokens = {
203
+ accessToken: tokenResponse.access_token,
204
+ // Keep old refresh token if new one not provided
205
+ refreshToken: tokenResponse.refresh_token ?? refreshToken,
206
+ expiresAt: tokenResponse.expires_in
207
+ ? calculateExpiresAt(tokenResponse.expires_in)
208
+ : undefined,
209
+ tokenType: tokenResponse.token_type ?? "Bearer",
210
+ scope: tokenResponse.scope,
211
+ };
212
+ await this.saveTokens(serverId, tokens);
213
+ return tokens;
214
+ }
215
+ /**
216
+ * Revoke tokens (if supported by the OAuth server)
217
+ */
218
+ async revokeTokens(serverId, revocationUrl) {
219
+ const tokens = await this.storage.getTokens(serverId);
220
+ if (!tokens) {
221
+ return;
222
+ }
223
+ const body = new URLSearchParams();
224
+ body.set("token", tokens.accessToken);
225
+ body.set("client_id", this.config.clientId);
226
+ if (this.config.clientSecret) {
227
+ body.set("client_secret", this.config.clientSecret);
228
+ }
229
+ try {
230
+ await fetch(revocationUrl, {
231
+ method: "POST",
232
+ headers: {
233
+ "Content-Type": "application/x-www-form-urlencoded",
234
+ },
235
+ body: body.toString(),
236
+ });
237
+ }
238
+ catch (error) {
239
+ logger.warn(`[NeuroLinkOAuthProvider] Token revocation failed: ${error instanceof Error ? error.message : String(error)}`);
240
+ }
241
+ // Always delete local tokens
242
+ await this.storage.deleteTokens(serverId);
243
+ }
244
+ /**
245
+ * Get authorization header value for API requests
246
+ */
247
+ async getAuthorizationHeader(serverId) {
248
+ const tokens = await this.tokens(serverId);
249
+ if (!tokens) {
250
+ return null;
251
+ }
252
+ return `${tokens.tokenType} ${tokens.accessToken}`;
253
+ }
254
+ /**
255
+ * Check if a server has valid (non-expired) tokens
256
+ */
257
+ async hasValidTokens(serverId) {
258
+ const tokens = await this.tokens(serverId);
259
+ return tokens !== null;
260
+ }
261
+ /**
262
+ * Generate a cryptographically secure state parameter
263
+ */
264
+ generateState() {
265
+ return randomBytes(32).toString("base64url");
266
+ }
267
+ /**
268
+ * Generate PKCE code verifier and challenge
269
+ * Uses SHA-256 for code challenge method (required by OAuth 2.1)
270
+ */
271
+ generatePKCE() {
272
+ // Generate code verifier (43-128 characters, URL-safe)
273
+ const codeVerifier = randomBytes(32).toString("base64url");
274
+ // Generate code challenge using SHA-256
275
+ const codeChallenge = createHash("sha256")
276
+ .update(codeVerifier)
277
+ .digest("base64url");
278
+ return {
279
+ codeVerifier,
280
+ codeChallenge,
281
+ codeChallengeMethod: "S256",
282
+ };
283
+ }
284
+ /**
285
+ * Get the OAuth configuration
286
+ */
287
+ getConfig() {
288
+ return { ...this.config };
289
+ }
290
+ /**
291
+ * Get the token storage instance
292
+ */
293
+ getStorage() {
294
+ return this.storage;
295
+ }
296
+ /**
297
+ * Clean up expired pending states and challenges
298
+ * Should be called periodically to prevent memory leaks
299
+ */
300
+ cleanupPendingRequests() {
301
+ // Clear old pending states (older than 10 minutes)
302
+ // Note: In a production system, you'd want to track timestamps
303
+ // For now, we just clear all if there are too many
304
+ if (this.pendingStates.size > 100) {
305
+ this.pendingStates.clear();
306
+ }
307
+ if (this.pendingChallenges.size > 100) {
308
+ this.pendingChallenges.clear();
309
+ }
310
+ }
311
+ }
312
+ /**
313
+ * Create an OAuth provider from MCP server auth configuration
314
+ */
315
+ export function createOAuthProviderFromConfig(authConfig, storage) {
316
+ return new NeuroLinkOAuthProvider({
317
+ clientId: authConfig.clientId,
318
+ clientSecret: authConfig.clientSecret,
319
+ authorizationUrl: authConfig.authorizationUrl,
320
+ tokenUrl: authConfig.tokenUrl,
321
+ redirectUrl: authConfig.redirectUrl,
322
+ scope: authConfig.scope,
323
+ usePKCE: authConfig.usePKCE ?? true,
324
+ }, storage);
325
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Token Storage for OAuth 2.1 authentication
3
+ * Provides implementations for storing OAuth tokens
4
+ */
5
+ import type { OAuthTokens, TokenStorage } from "../../types/mcpTypes.js";
6
+ /**
7
+ * In-memory token storage implementation
8
+ * Suitable for development and single-session use
9
+ * Tokens are lost when the process terminates
10
+ */
11
+ export declare class InMemoryTokenStorage implements TokenStorage {
12
+ private tokens;
13
+ getTokens(serverId: string): Promise<OAuthTokens | null>;
14
+ saveTokens(serverId: string, tokens: OAuthTokens): Promise<void>;
15
+ deleteTokens(serverId: string): Promise<void>;
16
+ hasTokens(serverId: string): Promise<boolean>;
17
+ clearAll(): Promise<void>;
18
+ /**
19
+ * Get the number of stored token sets
20
+ */
21
+ get size(): number;
22
+ /**
23
+ * Get all server IDs with stored tokens
24
+ */
25
+ getServerIds(): string[];
26
+ }
27
+ /**
28
+ * File-based token storage implementation
29
+ * Persists tokens to disk for cross-session use
30
+ */
31
+ export declare class FileTokenStorage implements TokenStorage {
32
+ private filePath;
33
+ private tokens;
34
+ private loaded;
35
+ constructor(filePath: string);
36
+ private loadTokens;
37
+ private saveToFile;
38
+ getTokens(serverId: string): Promise<OAuthTokens | null>;
39
+ saveTokens(serverId: string, tokens: OAuthTokens): Promise<void>;
40
+ deleteTokens(serverId: string): Promise<void>;
41
+ hasTokens(serverId: string): Promise<boolean>;
42
+ clearAll(): Promise<void>;
43
+ }
44
+ /**
45
+ * Check if tokens are expired or about to expire
46
+ * @param tokens - OAuth tokens to check
47
+ * @param bufferSeconds - Buffer time in seconds before expiration (default: 60)
48
+ * @returns True if tokens are expired or will expire within buffer time
49
+ */
50
+ export declare function isTokenExpired(tokens: OAuthTokens, bufferSeconds?: number): boolean;
51
+ /**
52
+ * Calculate token expiration timestamp from expires_in value
53
+ * @param expiresIn - Token lifetime in seconds
54
+ * @returns Expiration timestamp (Unix epoch in milliseconds)
55
+ */
56
+ export declare function calculateExpiresAt(expiresIn: number): number;
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Token Storage for OAuth 2.1 authentication
3
+ * Provides implementations for storing OAuth tokens
4
+ */
5
+ import { logger } from "../../utils/logger.js";
6
+ /**
7
+ * In-memory token storage implementation
8
+ * Suitable for development and single-session use
9
+ * Tokens are lost when the process terminates
10
+ */
11
+ export class InMemoryTokenStorage {
12
+ tokens = new Map();
13
+ async getTokens(serverId) {
14
+ return this.tokens.get(serverId) ?? null;
15
+ }
16
+ async saveTokens(serverId, tokens) {
17
+ this.tokens.set(serverId, tokens);
18
+ }
19
+ async deleteTokens(serverId) {
20
+ this.tokens.delete(serverId);
21
+ }
22
+ async hasTokens(serverId) {
23
+ return this.tokens.has(serverId);
24
+ }
25
+ async clearAll() {
26
+ this.tokens.clear();
27
+ }
28
+ /**
29
+ * Get the number of stored token sets
30
+ */
31
+ get size() {
32
+ return this.tokens.size;
33
+ }
34
+ /**
35
+ * Get all server IDs with stored tokens
36
+ */
37
+ getServerIds() {
38
+ return Array.from(this.tokens.keys());
39
+ }
40
+ }
41
+ /**
42
+ * File-based token storage implementation
43
+ * Persists tokens to disk for cross-session use
44
+ */
45
+ export class FileTokenStorage {
46
+ filePath;
47
+ tokens = new Map();
48
+ loaded = false;
49
+ constructor(filePath) {
50
+ this.filePath = filePath;
51
+ }
52
+ async loadTokens() {
53
+ if (this.loaded) {
54
+ return;
55
+ }
56
+ try {
57
+ const fs = await import("fs/promises");
58
+ const data = await fs.readFile(this.filePath, "utf-8");
59
+ const parsed = JSON.parse(data);
60
+ this.tokens = new Map(Object.entries(parsed));
61
+ this.loaded = true;
62
+ }
63
+ catch (error) {
64
+ // File doesn't exist or is invalid, start with empty tokens
65
+ if (error instanceof Error &&
66
+ "code" in error &&
67
+ error.code !== "ENOENT") {
68
+ logger.warn(`[FileTokenStorage] Error loading tokens: ${error.message}`);
69
+ }
70
+ this.tokens = new Map();
71
+ this.loaded = true;
72
+ }
73
+ }
74
+ async saveToFile() {
75
+ try {
76
+ const fs = await import("fs/promises");
77
+ const path = await import("path");
78
+ // Ensure directory exists
79
+ const dir = path.dirname(this.filePath);
80
+ await fs.mkdir(dir, { recursive: true });
81
+ // Write tokens to file
82
+ const data = Object.fromEntries(this.tokens.entries());
83
+ await fs.writeFile(this.filePath, JSON.stringify(data, null, 2), "utf-8");
84
+ }
85
+ catch (error) {
86
+ logger.error(`[FileTokenStorage] Error saving tokens: ${error instanceof Error ? error.message : String(error)}`);
87
+ throw error;
88
+ }
89
+ }
90
+ async getTokens(serverId) {
91
+ await this.loadTokens();
92
+ return this.tokens.get(serverId) ?? null;
93
+ }
94
+ async saveTokens(serverId, tokens) {
95
+ await this.loadTokens();
96
+ this.tokens.set(serverId, tokens);
97
+ await this.saveToFile();
98
+ }
99
+ async deleteTokens(serverId) {
100
+ await this.loadTokens();
101
+ this.tokens.delete(serverId);
102
+ await this.saveToFile();
103
+ }
104
+ async hasTokens(serverId) {
105
+ await this.loadTokens();
106
+ return this.tokens.has(serverId);
107
+ }
108
+ async clearAll() {
109
+ this.tokens.clear();
110
+ await this.saveToFile();
111
+ }
112
+ }
113
+ /**
114
+ * Check if tokens are expired or about to expire
115
+ * @param tokens - OAuth tokens to check
116
+ * @param bufferSeconds - Buffer time in seconds before expiration (default: 60)
117
+ * @returns True if tokens are expired or will expire within buffer time
118
+ */
119
+ export function isTokenExpired(tokens, bufferSeconds = 60) {
120
+ if (!tokens.expiresAt) {
121
+ return false; // No expiration set, assume valid
122
+ }
123
+ const bufferMs = bufferSeconds * 1000;
124
+ const now = Date.now();
125
+ return tokens.expiresAt - bufferMs <= now;
126
+ }
127
+ /**
128
+ * Calculate token expiration timestamp from expires_in value
129
+ * @param expiresIn - Token lifetime in seconds
130
+ * @returns Expiration timestamp (Unix epoch in milliseconds)
131
+ */
132
+ export function calculateExpiresAt(expiresIn) {
133
+ return Date.now() + expiresIn * 1000;
134
+ }
@@ -8,11 +8,15 @@
8
8
  */
9
9
  import { EventEmitter } from "events";
10
10
  import { ToolDiscoveryService } from "./toolDiscoveryService.js";
11
- import type { HITLManager } from "../hitl/hitlManager.js";
11
+ import type { HITLManager } from "../types/hitlTypes.js";
12
12
  import type { ExternalMCPServerInstance, ExternalMCPServerHealth, ExternalMCPConfigValidation, ExternalMCPOperationResult, ExternalMCPManagerConfig, ExternalMCPToolInfo } from "../types/externalMcp.js";
13
13
  import type { MCPServerInfo } from "../types/mcpTypes.js";
14
14
  import type { JsonObject } from "../types/common.js";
15
15
  import type { ServerLoadResult } from "../types/typeAliases.js";
16
+ /**
17
+ * ExternalServerManager
18
+ * Core class for managing external MCP servers
19
+ */
16
20
  export declare class ExternalServerManager extends EventEmitter {
17
21
  private servers;
18
22
  private config;