integrate-sdk 0.2.4 → 0.3.2

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 (86) hide show
  1. package/README.md +148 -242
  2. package/dist/index.d.ts +17 -24
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +260 -24
  5. package/dist/oauth.d.ts +19 -0
  6. package/dist/oauth.d.ts.map +1 -0
  7. package/dist/oauth.js +194 -0
  8. package/dist/server.d.ts +11 -0
  9. package/dist/server.d.ts.map +1 -0
  10. package/dist/server.js +1481 -0
  11. package/dist/src/adapters/auto-routes.d.ts +51 -0
  12. package/dist/src/adapters/auto-routes.d.ts.map +1 -0
  13. package/dist/src/adapters/base-handler.d.ts +106 -0
  14. package/dist/src/adapters/base-handler.d.ts.map +1 -0
  15. package/dist/src/adapters/nextjs.d.ts +223 -0
  16. package/dist/src/adapters/nextjs.d.ts.map +1 -0
  17. package/dist/src/adapters/tanstack-start.d.ts +169 -0
  18. package/dist/src/adapters/tanstack-start.d.ts.map +1 -0
  19. package/dist/src/client.d.ts.map +1 -0
  20. package/dist/{config → src/config}/types.d.ts +20 -0
  21. package/dist/src/config/types.d.ts.map +1 -0
  22. package/dist/src/errors.d.ts.map +1 -0
  23. package/dist/src/index.d.ts +31 -0
  24. package/dist/src/index.d.ts.map +1 -0
  25. package/dist/src/integrations/vercel-ai.d.ts.map +1 -0
  26. package/dist/{oauth → src/oauth}/manager.d.ts +6 -4
  27. package/dist/src/oauth/manager.d.ts.map +1 -0
  28. package/dist/src/oauth/pkce.d.ts.map +1 -0
  29. package/dist/src/oauth/types.d.ts.map +1 -0
  30. package/dist/src/oauth/window-manager.d.ts.map +1 -0
  31. package/dist/src/plugins/generic.d.ts.map +1 -0
  32. package/dist/src/plugins/github-client.d.ts.map +1 -0
  33. package/dist/{plugins → src/plugins}/github.d.ts +24 -7
  34. package/dist/src/plugins/github.d.ts.map +1 -0
  35. package/dist/src/plugins/gmail-client.d.ts.map +1 -0
  36. package/dist/{plugins → src/plugins}/gmail.d.ts +25 -7
  37. package/dist/src/plugins/gmail.d.ts.map +1 -0
  38. package/dist/src/plugins/server-client.d.ts.map +1 -0
  39. package/dist/{plugins → src/plugins}/types.d.ts +16 -4
  40. package/dist/src/plugins/types.d.ts.map +1 -0
  41. package/dist/src/protocol/jsonrpc.d.ts.map +1 -0
  42. package/dist/src/protocol/messages.d.ts.map +1 -0
  43. package/dist/src/server.d.ts +54 -0
  44. package/dist/src/server.d.ts.map +1 -0
  45. package/dist/src/transport/http-session.d.ts.map +1 -0
  46. package/dist/src/transport/http-stream.d.ts.map +1 -0
  47. package/dist/src/utils/naming.d.ts.map +1 -0
  48. package/index.ts +21 -0
  49. package/oauth.ts +20 -0
  50. package/package.json +14 -3
  51. package/server.ts +12 -0
  52. package/dist/client.d.ts.map +0 -1
  53. package/dist/config/types.d.ts.map +0 -1
  54. package/dist/errors.d.ts.map +0 -1
  55. package/dist/integrations/vercel-ai.d.ts.map +0 -1
  56. package/dist/oauth/manager.d.ts.map +0 -1
  57. package/dist/oauth/pkce.d.ts.map +0 -1
  58. package/dist/oauth/types.d.ts.map +0 -1
  59. package/dist/oauth/window-manager.d.ts.map +0 -1
  60. package/dist/plugins/generic.d.ts.map +0 -1
  61. package/dist/plugins/github-client.d.ts.map +0 -1
  62. package/dist/plugins/github.d.ts.map +0 -1
  63. package/dist/plugins/gmail-client.d.ts.map +0 -1
  64. package/dist/plugins/gmail.d.ts.map +0 -1
  65. package/dist/plugins/server-client.d.ts.map +0 -1
  66. package/dist/plugins/types.d.ts.map +0 -1
  67. package/dist/protocol/jsonrpc.d.ts.map +0 -1
  68. package/dist/protocol/messages.d.ts.map +0 -1
  69. package/dist/transport/http-session.d.ts.map +0 -1
  70. package/dist/transport/http-stream.d.ts.map +0 -1
  71. package/dist/utils/naming.d.ts.map +0 -1
  72. /package/dist/{client.d.ts → src/client.d.ts} +0 -0
  73. /package/dist/{errors.d.ts → src/errors.d.ts} +0 -0
  74. /package/dist/{integrations → src/integrations}/vercel-ai.d.ts +0 -0
  75. /package/dist/{oauth → src/oauth}/pkce.d.ts +0 -0
  76. /package/dist/{oauth → src/oauth}/types.d.ts +0 -0
  77. /package/dist/{oauth → src/oauth}/window-manager.d.ts +0 -0
  78. /package/dist/{plugins → src/plugins}/generic.d.ts +0 -0
  79. /package/dist/{plugins → src/plugins}/github-client.d.ts +0 -0
  80. /package/dist/{plugins → src/plugins}/gmail-client.d.ts +0 -0
  81. /package/dist/{plugins → src/plugins}/server-client.d.ts +0 -0
  82. /package/dist/{protocol → src/protocol}/jsonrpc.d.ts +0 -0
  83. /package/dist/{protocol → src/protocol}/messages.d.ts +0 -0
  84. /package/dist/{transport → src/transport}/http-session.d.ts +0 -0
  85. /package/dist/{transport → src/transport}/http-stream.d.ts +0 -0
  86. /package/dist/{utils → src/utils}/naming.d.ts +0 -0
package/dist/server.js ADDED
@@ -0,0 +1,1481 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, {
5
+ get: all[name],
6
+ enumerable: true,
7
+ configurable: true,
8
+ set: (newValue) => all[name] = () => newValue
9
+ });
10
+ };
11
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
12
+
13
+ // src/errors.ts
14
+ var exports_errors = {};
15
+ __export(exports_errors, {
16
+ parseServerError: () => parseServerError,
17
+ isTokenExpiredError: () => isTokenExpiredError,
18
+ isAuthorizationError: () => isAuthorizationError,
19
+ isAuthError: () => isAuthError,
20
+ ToolCallError: () => ToolCallError,
21
+ TokenExpiredError: () => TokenExpiredError,
22
+ IntegrateSDKError: () => IntegrateSDKError,
23
+ ConnectionError: () => ConnectionError,
24
+ AuthorizationError: () => AuthorizationError,
25
+ AuthenticationError: () => AuthenticationError
26
+ });
27
+ function isAuthError(error) {
28
+ return error instanceof AuthenticationError;
29
+ }
30
+ function isTokenExpiredError(error) {
31
+ return error instanceof TokenExpiredError;
32
+ }
33
+ function isAuthorizationError(error) {
34
+ return error instanceof AuthorizationError;
35
+ }
36
+ function parseServerError(error, context) {
37
+ if (error && typeof error === "object" && "jsonrpcError" in error) {
38
+ const jsonrpcError = error.jsonrpcError;
39
+ if (jsonrpcError && typeof jsonrpcError === "object") {
40
+ return parseServerError(jsonrpcError, context);
41
+ }
42
+ }
43
+ if (error && typeof error === "object" && "code" in error && "message" in error) {
44
+ const code = error.code;
45
+ const message = error.message || "Unknown error";
46
+ if (code === -32600) {
47
+ return new IntegrateSDKError(`Invalid request: ${message}`);
48
+ }
49
+ if (code === -32601) {
50
+ return new IntegrateSDKError(`Method not found: ${message}`);
51
+ }
52
+ if (code === -32602) {
53
+ return new IntegrateSDKError(`Invalid params: ${message}`);
54
+ }
55
+ if (code === 401 || code === -32001) {
56
+ if (message.toLowerCase().includes("expired") || message.toLowerCase().includes("token")) {
57
+ return new TokenExpiredError(message, context?.provider);
58
+ }
59
+ return new AuthenticationError(message, 401, context?.provider);
60
+ }
61
+ if (code === 403 || code === -32002) {
62
+ return new AuthorizationError(message, 403);
63
+ }
64
+ if (context?.toolName) {
65
+ return new ToolCallError(message, context.toolName, error);
66
+ }
67
+ return new IntegrateSDKError(message);
68
+ }
69
+ if (error instanceof Error) {
70
+ const message = error.message;
71
+ const statusCode = error.statusCode;
72
+ if (statusCode === 401) {
73
+ if (message.toLowerCase().includes("expired") || message.toLowerCase().includes("token")) {
74
+ return new TokenExpiredError(message, context?.provider);
75
+ }
76
+ return new AuthenticationError(message, 401, context?.provider);
77
+ }
78
+ if (statusCode === 403) {
79
+ return new AuthorizationError(message, 403);
80
+ }
81
+ if (message.includes("401") || message.includes("Unauthorized") || message.includes("unauthenticated")) {
82
+ if (message.toLowerCase().includes("expired") || message.toLowerCase().includes("token")) {
83
+ return new TokenExpiredError(message, context?.provider);
84
+ }
85
+ return new AuthenticationError(message, 401, context?.provider);
86
+ }
87
+ if (message.includes("403") || message.includes("Forbidden") || message.includes("unauthorized")) {
88
+ return new AuthorizationError(message, 403);
89
+ }
90
+ if (context?.toolName) {
91
+ return new ToolCallError(message, context.toolName, error);
92
+ }
93
+ return new IntegrateSDKError(message);
94
+ }
95
+ return new IntegrateSDKError(String(error));
96
+ }
97
+ var IntegrateSDKError, AuthenticationError, AuthorizationError, TokenExpiredError, ConnectionError, ToolCallError;
98
+ var init_errors = __esm(() => {
99
+ IntegrateSDKError = class IntegrateSDKError extends Error {
100
+ constructor(message) {
101
+ super(message);
102
+ this.name = "IntegrateSDKError";
103
+ }
104
+ };
105
+ AuthenticationError = class AuthenticationError extends IntegrateSDKError {
106
+ statusCode;
107
+ provider;
108
+ constructor(message, statusCode, provider) {
109
+ super(message);
110
+ this.name = "AuthenticationError";
111
+ this.statusCode = statusCode;
112
+ this.provider = provider;
113
+ }
114
+ };
115
+ AuthorizationError = class AuthorizationError extends IntegrateSDKError {
116
+ statusCode;
117
+ requiredScopes;
118
+ constructor(message, statusCode, requiredScopes) {
119
+ super(message);
120
+ this.name = "AuthorizationError";
121
+ this.statusCode = statusCode;
122
+ this.requiredScopes = requiredScopes;
123
+ }
124
+ };
125
+ TokenExpiredError = class TokenExpiredError extends AuthenticationError {
126
+ constructor(message, provider) {
127
+ super(message, 401, provider);
128
+ this.name = "TokenExpiredError";
129
+ }
130
+ };
131
+ ConnectionError = class ConnectionError extends IntegrateSDKError {
132
+ statusCode;
133
+ constructor(message, statusCode) {
134
+ super(message);
135
+ this.name = "ConnectionError";
136
+ this.statusCode = statusCode;
137
+ }
138
+ };
139
+ ToolCallError = class ToolCallError extends IntegrateSDKError {
140
+ toolName;
141
+ originalError;
142
+ constructor(message, toolName, originalError) {
143
+ super(message);
144
+ this.name = "ToolCallError";
145
+ this.toolName = toolName;
146
+ this.originalError = originalError;
147
+ }
148
+ };
149
+ });
150
+
151
+ // src/protocol/jsonrpc.ts
152
+ function parseMessage(message) {
153
+ try {
154
+ return JSON.parse(message);
155
+ } catch (error) {
156
+ throw new Error(`Failed to parse JSON-RPC message: ${error}`);
157
+ }
158
+ }
159
+
160
+ // src/transport/http-session.ts
161
+ class HttpSessionTransport {
162
+ url;
163
+ headers;
164
+ timeout;
165
+ messageHandlers = new Set;
166
+ sessionId;
167
+ sseController;
168
+ connected = false;
169
+ constructor(options) {
170
+ this.url = options.url;
171
+ this.headers = options.headers || {};
172
+ this.timeout = options.timeout || 30000;
173
+ }
174
+ async connect() {
175
+ if (this.connected) {
176
+ return;
177
+ }
178
+ this.connected = true;
179
+ }
180
+ async sendRequest(method, params) {
181
+ if (!this.connected) {
182
+ throw new Error("Not connected to server");
183
+ }
184
+ const request = {
185
+ jsonrpc: "2.0",
186
+ id: Date.now() + Math.random(),
187
+ method,
188
+ params
189
+ };
190
+ const headers = {
191
+ ...this.headers,
192
+ "Content-Type": "application/json"
193
+ };
194
+ if (this.sessionId) {
195
+ headers["mcp-session-id"] = this.sessionId;
196
+ }
197
+ try {
198
+ const controller = new AbortController;
199
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
200
+ const response = await fetch(this.url, {
201
+ method: "POST",
202
+ headers,
203
+ body: JSON.stringify(request),
204
+ signal: controller.signal
205
+ });
206
+ clearTimeout(timeoutId);
207
+ if (!response.ok) {
208
+ const error = new Error(`Request failed: ${response.statusText}`);
209
+ error.statusCode = response.status;
210
+ throw error;
211
+ }
212
+ if (!this.sessionId) {
213
+ const sid = response.headers.get("mcp-session-id");
214
+ if (sid) {
215
+ this.sessionId = sid;
216
+ console.log("Session established:", sid);
217
+ this.startSSEListener();
218
+ }
219
+ }
220
+ const jsonResponse = await response.json();
221
+ if ("error" in jsonResponse) {
222
+ const error = new Error(`JSON-RPC Error ${jsonResponse.error.code}: ${jsonResponse.error.message}`);
223
+ error.code = jsonResponse.error.code;
224
+ if (jsonResponse.error.data) {
225
+ error.data = jsonResponse.error.data;
226
+ }
227
+ error.jsonrpcError = jsonResponse.error;
228
+ throw error;
229
+ }
230
+ return jsonResponse.result;
231
+ } catch (error) {
232
+ if (error instanceof Error) {
233
+ if (error.name === "AbortError") {
234
+ const timeoutError = new Error("Request timeout");
235
+ timeoutError.code = -32000;
236
+ throw timeoutError;
237
+ }
238
+ throw error;
239
+ }
240
+ throw new Error(String(error));
241
+ }
242
+ }
243
+ async startSSEListener() {
244
+ if (!this.sessionId || this.sseController) {
245
+ return;
246
+ }
247
+ this.sseController = new AbortController;
248
+ try {
249
+ const response = await fetch(this.url, {
250
+ method: "GET",
251
+ headers: {
252
+ ...this.headers,
253
+ Accept: "text/event-stream",
254
+ "mcp-session-id": this.sessionId
255
+ },
256
+ signal: this.sseController.signal
257
+ });
258
+ if (!response.ok || !response.body) {
259
+ return;
260
+ }
261
+ this.processSSEStream(response.body);
262
+ } catch (error) {
263
+ if (error instanceof Error && error.name === "AbortError") {
264
+ return;
265
+ }
266
+ console.error("SSE connection error:", error);
267
+ }
268
+ }
269
+ async processSSEStream(body) {
270
+ const reader = body.getReader();
271
+ const decoder = new TextDecoder;
272
+ let buffer = "";
273
+ try {
274
+ while (true) {
275
+ const { done, value } = await reader.read();
276
+ if (done)
277
+ break;
278
+ buffer += decoder.decode(value, { stream: true });
279
+ const lines = buffer.split(`
280
+ `);
281
+ buffer = lines.pop() || "";
282
+ for (const line of lines) {
283
+ if (line.startsWith("data: ")) {
284
+ const data = line.slice(6).trim();
285
+ if (data) {
286
+ this.handleNotification(data);
287
+ }
288
+ }
289
+ }
290
+ }
291
+ } catch (error) {
292
+ if (error instanceof Error && error.name === "AbortError") {
293
+ return;
294
+ }
295
+ console.error("SSE stream error:", error);
296
+ } finally {
297
+ reader.releaseLock();
298
+ }
299
+ }
300
+ handleNotification(data) {
301
+ try {
302
+ const message = parseMessage(data);
303
+ this.messageHandlers.forEach((handler) => {
304
+ try {
305
+ handler(message);
306
+ } catch (error) {
307
+ console.error("Error in message handler:", error);
308
+ }
309
+ });
310
+ } catch (error) {
311
+ console.error("Failed to parse notification:", error);
312
+ }
313
+ }
314
+ onMessage(handler) {
315
+ this.messageHandlers.add(handler);
316
+ return () => {
317
+ this.messageHandlers.delete(handler);
318
+ };
319
+ }
320
+ async disconnect() {
321
+ if (!this.connected) {
322
+ return;
323
+ }
324
+ if (this.sseController) {
325
+ this.sseController.abort();
326
+ this.sseController = undefined;
327
+ }
328
+ this.messageHandlers.clear();
329
+ this.sessionId = undefined;
330
+ this.connected = false;
331
+ }
332
+ isConnected() {
333
+ return this.connected;
334
+ }
335
+ getSessionId() {
336
+ return this.sessionId;
337
+ }
338
+ setHeader(key, value) {
339
+ this.headers[key] = value;
340
+ }
341
+ removeHeader(key) {
342
+ delete this.headers[key];
343
+ }
344
+ getHeaders() {
345
+ return { ...this.headers };
346
+ }
347
+ }
348
+
349
+ // src/protocol/messages.ts
350
+ var MCPMethod;
351
+ ((MCPMethod2) => {
352
+ MCPMethod2["INITIALIZE"] = "initialize";
353
+ MCPMethod2["TOOLS_LIST"] = "tools/list";
354
+ MCPMethod2["TOOLS_CALL"] = "tools/call";
355
+ MCPMethod2["RESOURCES_LIST"] = "resources/list";
356
+ MCPMethod2["RESOURCES_READ"] = "resources/read";
357
+ MCPMethod2["PROMPTS_LIST"] = "prompts/list";
358
+ MCPMethod2["PROMPTS_GET"] = "prompts/get";
359
+ })(MCPMethod ||= {});
360
+
361
+ // src/client.ts
362
+ init_errors();
363
+
364
+ // src/utils/naming.ts
365
+ function camelToSnake(str) {
366
+ return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
367
+ }
368
+ function methodToToolName(methodName, pluginId) {
369
+ const snakeCaseMethod = camelToSnake(methodName);
370
+ return `${pluginId}_${snakeCaseMethod}`;
371
+ }
372
+
373
+ // src/oauth/pkce.ts
374
+ function generateCodeVerifier() {
375
+ const array = new Uint8Array(32);
376
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
377
+ crypto.getRandomValues(array);
378
+ } else if (typeof globalThis !== "undefined" && globalThis.crypto) {
379
+ globalThis.crypto.getRandomValues(array);
380
+ } else {
381
+ throw new Error("crypto.getRandomValues is not available. Please use Node.js 19+ or a modern browser.");
382
+ }
383
+ return base64UrlEncode(array);
384
+ }
385
+ async function generateCodeChallenge(verifier) {
386
+ const encoder = new TextEncoder;
387
+ const data = encoder.encode(verifier);
388
+ let hashBuffer;
389
+ if (typeof crypto !== "undefined" && crypto.subtle) {
390
+ hashBuffer = await crypto.subtle.digest("SHA-256", data);
391
+ } else if (typeof globalThis !== "undefined" && globalThis.crypto?.subtle) {
392
+ hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", data);
393
+ } else {
394
+ throw new Error("crypto.subtle.digest is not available. Please use Node.js 19+ or a modern browser.");
395
+ }
396
+ return base64UrlEncode(new Uint8Array(hashBuffer));
397
+ }
398
+ function generateState() {
399
+ const array = new Uint8Array(16);
400
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
401
+ crypto.getRandomValues(array);
402
+ } else if (typeof globalThis !== "undefined" && globalThis.crypto) {
403
+ globalThis.crypto.getRandomValues(array);
404
+ } else {
405
+ throw new Error("crypto.getRandomValues is not available. Please use Node.js 19+ or a modern browser.");
406
+ }
407
+ return base64UrlEncode(array);
408
+ }
409
+ function base64UrlEncode(array) {
410
+ let base64 = "";
411
+ if (typeof Buffer !== "undefined") {
412
+ base64 = Buffer.from(array).toString("base64");
413
+ } else {
414
+ const binary = String.fromCharCode(...array);
415
+ base64 = btoa(binary);
416
+ }
417
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
418
+ }
419
+
420
+ // src/oauth/window-manager.ts
421
+ function isBrowser() {
422
+ return typeof window !== "undefined" && typeof window.document !== "undefined";
423
+ }
424
+
425
+ class OAuthWindowManager {
426
+ popupWindow = null;
427
+ popupCheckInterval = null;
428
+ openPopup(url, options) {
429
+ if (!isBrowser()) {
430
+ throw new Error("OAuthWindowManager.openPopup() can only be used in browser environments");
431
+ }
432
+ const width = options?.width || 600;
433
+ const height = options?.height || 700;
434
+ const left = window.screenX + (window.outerWidth - width) / 2;
435
+ const top = window.screenY + (window.outerHeight - height) / 2;
436
+ const features = [
437
+ `width=${width}`,
438
+ `height=${height}`,
439
+ `left=${left}`,
440
+ `top=${top}`,
441
+ "toolbar=no",
442
+ "location=no",
443
+ "directories=no",
444
+ "status=no",
445
+ "menubar=no",
446
+ "scrollbars=yes",
447
+ "resizable=yes",
448
+ "copyhistory=no"
449
+ ].join(",");
450
+ this.popupWindow = window.open(url, "oauth_popup", features);
451
+ if (!this.popupWindow) {
452
+ console.warn("Popup was blocked by the browser. Please allow popups for this site.");
453
+ return null;
454
+ }
455
+ this.popupWindow.focus();
456
+ return this.popupWindow;
457
+ }
458
+ openRedirect(url) {
459
+ if (!isBrowser()) {
460
+ throw new Error("OAuthWindowManager.openRedirect() can only be used in browser environments");
461
+ }
462
+ window.location.href = url;
463
+ }
464
+ listenForCallback(mode, timeoutMs = 5 * 60 * 1000) {
465
+ if (mode === "popup") {
466
+ return this.listenForPopupCallback(timeoutMs);
467
+ } else {
468
+ return this.listenForRedirectCallback();
469
+ }
470
+ }
471
+ listenForPopupCallback(timeoutMs) {
472
+ if (!isBrowser()) {
473
+ return Promise.reject(new Error("OAuth popup callback can only be used in browser environments"));
474
+ }
475
+ return new Promise((resolve, reject) => {
476
+ const timeout = setTimeout(() => {
477
+ this.cleanup();
478
+ reject(new Error("OAuth authorization timed out"));
479
+ }, timeoutMs);
480
+ const messageHandler = (event) => {
481
+ if (event.data && event.data.type === "oauth_callback") {
482
+ clearTimeout(timeout);
483
+ window.removeEventListener("message", messageHandler);
484
+ const { code, state, error } = event.data;
485
+ if (error) {
486
+ this.cleanup();
487
+ reject(new Error(`OAuth error: ${error}`));
488
+ return;
489
+ }
490
+ if (!code || !state) {
491
+ this.cleanup();
492
+ reject(new Error("Invalid OAuth callback: missing code or state"));
493
+ return;
494
+ }
495
+ this.cleanup();
496
+ resolve({ code, state });
497
+ }
498
+ };
499
+ window.addEventListener("message", messageHandler);
500
+ this.popupCheckInterval = setInterval(() => {
501
+ if (this.popupWindow?.closed) {
502
+ clearTimeout(timeout);
503
+ clearInterval(this.popupCheckInterval);
504
+ window.removeEventListener("message", messageHandler);
505
+ this.cleanup();
506
+ reject(new Error("OAuth popup was closed by user"));
507
+ }
508
+ }, 500);
509
+ });
510
+ }
511
+ listenForRedirectCallback() {
512
+ if (!isBrowser()) {
513
+ return Promise.reject(new Error("OAuth redirect callback can only be used in browser environments"));
514
+ }
515
+ return new Promise((resolve, reject) => {
516
+ const params = new URLSearchParams(window.location.search);
517
+ const code = params.get("code");
518
+ const state = params.get("state");
519
+ const error = params.get("error");
520
+ const errorDescription = params.get("error_description");
521
+ if (error) {
522
+ const errorMsg = errorDescription || error;
523
+ reject(new Error(`OAuth error: ${errorMsg}`));
524
+ return;
525
+ }
526
+ if (!code || !state) {
527
+ reject(new Error("Invalid OAuth callback: missing code or state in URL"));
528
+ return;
529
+ }
530
+ resolve({ code, state });
531
+ });
532
+ }
533
+ cleanup() {
534
+ if (this.popupWindow && !this.popupWindow.closed) {
535
+ this.popupWindow.close();
536
+ }
537
+ this.popupWindow = null;
538
+ if (this.popupCheckInterval) {
539
+ clearInterval(this.popupCheckInterval);
540
+ this.popupCheckInterval = null;
541
+ }
542
+ }
543
+ close() {
544
+ this.cleanup();
545
+ }
546
+ }
547
+ function sendCallbackToOpener(params) {
548
+ if (!isBrowser()) {
549
+ console.error("sendCallbackToOpener() can only be used in browser environments");
550
+ return;
551
+ }
552
+ if (!window.opener) {
553
+ console.error("No opener window found. This function should only be called from a popup window.");
554
+ return;
555
+ }
556
+ window.opener.postMessage({
557
+ type: "oauth_callback",
558
+ code: params.code,
559
+ state: params.state,
560
+ error: params.error
561
+ }, "*");
562
+ window.close();
563
+ }
564
+
565
+ // src/oauth/manager.ts
566
+ class OAuthManager {
567
+ pendingAuths = new Map;
568
+ sessionToken;
569
+ windowManager;
570
+ flowConfig;
571
+ oauthApiBase;
572
+ constructor(oauthApiBase, flowConfig) {
573
+ this.oauthApiBase = oauthApiBase;
574
+ this.windowManager = new OAuthWindowManager;
575
+ this.flowConfig = {
576
+ mode: flowConfig?.mode || "redirect",
577
+ popupOptions: flowConfig?.popupOptions,
578
+ onAuthCallback: flowConfig?.onAuthCallback
579
+ };
580
+ }
581
+ async initiateFlow(provider, config) {
582
+ const codeVerifier = generateCodeVerifier();
583
+ const codeChallenge = await generateCodeChallenge(codeVerifier);
584
+ const state = generateState();
585
+ const pendingAuth = {
586
+ provider,
587
+ state,
588
+ codeVerifier,
589
+ codeChallenge,
590
+ scopes: config.scopes,
591
+ redirectUri: config.redirectUri,
592
+ initiatedAt: Date.now()
593
+ };
594
+ this.pendingAuths.set(state, pendingAuth);
595
+ const authUrl = await this.getAuthorizationUrl(provider, config.scopes, state, codeChallenge, config.redirectUri);
596
+ if (this.flowConfig.mode === "popup") {
597
+ this.windowManager.openPopup(authUrl, this.flowConfig.popupOptions);
598
+ try {
599
+ const callbackParams = await this.windowManager.listenForCallback("popup");
600
+ await this.handleCallback(callbackParams.code, callbackParams.state);
601
+ } catch (error) {
602
+ this.pendingAuths.delete(state);
603
+ throw error;
604
+ }
605
+ } else {
606
+ this.windowManager.openRedirect(authUrl);
607
+ }
608
+ }
609
+ async handleCallback(code, state) {
610
+ const pendingAuth = this.pendingAuths.get(state);
611
+ if (!pendingAuth) {
612
+ throw new Error("Invalid state parameter: no matching OAuth flow found");
613
+ }
614
+ const fiveMinutes = 5 * 60 * 1000;
615
+ if (Date.now() - pendingAuth.initiatedAt > fiveMinutes) {
616
+ this.pendingAuths.delete(state);
617
+ throw new Error("OAuth flow expired: please try again");
618
+ }
619
+ if (this.flowConfig.onAuthCallback) {
620
+ try {
621
+ await this.flowConfig.onAuthCallback(pendingAuth.provider, code, state);
622
+ } catch (error) {
623
+ console.error("Custom OAuth callback handler failed:", error);
624
+ }
625
+ }
626
+ try {
627
+ const response = await this.exchangeCodeForToken(pendingAuth.provider, code, pendingAuth.codeVerifier, state);
628
+ this.sessionToken = response.sessionToken;
629
+ this.pendingAuths.delete(state);
630
+ return response.sessionToken;
631
+ } catch (error) {
632
+ this.pendingAuths.delete(state);
633
+ throw error;
634
+ }
635
+ }
636
+ async checkAuthStatus(provider) {
637
+ if (!this.sessionToken) {
638
+ return {
639
+ authorized: false,
640
+ provider
641
+ };
642
+ }
643
+ try {
644
+ const url = `${this.oauthApiBase}/status?provider=${encodeURIComponent(provider)}`;
645
+ const response = await fetch(url, {
646
+ method: "GET",
647
+ headers: {
648
+ "X-Session-Token": this.sessionToken
649
+ }
650
+ });
651
+ if (!response.ok) {
652
+ return {
653
+ authorized: false,
654
+ provider
655
+ };
656
+ }
657
+ const status = await response.json();
658
+ return status;
659
+ } catch (error) {
660
+ console.error("Failed to check auth status:", error);
661
+ return {
662
+ authorized: false,
663
+ provider
664
+ };
665
+ }
666
+ }
667
+ getSessionToken() {
668
+ return this.sessionToken;
669
+ }
670
+ setSessionToken(token) {
671
+ this.sessionToken = token;
672
+ }
673
+ clearSessionToken() {
674
+ this.sessionToken = undefined;
675
+ }
676
+ async getAuthorizationUrl(provider, scopes, state, codeChallenge, redirectUri) {
677
+ const url = `${this.oauthApiBase}/authorize`;
678
+ const response = await fetch(url, {
679
+ method: "POST",
680
+ headers: {
681
+ "Content-Type": "application/json"
682
+ },
683
+ body: JSON.stringify({
684
+ provider,
685
+ scopes,
686
+ state,
687
+ codeChallenge,
688
+ codeChallengeMethod: "S256",
689
+ redirectUri
690
+ })
691
+ });
692
+ if (!response.ok) {
693
+ const error = await response.text();
694
+ throw new Error(`Failed to get authorization URL: ${error}`);
695
+ }
696
+ const data = await response.json();
697
+ return data.authorizationUrl;
698
+ }
699
+ async exchangeCodeForToken(provider, code, codeVerifier, state) {
700
+ const url = `${this.oauthApiBase}/callback`;
701
+ const response = await fetch(url, {
702
+ method: "POST",
703
+ headers: {
704
+ "Content-Type": "application/json"
705
+ },
706
+ body: JSON.stringify({
707
+ provider,
708
+ code,
709
+ codeVerifier,
710
+ state
711
+ })
712
+ });
713
+ if (!response.ok) {
714
+ const error = await response.text();
715
+ throw new Error(`Failed to exchange code for token: ${error}`);
716
+ }
717
+ const data = await response.json();
718
+ return data;
719
+ }
720
+ close() {
721
+ this.windowManager.close();
722
+ }
723
+ }
724
+
725
+ // src/client.ts
726
+ var MCP_SERVER_URL = "https://mcp.integrate.dev/api/v1/mcp";
727
+ var clientCache = new Map;
728
+ var cleanupClients = new Set;
729
+ var cleanupHandlersRegistered = false;
730
+
731
+ class MCPClient {
732
+ transport;
733
+ plugins;
734
+ availableTools = new Map;
735
+ enabledToolNames = new Set;
736
+ initialized = false;
737
+ clientInfo;
738
+ onReauthRequired;
739
+ maxReauthRetries;
740
+ authState = new Map;
741
+ connectionMode;
742
+ connecting = null;
743
+ oauthManager;
744
+ github;
745
+ gmail;
746
+ server;
747
+ constructor(config) {
748
+ this.transport = new HttpSessionTransport({
749
+ url: MCP_SERVER_URL,
750
+ headers: config.headers,
751
+ timeout: config.timeout
752
+ });
753
+ this.plugins = config.plugins;
754
+ this.clientInfo = config.clientInfo || {
755
+ name: "integrate-sdk",
756
+ version: "0.1.0"
757
+ };
758
+ this.onReauthRequired = config.onReauthRequired;
759
+ this.maxReauthRetries = config.maxReauthRetries ?? 1;
760
+ this.connectionMode = config.connectionMode ?? "lazy";
761
+ this.oauthManager = new OAuthManager(config.oauthApiBase || "/api/integrate/oauth", config.oauthFlow);
762
+ if (config.sessionToken) {
763
+ this.oauthManager.setSessionToken(config.sessionToken);
764
+ this.transport.setHeader("X-Session-Token", config.sessionToken);
765
+ }
766
+ for (const plugin of this.plugins) {
767
+ for (const toolName of plugin.tools) {
768
+ this.enabledToolNames.add(toolName);
769
+ }
770
+ if (plugin.oauth) {
771
+ this.authState.set(plugin.oauth.provider, { authenticated: true });
772
+ }
773
+ }
774
+ this.github = this.createPluginProxy("github");
775
+ this.gmail = this.createPluginProxy("gmail");
776
+ this.server = this.createServerProxy();
777
+ this.initializePlugins();
778
+ }
779
+ async ensureConnected() {
780
+ if (this.initialized && this.transport.isConnected()) {
781
+ return;
782
+ }
783
+ if (this.connecting) {
784
+ return this.connecting;
785
+ }
786
+ if (this.connectionMode === "manual" && !this.initialized) {
787
+ throw new Error("Client not connected. Call connect() first when using manual connection mode.");
788
+ }
789
+ this.connecting = this.connect();
790
+ try {
791
+ await this.connecting;
792
+ } finally {
793
+ this.connecting = null;
794
+ }
795
+ }
796
+ createPluginProxy(pluginId) {
797
+ return new Proxy({}, {
798
+ get: (_target, methodName) => {
799
+ return async (args) => {
800
+ await this.ensureConnected();
801
+ const toolName = methodToToolName(methodName, pluginId);
802
+ return await this.callToolWithRetry(toolName, args, 0);
803
+ };
804
+ }
805
+ });
806
+ }
807
+ createServerProxy() {
808
+ return new Proxy({}, {
809
+ get: (_target, methodName) => {
810
+ return async (args) => {
811
+ await this.ensureConnected();
812
+ const toolName = methodToToolName(methodName, "");
813
+ const finalToolName = toolName.startsWith("_") ? toolName.substring(1) : toolName;
814
+ return await this.callServerToolInternal(finalToolName, args);
815
+ };
816
+ }
817
+ });
818
+ }
819
+ async callServerToolInternal(name, args) {
820
+ if (!this.initialized) {
821
+ throw new Error("Client not initialized. Call connect() first.");
822
+ }
823
+ if (!this.availableTools.has(name)) {
824
+ throw new Error(`Tool "${name}" is not available on the server. Available tools: ${Array.from(this.availableTools.keys()).join(", ")}`);
825
+ }
826
+ const params = {
827
+ name,
828
+ arguments: args
829
+ };
830
+ try {
831
+ const response = await this.transport.sendRequest("tools/call" /* TOOLS_CALL */, params);
832
+ return response;
833
+ } catch (error) {
834
+ const parsedError = parseServerError(error, { toolName: name });
835
+ throw parsedError;
836
+ }
837
+ }
838
+ async initializePlugins() {
839
+ for (const plugin of this.plugins) {
840
+ if (plugin.onInit) {
841
+ await plugin.onInit(this);
842
+ }
843
+ }
844
+ }
845
+ async connect() {
846
+ for (const plugin of this.plugins) {
847
+ if (plugin.onBeforeConnect) {
848
+ await plugin.onBeforeConnect(this);
849
+ }
850
+ }
851
+ await this.transport.connect();
852
+ await this.initialize();
853
+ await this.discoverTools();
854
+ for (const plugin of this.plugins) {
855
+ if (plugin.onAfterConnect) {
856
+ await plugin.onAfterConnect(this);
857
+ }
858
+ }
859
+ }
860
+ async initialize() {
861
+ const params = {
862
+ protocolVersion: "2024-11-05",
863
+ capabilities: {
864
+ tools: {}
865
+ },
866
+ clientInfo: this.clientInfo
867
+ };
868
+ const response = await this.transport.sendRequest("initialize" /* INITIALIZE */, params);
869
+ this.initialized = true;
870
+ return response;
871
+ }
872
+ async discoverTools() {
873
+ const response = await this.transport.sendRequest("tools/list" /* TOOLS_LIST */);
874
+ for (const tool of response.tools) {
875
+ this.availableTools.set(tool.name, tool);
876
+ }
877
+ const enabledTools = response.tools.filter((tool) => this.enabledToolNames.has(tool.name));
878
+ console.log(`Discovered ${response.tools.length} tools, ${enabledTools.length} enabled by plugins`);
879
+ }
880
+ async _callToolByName(name, args) {
881
+ return await this.callToolWithRetry(name, args, 0);
882
+ }
883
+ async callServerTool(name, args) {
884
+ if (!this.initialized) {
885
+ throw new Error("Client not initialized. Call connect() first.");
886
+ }
887
+ if (!this.availableTools.has(name)) {
888
+ throw new Error(`Tool "${name}" is not available on the server. Available tools: ${Array.from(this.availableTools.keys()).join(", ")}`);
889
+ }
890
+ const params = {
891
+ name,
892
+ arguments: args
893
+ };
894
+ try {
895
+ const response = await this.transport.sendRequest("tools/call" /* TOOLS_CALL */, params);
896
+ return response;
897
+ } catch (error) {
898
+ const parsedError = parseServerError(error, { toolName: name });
899
+ throw parsedError;
900
+ }
901
+ }
902
+ async callToolWithRetry(name, args, retryCount = 0) {
903
+ if (!this.initialized) {
904
+ throw new Error("Client not initialized. Call connect() first.");
905
+ }
906
+ if (!this.enabledToolNames.has(name)) {
907
+ throw new Error(`Tool "${name}" is not enabled. Enable it by adding the appropriate plugin.`);
908
+ }
909
+ if (!this.availableTools.has(name)) {
910
+ throw new Error(`Tool "${name}" is not available on the server. Available tools: ${Array.from(this.availableTools.keys()).join(", ")}`);
911
+ }
912
+ const params = {
913
+ name,
914
+ arguments: args
915
+ };
916
+ try {
917
+ const response = await this.transport.sendRequest("tools/call" /* TOOLS_CALL */, params);
918
+ const provider = this.getProviderForTool(name);
919
+ if (provider) {
920
+ this.authState.set(provider, { authenticated: true });
921
+ }
922
+ return response;
923
+ } catch (error) {
924
+ const provider = this.getProviderForTool(name);
925
+ const parsedError = parseServerError(error, { toolName: name, provider });
926
+ if (isAuthError(parsedError) && retryCount < this.maxReauthRetries) {
927
+ if (provider) {
928
+ this.authState.set(provider, {
929
+ authenticated: false,
930
+ lastError: parsedError
931
+ });
932
+ }
933
+ if (this.onReauthRequired && provider) {
934
+ const reauthSuccess = await this.onReauthRequired({
935
+ provider,
936
+ error: parsedError,
937
+ toolName: name
938
+ });
939
+ if (reauthSuccess) {
940
+ return await this.callToolWithRetry(name, args, retryCount + 1);
941
+ }
942
+ }
943
+ }
944
+ throw parsedError;
945
+ }
946
+ }
947
+ getProviderForTool(toolName) {
948
+ for (const plugin of this.plugins) {
949
+ if (plugin.tools.includes(toolName) && plugin.oauth) {
950
+ return plugin.oauth.provider;
951
+ }
952
+ }
953
+ return;
954
+ }
955
+ getTool(name) {
956
+ return this.availableTools.get(name);
957
+ }
958
+ getAvailableTools() {
959
+ return Array.from(this.availableTools.values());
960
+ }
961
+ getEnabledTools() {
962
+ return Array.from(this.availableTools.values()).filter((tool) => this.enabledToolNames.has(tool.name));
963
+ }
964
+ getOAuthConfig(pluginId) {
965
+ const plugin = this.plugins.find((p) => p.id === pluginId);
966
+ return plugin?.oauth;
967
+ }
968
+ getAllOAuthConfigs() {
969
+ const configs = new Map;
970
+ for (const plugin of this.plugins) {
971
+ if (plugin.oauth) {
972
+ configs.set(plugin.id, plugin.oauth);
973
+ }
974
+ }
975
+ return configs;
976
+ }
977
+ onMessage(handler) {
978
+ return this.transport.onMessage(handler);
979
+ }
980
+ async disconnect() {
981
+ for (const plugin of this.plugins) {
982
+ if (plugin.onDisconnect) {
983
+ await plugin.onDisconnect(this);
984
+ }
985
+ }
986
+ await this.transport.disconnect();
987
+ this.initialized = false;
988
+ }
989
+ isConnected() {
990
+ return this.transport.isConnected();
991
+ }
992
+ isInitialized() {
993
+ return this.initialized;
994
+ }
995
+ getAuthState(provider) {
996
+ return this.authState.get(provider);
997
+ }
998
+ isProviderAuthenticated(provider) {
999
+ return this.authState.get(provider)?.authenticated ?? false;
1000
+ }
1001
+ async isAuthorized(provider) {
1002
+ const status = await this.oauthManager.checkAuthStatus(provider);
1003
+ return status.authorized;
1004
+ }
1005
+ async authorizedProviders() {
1006
+ const authorized = [];
1007
+ for (const plugin of this.plugins) {
1008
+ if (plugin.oauth) {
1009
+ const status = await this.oauthManager.checkAuthStatus(plugin.oauth.provider);
1010
+ if (status.authorized) {
1011
+ authorized.push(plugin.oauth.provider);
1012
+ }
1013
+ }
1014
+ }
1015
+ return authorized;
1016
+ }
1017
+ async getAuthorizationStatus(provider) {
1018
+ return await this.oauthManager.checkAuthStatus(provider);
1019
+ }
1020
+ async authorize(provider) {
1021
+ const plugin = this.plugins.find((p) => p.oauth?.provider === provider);
1022
+ if (!plugin?.oauth) {
1023
+ throw new Error(`No OAuth configuration found for provider: ${provider}`);
1024
+ }
1025
+ await this.oauthManager.initiateFlow(provider, plugin.oauth);
1026
+ this.authState.set(provider, { authenticated: true });
1027
+ }
1028
+ async handleOAuthCallback(params) {
1029
+ const sessionToken = await this.oauthManager.handleCallback(params.code, params.state);
1030
+ this.transport.setHeader("X-Session-Token", sessionToken);
1031
+ for (const plugin of this.plugins) {
1032
+ if (plugin.oauth) {
1033
+ this.authState.set(plugin.oauth.provider, { authenticated: true });
1034
+ }
1035
+ }
1036
+ }
1037
+ getSessionToken() {
1038
+ return this.oauthManager.getSessionToken();
1039
+ }
1040
+ setSessionToken(token) {
1041
+ this.oauthManager.setSessionToken(token);
1042
+ this.transport.setHeader("X-Session-Token", token);
1043
+ }
1044
+ async reauthenticate(provider) {
1045
+ const state = this.authState.get(provider);
1046
+ if (!state) {
1047
+ throw new Error(`Provider "${provider}" not found in configured plugins`);
1048
+ }
1049
+ if (!this.onReauthRequired) {
1050
+ throw new Error("No re-authentication handler configured. Set onReauthRequired in client config.");
1051
+ }
1052
+ const lastError = state.lastError || new (await Promise.resolve().then(() => (init_errors(), exports_errors))).AuthenticationError("Manual re-authentication requested", undefined, provider);
1053
+ const success = await this.onReauthRequired({
1054
+ provider,
1055
+ error: lastError
1056
+ });
1057
+ if (success) {
1058
+ this.authState.set(provider, { authenticated: true });
1059
+ }
1060
+ return success;
1061
+ }
1062
+ }
1063
+ function registerCleanupHandlers() {
1064
+ if (cleanupHandlersRegistered)
1065
+ return;
1066
+ cleanupHandlersRegistered = true;
1067
+ const cleanup = async () => {
1068
+ const clients = Array.from(cleanupClients);
1069
+ cleanupClients.clear();
1070
+ await Promise.all(clients.map(async (client) => {
1071
+ try {
1072
+ if (client.isConnected()) {
1073
+ await client.disconnect();
1074
+ }
1075
+ } catch (error) {
1076
+ console.error("Error disconnecting client:", error);
1077
+ }
1078
+ }));
1079
+ };
1080
+ if (typeof process !== "undefined") {
1081
+ process.on("SIGINT", async () => {
1082
+ await cleanup();
1083
+ process.exit(0);
1084
+ });
1085
+ process.on("SIGTERM", async () => {
1086
+ await cleanup();
1087
+ process.exit(0);
1088
+ });
1089
+ process.on("beforeExit", async () => {
1090
+ await cleanup();
1091
+ });
1092
+ }
1093
+ }
1094
+ function generateCacheKey(config) {
1095
+ const parts = [
1096
+ config.clientInfo?.name || "integrate-sdk",
1097
+ config.clientInfo?.version || "0.1.0",
1098
+ JSON.stringify(config.plugins.map((p) => ({ id: p.id, tools: p.tools }))),
1099
+ JSON.stringify(config.headers || {}),
1100
+ config.timeout?.toString() || "30000"
1101
+ ];
1102
+ return parts.join("|");
1103
+ }
1104
+ function createMCPClient(config) {
1105
+ const useSingleton = config.singleton ?? true;
1106
+ const connectionMode = config.connectionMode ?? "lazy";
1107
+ const autoCleanup = config.autoCleanup ?? true;
1108
+ if (useSingleton) {
1109
+ const cacheKey = generateCacheKey(config);
1110
+ const existing = clientCache.get(cacheKey);
1111
+ if (existing && existing.isConnected()) {
1112
+ return existing;
1113
+ }
1114
+ if (existing) {
1115
+ clientCache.delete(cacheKey);
1116
+ cleanupClients.delete(existing);
1117
+ }
1118
+ const client = new MCPClient(config);
1119
+ clientCache.set(cacheKey, client);
1120
+ if (autoCleanup) {
1121
+ cleanupClients.add(client);
1122
+ registerCleanupHandlers();
1123
+ }
1124
+ if (connectionMode === "eager") {
1125
+ client.connect().catch((error) => {
1126
+ console.error("Failed to connect client:", error);
1127
+ });
1128
+ }
1129
+ return client;
1130
+ } else {
1131
+ const client = new MCPClient(config);
1132
+ if (autoCleanup) {
1133
+ cleanupClients.add(client);
1134
+ registerCleanupHandlers();
1135
+ }
1136
+ if (connectionMode === "eager") {
1137
+ client.connect().catch((error) => {
1138
+ console.error("Failed to connect client:", error);
1139
+ });
1140
+ }
1141
+ return client;
1142
+ }
1143
+ }
1144
+ async function clearClientCache() {
1145
+ const clients = Array.from(clientCache.values());
1146
+ clientCache.clear();
1147
+ await Promise.all(clients.map(async (client) => {
1148
+ try {
1149
+ if (client.isConnected()) {
1150
+ await client.disconnect();
1151
+ }
1152
+ } catch (error) {
1153
+ console.error("Error disconnecting client during cache clear:", error);
1154
+ }
1155
+ }));
1156
+ }
1157
+
1158
+ // src/adapters/base-handler.ts
1159
+ var MCP_SERVER_URL2 = "https://mcp.integrate.dev/api/v1/mcp";
1160
+
1161
+ class OAuthHandler {
1162
+ config;
1163
+ serverUrl = MCP_SERVER_URL2;
1164
+ constructor(config) {
1165
+ this.config = config;
1166
+ }
1167
+ async handleAuthorize(request) {
1168
+ const providerConfig = this.config.providers[request.provider];
1169
+ if (!providerConfig) {
1170
+ throw new Error(`Provider ${request.provider} not configured. Add OAuth credentials to your API route configuration.`);
1171
+ }
1172
+ if (!providerConfig.clientId || !providerConfig.clientSecret) {
1173
+ throw new Error(`Missing OAuth credentials for ${request.provider}. Check your environment variables.`);
1174
+ }
1175
+ const url = new URL("/oauth/authorize", this.serverUrl);
1176
+ url.searchParams.set("provider", request.provider);
1177
+ url.searchParams.set("client_id", providerConfig.clientId);
1178
+ url.searchParams.set("client_secret", providerConfig.clientSecret);
1179
+ url.searchParams.set("scope", request.scopes.join(","));
1180
+ url.searchParams.set("state", request.state);
1181
+ url.searchParams.set("code_challenge", request.codeChallenge);
1182
+ url.searchParams.set("code_challenge_method", request.codeChallengeMethod);
1183
+ const redirectUri = request.redirectUri || providerConfig.redirectUri;
1184
+ if (redirectUri) {
1185
+ url.searchParams.set("redirect_uri", redirectUri);
1186
+ }
1187
+ const response = await fetch(url.toString(), {
1188
+ method: "GET"
1189
+ });
1190
+ if (!response.ok) {
1191
+ const error = await response.text();
1192
+ throw new Error(`MCP server failed to generate authorization URL: ${error}`);
1193
+ }
1194
+ const data = await response.json();
1195
+ return data;
1196
+ }
1197
+ async handleCallback(request) {
1198
+ const url = new URL("/oauth/callback", this.serverUrl);
1199
+ const response = await fetch(url.toString(), {
1200
+ method: "POST",
1201
+ headers: {
1202
+ "Content-Type": "application/json"
1203
+ },
1204
+ body: JSON.stringify({
1205
+ provider: request.provider,
1206
+ code: request.code,
1207
+ code_verifier: request.codeVerifier,
1208
+ state: request.state
1209
+ })
1210
+ });
1211
+ if (!response.ok) {
1212
+ const error = await response.text();
1213
+ throw new Error(`MCP server failed to exchange authorization code: ${error}`);
1214
+ }
1215
+ const data = await response.json();
1216
+ return data;
1217
+ }
1218
+ async handleStatus(provider, sessionToken) {
1219
+ const url = new URL("/oauth/status", this.serverUrl);
1220
+ url.searchParams.set("provider", provider);
1221
+ const response = await fetch(url.toString(), {
1222
+ method: "GET",
1223
+ headers: {
1224
+ "X-Session-Token": sessionToken
1225
+ }
1226
+ });
1227
+ if (!response.ok) {
1228
+ if (response.status === 401) {
1229
+ return {
1230
+ authorized: false,
1231
+ provider
1232
+ };
1233
+ }
1234
+ const error = await response.text();
1235
+ throw new Error(`MCP server failed to check authorization status: ${error}`);
1236
+ }
1237
+ const data = await response.json();
1238
+ return data;
1239
+ }
1240
+ }
1241
+
1242
+ // src/adapters/auto-routes.ts
1243
+ var globalOAuthConfig = null;
1244
+ function setGlobalOAuthConfig(config) {
1245
+ globalOAuthConfig = config;
1246
+ }
1247
+ async function POST(req, context) {
1248
+ if (!globalOAuthConfig) {
1249
+ throw new Error("OAuth configuration not found. Did you configure oauthProviders in createMCPClient?");
1250
+ }
1251
+ const handler = new OAuthHandler(globalOAuthConfig);
1252
+ const action = context?.params?.action;
1253
+ if (!action) {
1254
+ return createErrorResponse("Missing action parameter", 400);
1255
+ }
1256
+ try {
1257
+ if (action === "authorize") {
1258
+ const body = await parseRequestBody(req);
1259
+ const result = await handler.handleAuthorize(body);
1260
+ return createSuccessResponse(result);
1261
+ }
1262
+ if (action === "callback") {
1263
+ const body = await parseRequestBody(req);
1264
+ const result = await handler.handleCallback(body);
1265
+ return createSuccessResponse(result);
1266
+ }
1267
+ return createErrorResponse(`Unknown action: ${action}`, 404);
1268
+ } catch (error) {
1269
+ console.error(`[OAuth ${action}] Error:`, error);
1270
+ return createErrorResponse(error.message, 500);
1271
+ }
1272
+ }
1273
+ async function GET(req, context) {
1274
+ if (!globalOAuthConfig) {
1275
+ throw new Error("OAuth configuration not found. Did you configure oauthProviders in createMCPClient?");
1276
+ }
1277
+ const handler = new OAuthHandler(globalOAuthConfig);
1278
+ const action = context?.params?.action;
1279
+ if (!action) {
1280
+ return createErrorResponse("Missing action parameter", 400);
1281
+ }
1282
+ try {
1283
+ if (action === "status") {
1284
+ const { provider, sessionToken } = parseQueryParams(req);
1285
+ if (!provider || !sessionToken) {
1286
+ return createErrorResponse("Missing provider or session token", 400);
1287
+ }
1288
+ const result = await handler.handleStatus(provider, sessionToken);
1289
+ return createSuccessResponse(result);
1290
+ }
1291
+ return createErrorResponse(`Unknown action: ${action}`, 404);
1292
+ } catch (error) {
1293
+ console.error(`[OAuth ${action}] Error:`, error);
1294
+ return createErrorResponse(error.message, 500);
1295
+ }
1296
+ }
1297
+ async function parseRequestBody(req) {
1298
+ if (typeof req.json === "function") {
1299
+ return await req.json();
1300
+ }
1301
+ throw new Error("Unable to parse request body");
1302
+ }
1303
+ function parseQueryParams(req) {
1304
+ let url;
1305
+ if (req.nextUrl) {
1306
+ url = new URL(req.nextUrl);
1307
+ } else if (req.url) {
1308
+ url = new URL(req.url);
1309
+ } else {
1310
+ return {};
1311
+ }
1312
+ const provider = url.searchParams.get("provider") || undefined;
1313
+ const sessionToken = req.headers?.get?.("x-session-token") || undefined;
1314
+ return { provider, sessionToken };
1315
+ }
1316
+ function createSuccessResponse(data) {
1317
+ if (typeof globalThis.NextResponse !== "undefined") {
1318
+ const NextResponse = globalThis.NextResponse;
1319
+ return NextResponse.json(data);
1320
+ }
1321
+ return new Response(JSON.stringify(data), {
1322
+ status: 200,
1323
+ headers: { "Content-Type": "application/json" }
1324
+ });
1325
+ }
1326
+ function createErrorResponse(message, status) {
1327
+ if (typeof globalThis.NextResponse !== "undefined") {
1328
+ const NextResponse = globalThis.NextResponse;
1329
+ return NextResponse.json({ error: message }, { status });
1330
+ }
1331
+ return new Response(JSON.stringify({ error: message }), {
1332
+ status,
1333
+ headers: { "Content-Type": "application/json" }
1334
+ });
1335
+ }
1336
+
1337
+ // src/plugins/github.ts
1338
+ var GITHUB_TOOLS = [
1339
+ "github_create_issue",
1340
+ "github_list_issues",
1341
+ "github_get_issue",
1342
+ "github_update_issue",
1343
+ "github_close_issue",
1344
+ "github_create_pull_request",
1345
+ "github_list_pull_requests",
1346
+ "github_get_pull_request",
1347
+ "github_merge_pull_request",
1348
+ "github_list_repos",
1349
+ "github_list_own_repos",
1350
+ "github_get_repo",
1351
+ "github_create_repo",
1352
+ "github_list_branches",
1353
+ "github_create_branch",
1354
+ "github_get_user",
1355
+ "github_list_commits",
1356
+ "github_get_commit"
1357
+ ];
1358
+ function githubPlugin(config) {
1359
+ const oauth = {
1360
+ provider: "github",
1361
+ clientId: config.clientId,
1362
+ clientSecret: config.clientSecret,
1363
+ scopes: config.scopes || ["repo", "user"],
1364
+ redirectUri: config.redirectUri,
1365
+ config: {
1366
+ apiBaseUrl: config.apiBaseUrl || "https://api.github.com",
1367
+ ...config
1368
+ }
1369
+ };
1370
+ return {
1371
+ id: "github",
1372
+ tools: [...GITHUB_TOOLS],
1373
+ oauth,
1374
+ async onInit(_client) {
1375
+ console.log("GitHub plugin initialized");
1376
+ },
1377
+ async onAfterConnect(_client) {
1378
+ console.log("GitHub plugin connected");
1379
+ }
1380
+ };
1381
+ }
1382
+ // src/plugins/gmail.ts
1383
+ var GMAIL_TOOLS = [
1384
+ "gmail_send_message",
1385
+ "gmail_list_messages",
1386
+ "gmail_get_message",
1387
+ "gmail_search_messages"
1388
+ ];
1389
+ function gmailPlugin(config) {
1390
+ const oauth = {
1391
+ provider: "google",
1392
+ clientId: config.clientId,
1393
+ clientSecret: config.clientSecret,
1394
+ scopes: config.scopes || [
1395
+ "https://www.googleapis.com/auth/gmail.send",
1396
+ "https://www.googleapis.com/auth/gmail.readonly",
1397
+ "https://www.googleapis.com/auth/gmail.modify",
1398
+ "https://www.googleapis.com/auth/gmail.labels"
1399
+ ],
1400
+ redirectUri: config.redirectUri,
1401
+ config
1402
+ };
1403
+ return {
1404
+ id: "gmail",
1405
+ tools: [...GMAIL_TOOLS],
1406
+ oauth,
1407
+ async onInit(_client) {
1408
+ console.log("Gmail plugin initialized");
1409
+ },
1410
+ async onAfterConnect(_client) {
1411
+ console.log("Gmail plugin connected");
1412
+ }
1413
+ };
1414
+ }
1415
+ // src/plugins/generic.ts
1416
+ function genericOAuthPlugin(config) {
1417
+ const oauth = {
1418
+ provider: config.provider,
1419
+ clientId: config.clientId,
1420
+ clientSecret: config.clientSecret,
1421
+ scopes: config.scopes,
1422
+ redirectUri: config.redirectUri,
1423
+ config
1424
+ };
1425
+ return {
1426
+ id: config.id,
1427
+ tools: config.tools,
1428
+ oauth,
1429
+ onInit: config.onInit,
1430
+ onAfterConnect: config.onAfterConnect,
1431
+ onDisconnect: config.onDisconnect
1432
+ };
1433
+ }
1434
+ function createSimplePlugin(config) {
1435
+ return {
1436
+ id: config.id,
1437
+ tools: config.tools,
1438
+ onInit: config.onInit,
1439
+ onAfterConnect: config.onAfterConnect,
1440
+ onDisconnect: config.onDisconnect
1441
+ };
1442
+ }
1443
+
1444
+ // src/server.ts
1445
+ function createMCPServer(config) {
1446
+ if (typeof window !== "undefined") {
1447
+ throw new Error("createMCPServer() should only be called on the server-side. " + "Use createMCPClient() for client-side code.");
1448
+ }
1449
+ const providers = {};
1450
+ for (const plugin of config.plugins) {
1451
+ if (plugin.oauth) {
1452
+ const { clientId, clientSecret, redirectUri } = plugin.oauth;
1453
+ if (!clientId || !clientSecret) {
1454
+ console.warn(`Warning: Plugin "${plugin.id}" is missing OAuth credentials. ` + `Provide clientId and clientSecret in the plugin configuration.`);
1455
+ continue;
1456
+ }
1457
+ providers[plugin.id] = {
1458
+ clientId,
1459
+ clientSecret,
1460
+ redirectUri
1461
+ };
1462
+ }
1463
+ }
1464
+ if (Object.keys(providers).length > 0) {
1465
+ setGlobalOAuthConfig({ providers });
1466
+ }
1467
+ const client = new MCPClient(config);
1468
+ return {
1469
+ client,
1470
+ handlers: {
1471
+ info: "To use OAuth handlers, create a route file and add: export * from 'integrate-sdk/oauth'"
1472
+ }
1473
+ };
1474
+ }
1475
+ export {
1476
+ gmailPlugin,
1477
+ githubPlugin,
1478
+ genericOAuthPlugin,
1479
+ createSimplePlugin,
1480
+ createMCPServer
1481
+ };