integrate-sdk 0.6.8 → 0.6.9

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.
@@ -8,11 +8,1751 @@ var __export = (target, all) => {
8
8
  set: (newValue) => all[name] = () => newValue
9
9
  });
10
10
  };
11
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
11
12
 
12
- // src/adapters/solid-start.ts
13
- function toSolidStartHandler(baseHandler) {
13
+ // src/oauth/pkce.ts
14
+ var exports_pkce = {};
15
+ __export(exports_pkce, {
16
+ parseState: () => parseState,
17
+ generateStateWithReturnUrl: () => generateStateWithReturnUrl,
18
+ generateState: () => generateState,
19
+ generateCodeVerifier: () => generateCodeVerifier,
20
+ generateCodeChallenge: () => generateCodeChallenge
21
+ });
22
+ function generateCodeVerifier() {
23
+ const array = new Uint8Array(32);
24
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
25
+ crypto.getRandomValues(array);
26
+ } else if (typeof globalThis !== "undefined" && globalThis.crypto) {
27
+ globalThis.crypto.getRandomValues(array);
28
+ } else {
29
+ throw new Error("crypto.getRandomValues is not available. Please use Node.js 19+ or a modern browser.");
30
+ }
31
+ return base64UrlEncode(array);
32
+ }
33
+ async function generateCodeChallenge(verifier) {
34
+ const encoder = new TextEncoder;
35
+ const data = encoder.encode(verifier);
36
+ let hashBuffer;
37
+ if (typeof crypto !== "undefined" && crypto.subtle) {
38
+ hashBuffer = await crypto.subtle.digest("SHA-256", data);
39
+ } else if (typeof globalThis !== "undefined" && globalThis.crypto?.subtle) {
40
+ hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", data);
41
+ } else {
42
+ throw new Error("crypto.subtle.digest is not available. Please use Node.js 19+ or a modern browser.");
43
+ }
44
+ return base64UrlEncode(new Uint8Array(hashBuffer));
45
+ }
46
+ function generateState() {
47
+ const array = new Uint8Array(16);
48
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
49
+ crypto.getRandomValues(array);
50
+ } else if (typeof globalThis !== "undefined" && globalThis.crypto) {
51
+ globalThis.crypto.getRandomValues(array);
52
+ } else {
53
+ throw new Error("crypto.getRandomValues is not available. Please use Node.js 19+ or a modern browser.");
54
+ }
55
+ return base64UrlEncode(array);
56
+ }
57
+ function generateStateWithReturnUrl(returnUrl) {
58
+ const csrf = generateState();
59
+ const stateData = returnUrl ? { csrf, returnUrl } : { csrf };
60
+ const encoder = new TextEncoder;
61
+ const jsonBytes = encoder.encode(JSON.stringify(stateData));
62
+ return base64UrlEncode(jsonBytes);
63
+ }
64
+ function parseState(state) {
65
+ try {
66
+ const decoded = base64UrlDecode(state);
67
+ const parsed = JSON.parse(decoded);
68
+ if (typeof parsed === "string") {
69
+ return { csrf: parsed };
70
+ } else if (parsed && typeof parsed === "object") {
71
+ return {
72
+ csrf: parsed.csrf || state,
73
+ returnUrl: parsed.returnUrl
74
+ };
75
+ }
76
+ return { csrf: state };
77
+ } catch {
78
+ return { csrf: state };
79
+ }
80
+ }
81
+ function base64UrlEncode(array) {
82
+ let base64 = "";
83
+ if (typeof Buffer !== "undefined") {
84
+ base64 = Buffer.from(array).toString("base64");
85
+ } else {
86
+ const binary = String.fromCharCode(...array);
87
+ base64 = btoa(binary);
88
+ }
89
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
90
+ }
91
+ function base64UrlDecode(str) {
92
+ let base64 = str.replace(/-/g, "+").replace(/_/g, "/");
93
+ while (base64.length % 4 !== 0) {
94
+ base64 += "=";
95
+ }
96
+ if (typeof Buffer !== "undefined") {
97
+ return Buffer.from(base64, "base64").toString("utf-8");
98
+ } else {
99
+ const binary = atob(base64);
100
+ const bytes = new Uint8Array(binary.length);
101
+ for (let i = 0;i < binary.length; i++) {
102
+ bytes[i] = binary.charCodeAt(i);
103
+ }
104
+ const decoder = new TextDecoder;
105
+ return decoder.decode(bytes);
106
+ }
107
+ }
108
+
109
+ // src/errors.ts
110
+ var exports_errors = {};
111
+ __export(exports_errors, {
112
+ parseServerError: () => parseServerError,
113
+ isTokenExpiredError: () => isTokenExpiredError,
114
+ isAuthorizationError: () => isAuthorizationError,
115
+ isAuthError: () => isAuthError,
116
+ ToolCallError: () => ToolCallError,
117
+ TokenExpiredError: () => TokenExpiredError,
118
+ IntegrateSDKError: () => IntegrateSDKError,
119
+ ConnectionError: () => ConnectionError,
120
+ AuthorizationError: () => AuthorizationError,
121
+ AuthenticationError: () => AuthenticationError
122
+ });
123
+ function isAuthError(error) {
124
+ return error instanceof AuthenticationError;
125
+ }
126
+ function isTokenExpiredError(error) {
127
+ return error instanceof TokenExpiredError;
128
+ }
129
+ function isAuthorizationError(error) {
130
+ return error instanceof AuthorizationError;
131
+ }
132
+ function parseServerError(error, context) {
133
+ if (error && typeof error === "object" && "jsonrpcError" in error) {
134
+ const jsonrpcError = error.jsonrpcError;
135
+ if (jsonrpcError && typeof jsonrpcError === "object") {
136
+ return parseServerError(jsonrpcError, context);
137
+ }
138
+ }
139
+ if (error && typeof error === "object" && "code" in error && "message" in error) {
140
+ const code = error.code;
141
+ const message = error.message || "Unknown error";
142
+ if (code === -32600) {
143
+ return new IntegrateSDKError(`Invalid request: ${message}`);
144
+ }
145
+ if (code === -32601) {
146
+ return new IntegrateSDKError(`Method not found: ${message}`);
147
+ }
148
+ if (code === -32602) {
149
+ return new IntegrateSDKError(`Invalid params: ${message}`);
150
+ }
151
+ if (code === 401 || code === -32001) {
152
+ if (message.toLowerCase().includes("expired") || message.toLowerCase().includes("token")) {
153
+ return new TokenExpiredError(message, context?.provider);
154
+ }
155
+ return new AuthenticationError(message, 401, context?.provider);
156
+ }
157
+ if (code === 403 || code === -32002) {
158
+ return new AuthorizationError(message, 403);
159
+ }
160
+ if (context?.toolName) {
161
+ return new ToolCallError(message, context.toolName, error);
162
+ }
163
+ return new IntegrateSDKError(message);
164
+ }
165
+ if (error instanceof Error) {
166
+ const message = error.message;
167
+ const statusCode = error.statusCode;
168
+ if (statusCode === 401) {
169
+ if (message.toLowerCase().includes("expired") || message.toLowerCase().includes("token")) {
170
+ return new TokenExpiredError(message, context?.provider);
171
+ }
172
+ return new AuthenticationError(message, 401, context?.provider);
173
+ }
174
+ if (statusCode === 403) {
175
+ return new AuthorizationError(message, 403);
176
+ }
177
+ if (message.includes("401") || message.includes("Unauthorized") || message.includes("unauthenticated")) {
178
+ if (message.toLowerCase().includes("expired") || message.toLowerCase().includes("token")) {
179
+ return new TokenExpiredError(message, context?.provider);
180
+ }
181
+ return new AuthenticationError(message, 401, context?.provider);
182
+ }
183
+ if (message.includes("403") || message.includes("Forbidden") || message.includes("unauthorized")) {
184
+ return new AuthorizationError(message, 403);
185
+ }
186
+ if (context?.toolName) {
187
+ return new ToolCallError(message, context.toolName, error);
188
+ }
189
+ return new IntegrateSDKError(message);
190
+ }
191
+ return new IntegrateSDKError(String(error));
192
+ }
193
+ var IntegrateSDKError, AuthenticationError, AuthorizationError, TokenExpiredError, ConnectionError, ToolCallError;
194
+ var init_errors = __esm(() => {
195
+ IntegrateSDKError = class IntegrateSDKError extends Error {
196
+ constructor(message) {
197
+ super(message);
198
+ this.name = "IntegrateSDKError";
199
+ }
200
+ };
201
+ AuthenticationError = class AuthenticationError extends IntegrateSDKError {
202
+ statusCode;
203
+ provider;
204
+ constructor(message, statusCode, provider) {
205
+ super(message);
206
+ this.name = "AuthenticationError";
207
+ this.statusCode = statusCode;
208
+ this.provider = provider;
209
+ }
210
+ };
211
+ AuthorizationError = class AuthorizationError extends IntegrateSDKError {
212
+ statusCode;
213
+ requiredScopes;
214
+ constructor(message, statusCode, requiredScopes) {
215
+ super(message);
216
+ this.name = "AuthorizationError";
217
+ this.statusCode = statusCode;
218
+ this.requiredScopes = requiredScopes;
219
+ }
220
+ };
221
+ TokenExpiredError = class TokenExpiredError extends AuthenticationError {
222
+ constructor(message, provider) {
223
+ super(message, 401, provider);
224
+ this.name = "TokenExpiredError";
225
+ }
226
+ };
227
+ ConnectionError = class ConnectionError extends IntegrateSDKError {
228
+ statusCode;
229
+ constructor(message, statusCode) {
230
+ super(message);
231
+ this.name = "ConnectionError";
232
+ this.statusCode = statusCode;
233
+ }
234
+ };
235
+ ToolCallError = class ToolCallError extends IntegrateSDKError {
236
+ toolName;
237
+ originalError;
238
+ constructor(message, toolName, originalError) {
239
+ super(message);
240
+ this.name = "ToolCallError";
241
+ this.toolName = toolName;
242
+ this.originalError = originalError;
243
+ }
244
+ };
245
+ });
246
+
247
+ // src/adapters/base-handler.ts
248
+ var MCP_SERVER_URL = "https://mcp.integrate.dev/api/v1/mcp";
249
+
250
+ class OAuthHandler {
251
+ config;
252
+ serverUrl;
253
+ apiKey;
254
+ constructor(config) {
255
+ this.config = config;
256
+ if (!config || !config.providers) {
257
+ throw new Error("OAuthHandler requires a valid config with providers");
258
+ }
259
+ this.serverUrl = config.serverUrl || MCP_SERVER_URL;
260
+ this.apiKey = config.apiKey;
261
+ }
262
+ getHeaders(additionalHeaders) {
263
+ const headers = {
264
+ ...additionalHeaders
265
+ };
266
+ if (this.apiKey) {
267
+ headers["X-API-KEY"] = this.apiKey;
268
+ }
269
+ return headers;
270
+ }
271
+ async handleAuthorize(request) {
272
+ const providerConfig = this.config.providers[request.provider];
273
+ if (!providerConfig) {
274
+ throw new Error(`Provider ${request.provider} not configured. Add OAuth credentials to your API route configuration.`);
275
+ }
276
+ if (!providerConfig.clientId || !providerConfig.clientSecret) {
277
+ throw new Error(`Missing OAuth credentials for ${request.provider}. Check your environment variables.`);
278
+ }
279
+ const url = new URL("/oauth/authorize", this.serverUrl);
280
+ url.searchParams.set("provider", request.provider);
281
+ url.searchParams.set("client_id", providerConfig.clientId);
282
+ url.searchParams.set("client_secret", providerConfig.clientSecret);
283
+ url.searchParams.set("scope", request.scopes.join(","));
284
+ url.searchParams.set("state", request.state);
285
+ url.searchParams.set("code_challenge", request.codeChallenge);
286
+ url.searchParams.set("code_challenge_method", request.codeChallengeMethod);
287
+ const redirectUri = request.redirectUri || providerConfig.redirectUri;
288
+ if (redirectUri) {
289
+ url.searchParams.set("redirect_uri", redirectUri);
290
+ }
291
+ const response = await fetch(url.toString(), {
292
+ method: "GET",
293
+ headers: this.getHeaders()
294
+ });
295
+ if (!response.ok) {
296
+ const error = await response.text();
297
+ throw new Error(`MCP server failed to generate authorization URL: ${error}`);
298
+ }
299
+ const data = await response.json();
300
+ return data;
301
+ }
302
+ async handleCallback(request) {
303
+ const providerConfig = this.config.providers[request.provider];
304
+ if (!providerConfig) {
305
+ throw new Error(`Provider ${request.provider} not configured. Add OAuth credentials to your API route configuration.`);
306
+ }
307
+ if (!providerConfig.clientId || !providerConfig.clientSecret) {
308
+ throw new Error(`Missing OAuth credentials for ${request.provider}. Check your environment variables.`);
309
+ }
310
+ const url = new URL("/oauth/callback", this.serverUrl);
311
+ const response = await fetch(url.toString(), {
312
+ method: "POST",
313
+ headers: this.getHeaders({
314
+ "Content-Type": "application/json"
315
+ }),
316
+ body: JSON.stringify({
317
+ provider: request.provider,
318
+ code: request.code,
319
+ code_verifier: request.codeVerifier,
320
+ state: request.state,
321
+ client_id: providerConfig.clientId,
322
+ client_secret: providerConfig.clientSecret,
323
+ redirect_uri: providerConfig.redirectUri
324
+ })
325
+ });
326
+ if (!response.ok) {
327
+ const error = await response.text();
328
+ throw new Error(`MCP server failed to exchange authorization code: ${error}`);
329
+ }
330
+ const data = await response.json();
331
+ return data;
332
+ }
333
+ async handleStatus(provider, accessToken) {
334
+ const url = new URL("/oauth/status", this.serverUrl);
335
+ url.searchParams.set("provider", provider);
336
+ const response = await fetch(url.toString(), {
337
+ method: "GET",
338
+ headers: this.getHeaders({
339
+ Authorization: `Bearer ${accessToken}`
340
+ })
341
+ });
342
+ if (!response.ok) {
343
+ if (response.status === 401) {
344
+ return {
345
+ authorized: false
346
+ };
347
+ }
348
+ const error = await response.text();
349
+ throw new Error(`MCP server failed to check authorization status: ${error}`);
350
+ }
351
+ const data = await response.json();
352
+ return data;
353
+ }
354
+ async handleDisconnect(request, accessToken) {
355
+ if (!accessToken) {
356
+ throw new Error("No access token provided. Cannot disconnect provider.");
357
+ }
358
+ const url = new URL("/oauth/disconnect", this.serverUrl);
359
+ const response = await fetch(url.toString(), {
360
+ method: "POST",
361
+ headers: this.getHeaders({
362
+ "Content-Type": "application/json",
363
+ Authorization: `Bearer ${accessToken}`
364
+ }),
365
+ body: JSON.stringify({
366
+ provider: request.provider
367
+ })
368
+ });
369
+ if (!response.ok) {
370
+ const error = await response.text();
371
+ throw new Error(`MCP server failed to disconnect provider: ${error}`);
372
+ }
373
+ const data = await response.json();
374
+ return data;
375
+ }
376
+ }
377
+
378
+ // src/adapters/nextjs.ts
379
+ function createNextOAuthHandler(config) {
380
+ const handler = new OAuthHandler(config);
381
+ const handlers = {
382
+ async authorize(req) {
383
+ try {
384
+ const body = await req.json();
385
+ const result = await handler.handleAuthorize(body);
386
+ return Response.json(result);
387
+ } catch (error) {
388
+ console.error("[OAuth Authorize] Error:", error);
389
+ return Response.json({ error: error.message || "Failed to get authorization URL" }, { status: 500 });
390
+ }
391
+ },
392
+ async callback(req) {
393
+ try {
394
+ const body = await req.json();
395
+ const result = await handler.handleCallback(body);
396
+ return Response.json(result);
397
+ } catch (error) {
398
+ console.error("[OAuth Callback] Error:", error);
399
+ return Response.json({ error: error.message || "Failed to exchange authorization code" }, { status: 500 });
400
+ }
401
+ },
402
+ async status(req) {
403
+ try {
404
+ const provider = req.nextUrl.searchParams.get("provider");
405
+ const authHeader = req.headers.get("authorization");
406
+ if (!provider) {
407
+ return Response.json({ error: "Missing provider query parameter" }, { status: 400 });
408
+ }
409
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
410
+ return Response.json({ error: "Missing or invalid Authorization header" }, { status: 400 });
411
+ }
412
+ const accessToken = authHeader.substring(7);
413
+ const result = await handler.handleStatus(provider, accessToken);
414
+ return Response.json(result);
415
+ } catch (error) {
416
+ console.error("[OAuth Status] Error:", error);
417
+ return Response.json({ error: error.message || "Failed to check authorization status" }, { status: 500 });
418
+ }
419
+ },
420
+ async disconnect(req) {
421
+ try {
422
+ const authHeader = req.headers.get("authorization");
423
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
424
+ return Response.json({ error: "Missing or invalid Authorization header" }, { status: 400 });
425
+ }
426
+ const accessToken = authHeader.substring(7);
427
+ const body = await req.json();
428
+ const { provider } = body;
429
+ if (!provider) {
430
+ return Response.json({ error: "Missing provider in request body" }, { status: 400 });
431
+ }
432
+ const result = await handler.handleDisconnect({ provider }, accessToken);
433
+ return Response.json(result);
434
+ } catch (error) {
435
+ console.error("[OAuth Disconnect] Error:", error);
436
+ return Response.json({ error: error.message || "Failed to disconnect provider" }, { status: 500 });
437
+ }
438
+ },
439
+ createRoutes() {
440
+ return {
441
+ async POST(req, context) {
442
+ const params = context.params instanceof Promise ? await context.params : context.params;
443
+ const action = params.action;
444
+ if (action === "authorize") {
445
+ return handlers.authorize(req);
446
+ }
447
+ if (action === "callback") {
448
+ return handlers.callback(req);
449
+ }
450
+ if (action === "disconnect") {
451
+ return handlers.disconnect(req);
452
+ }
453
+ return Response.json({ error: `Unknown action: ${action}` }, { status: 404 });
454
+ },
455
+ async GET(req, context) {
456
+ const params = context.params instanceof Promise ? await context.params : context.params;
457
+ const action = params.action;
458
+ if (action === "status") {
459
+ return handlers.status(req);
460
+ }
461
+ return Response.json({ error: `Unknown action: ${action}` }, { status: 404 });
462
+ }
463
+ };
464
+ },
465
+ toNextJsHandler(redirectConfig) {
466
+ const defaultRedirectUrl = redirectConfig?.redirectUrl || "/";
467
+ const errorRedirectUrl = redirectConfig?.errorRedirectUrl || "/auth-error";
468
+ return {
469
+ async POST(req, context) {
470
+ const params = context.params instanceof Promise ? await context.params : context.params;
471
+ const segments = params.all || [];
472
+ if (segments.length === 2 && segments[0] === "oauth") {
473
+ const action = segments[1];
474
+ if (action === "authorize") {
475
+ return handlers.authorize(req);
476
+ }
477
+ if (action === "callback") {
478
+ return handlers.callback(req);
479
+ }
480
+ if (action === "disconnect") {
481
+ return handlers.disconnect(req);
482
+ }
483
+ return Response.json({ error: `Unknown action: ${action}` }, { status: 404 });
484
+ }
485
+ return Response.json({ error: `Invalid route: /${segments.join("/")}` }, { status: 404 });
486
+ },
487
+ async GET(req, context) {
488
+ const params = context.params instanceof Promise ? await context.params : context.params;
489
+ const segments = params.all || [];
490
+ if (segments.length === 2 && segments[0] === "oauth") {
491
+ const action = segments[1];
492
+ if (action === "status") {
493
+ return handlers.status(req);
494
+ }
495
+ if (action === "callback") {
496
+ const { searchParams } = new URL(req.url);
497
+ const code = searchParams.get("code");
498
+ const state = searchParams.get("state");
499
+ const error = searchParams.get("error");
500
+ const errorDescription = searchParams.get("error_description");
501
+ if (error) {
502
+ const errorMsg = errorDescription || error;
503
+ console.error("[OAuth Redirect] Error:", errorMsg);
504
+ return Response.redirect(new URL(`${errorRedirectUrl}?error=${encodeURIComponent(errorMsg)}`, req.url));
505
+ }
506
+ if (!code || !state) {
507
+ console.error("[OAuth Redirect] Missing code or state parameter");
508
+ return Response.redirect(new URL(`${errorRedirectUrl}?error=${encodeURIComponent("Invalid OAuth callback")}`, req.url));
509
+ }
510
+ let returnUrl = defaultRedirectUrl;
511
+ try {
512
+ const { parseState: parseState2 } = await Promise.resolve().then(() => exports_pkce);
513
+ const stateData = parseState2(state);
514
+ if (stateData.returnUrl) {
515
+ returnUrl = stateData.returnUrl;
516
+ }
517
+ } catch (e) {
518
+ try {
519
+ const referrer = req.headers?.get?.("referer") || req.headers?.get?.("referrer");
520
+ if (referrer) {
521
+ const referrerUrl = new URL(referrer);
522
+ const currentUrl = new URL(req.url);
523
+ if (referrerUrl.origin === currentUrl.origin) {
524
+ returnUrl = referrerUrl.pathname + referrerUrl.search;
525
+ }
526
+ }
527
+ } catch {}
528
+ }
529
+ const targetUrl = new URL(returnUrl, req.url);
530
+ targetUrl.hash = `oauth_callback=${encodeURIComponent(JSON.stringify({ code, state }))}`;
531
+ return Response.redirect(targetUrl);
532
+ }
533
+ return Response.json({ error: `Unknown action: ${action}` }, { status: 404 });
534
+ }
535
+ return Response.json({ error: `Invalid route: /${segments.join("/")}` }, { status: 404 });
536
+ }
537
+ };
538
+ }
539
+ };
540
+ return handlers;
541
+ }
542
+
543
+ // src/protocol/jsonrpc.ts
544
+ function parseMessage(message) {
545
+ try {
546
+ return JSON.parse(message);
547
+ } catch (error) {
548
+ throw new Error(`Failed to parse JSON-RPC message: ${error}`);
549
+ }
550
+ }
551
+
552
+ // src/transport/http-session.ts
553
+ class HttpSessionTransport {
554
+ url;
555
+ headers;
556
+ timeout;
557
+ messageHandlers = new Set;
558
+ sessionId;
559
+ sseController;
560
+ connected = false;
561
+ constructor(options) {
562
+ this.url = options.url;
563
+ this.headers = options.headers || {};
564
+ this.timeout = options.timeout || 30000;
565
+ }
566
+ async connect() {
567
+ if (this.connected) {
568
+ return;
569
+ }
570
+ this.connected = true;
571
+ }
572
+ async sendRequest(method, params) {
573
+ if (!this.connected) {
574
+ throw new Error("Not connected to server");
575
+ }
576
+ const request = {
577
+ jsonrpc: "2.0",
578
+ id: Date.now() + Math.random(),
579
+ method,
580
+ params
581
+ };
582
+ const headers = {
583
+ ...this.headers,
584
+ "Content-Type": "application/json"
585
+ };
586
+ if (this.sessionId) {
587
+ headers["mcp-session-id"] = this.sessionId;
588
+ }
589
+ try {
590
+ const controller = new AbortController;
591
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
592
+ const response = await fetch(this.url, {
593
+ method: "POST",
594
+ headers,
595
+ body: JSON.stringify(request),
596
+ signal: controller.signal
597
+ });
598
+ clearTimeout(timeoutId);
599
+ if (!response.ok) {
600
+ const error = new Error(`Request failed: ${response.statusText}`);
601
+ error.statusCode = response.status;
602
+ throw error;
603
+ }
604
+ if (!this.sessionId) {
605
+ const sid = response.headers.get("mcp-session-id");
606
+ if (sid) {
607
+ this.sessionId = sid;
608
+ console.log("Session established:", sid);
609
+ this.startSSEListener();
610
+ }
611
+ }
612
+ const jsonResponse = await response.json();
613
+ if ("error" in jsonResponse) {
614
+ const error = new Error(`JSON-RPC Error ${jsonResponse.error.code}: ${jsonResponse.error.message}`);
615
+ error.code = jsonResponse.error.code;
616
+ if (jsonResponse.error.data) {
617
+ error.data = jsonResponse.error.data;
618
+ }
619
+ error.jsonrpcError = jsonResponse.error;
620
+ throw error;
621
+ }
622
+ return jsonResponse.result;
623
+ } catch (error) {
624
+ if (error instanceof Error) {
625
+ if (error.name === "AbortError") {
626
+ const timeoutError = new Error("Request timeout");
627
+ timeoutError.code = -32000;
628
+ throw timeoutError;
629
+ }
630
+ throw error;
631
+ }
632
+ throw new Error(String(error));
633
+ }
634
+ }
635
+ async startSSEListener() {
636
+ if (!this.sessionId || this.sseController) {
637
+ return;
638
+ }
639
+ this.sseController = new AbortController;
640
+ try {
641
+ const response = await fetch(this.url, {
642
+ method: "GET",
643
+ headers: {
644
+ ...this.headers,
645
+ Accept: "text/event-stream",
646
+ "mcp-session-id": this.sessionId
647
+ },
648
+ signal: this.sseController.signal
649
+ });
650
+ if (!response.ok || !response.body) {
651
+ return;
652
+ }
653
+ this.processSSEStream(response.body);
654
+ } catch (error) {
655
+ if (error instanceof Error && error.name === "AbortError") {
656
+ return;
657
+ }
658
+ console.error("SSE connection error:", error);
659
+ }
660
+ }
661
+ async processSSEStream(body) {
662
+ const reader = body.getReader();
663
+ const decoder = new TextDecoder;
664
+ let buffer = "";
665
+ try {
666
+ while (true) {
667
+ const { done, value } = await reader.read();
668
+ if (done)
669
+ break;
670
+ buffer += decoder.decode(value, { stream: true });
671
+ const lines = buffer.split(`
672
+ `);
673
+ buffer = lines.pop() || "";
674
+ for (const line of lines) {
675
+ if (line.startsWith("data: ")) {
676
+ const data = line.slice(6).trim();
677
+ if (data) {
678
+ this.handleNotification(data);
679
+ }
680
+ }
681
+ }
682
+ }
683
+ } catch (error) {
684
+ if (error instanceof Error && error.name === "AbortError") {
685
+ return;
686
+ }
687
+ console.error("SSE stream error:", error);
688
+ } finally {
689
+ reader.releaseLock();
690
+ }
691
+ }
692
+ handleNotification(data) {
693
+ try {
694
+ const message = parseMessage(data);
695
+ this.messageHandlers.forEach((handler) => {
696
+ try {
697
+ handler(message);
698
+ } catch (error) {
699
+ console.error("Error in message handler:", error);
700
+ }
701
+ });
702
+ } catch (error) {
703
+ console.error("Failed to parse notification:", error);
704
+ }
705
+ }
706
+ onMessage(handler) {
707
+ this.messageHandlers.add(handler);
708
+ return () => {
709
+ this.messageHandlers.delete(handler);
710
+ };
711
+ }
712
+ async disconnect() {
713
+ if (!this.connected) {
714
+ return;
715
+ }
716
+ if (this.sseController) {
717
+ this.sseController.abort();
718
+ this.sseController = undefined;
719
+ }
720
+ this.messageHandlers.clear();
721
+ this.sessionId = undefined;
722
+ this.connected = false;
723
+ }
724
+ isConnected() {
725
+ return this.connected;
726
+ }
727
+ getSessionId() {
728
+ return this.sessionId;
729
+ }
730
+ setHeader(key, value) {
731
+ this.headers[key] = value;
732
+ }
733
+ removeHeader(key) {
734
+ delete this.headers[key];
735
+ }
736
+ getHeaders() {
737
+ return { ...this.headers };
738
+ }
739
+ }
740
+
741
+ // src/client.ts
742
+ init_errors();
743
+
744
+ // src/utils/naming.ts
745
+ function camelToSnake(str) {
746
+ return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
747
+ }
748
+ function methodToToolName(methodName, pluginId) {
749
+ const snakeCaseMethod = camelToSnake(methodName);
750
+ return `${pluginId}_${snakeCaseMethod}`;
751
+ }
752
+ // src/oauth/window-manager.ts
753
+ function isBrowser() {
754
+ return typeof window !== "undefined" && typeof window.document !== "undefined";
755
+ }
756
+
757
+ class OAuthWindowManager {
758
+ popupWindow = null;
759
+ popupCheckInterval = null;
760
+ popupCheckTimeout = null;
761
+ openPopup(url, options) {
762
+ if (!isBrowser()) {
763
+ throw new Error("OAuthWindowManager.openPopup() can only be used in browser environments");
764
+ }
765
+ const width = options?.width || 600;
766
+ const height = options?.height || 700;
767
+ const left = window.screenX + (window.outerWidth - width) / 2;
768
+ const top = window.screenY + (window.outerHeight - height) / 2;
769
+ const features = [
770
+ `popup=yes`,
771
+ `width=${width}`,
772
+ `height=${height}`,
773
+ `left=${left}`,
774
+ `top=${top}`,
775
+ "toolbar=no",
776
+ "location=no",
777
+ "directories=no",
778
+ "status=no",
779
+ "menubar=no",
780
+ "scrollbars=yes",
781
+ "resizable=yes",
782
+ "copyhistory=no",
783
+ "noopener=no"
784
+ ].join(",");
785
+ const windowName = `oauth_popup_${Date.now()}`;
786
+ this.popupWindow = window.open(url, windowName, features);
787
+ if (!this.popupWindow) {
788
+ console.warn("Popup was blocked by the browser. Please allow popups for this site.");
789
+ return null;
790
+ }
791
+ this.popupWindow.focus();
792
+ return this.popupWindow;
793
+ }
794
+ openRedirect(url) {
795
+ if (!isBrowser()) {
796
+ throw new Error("OAuthWindowManager.openRedirect() can only be used in browser environments");
797
+ }
798
+ window.location.href = url;
799
+ }
800
+ listenForCallback(mode, timeoutMs = 5 * 60 * 1000) {
801
+ if (mode === "popup") {
802
+ return this.listenForPopupCallback(timeoutMs);
803
+ } else {
804
+ return this.listenForRedirectCallback();
805
+ }
806
+ }
807
+ listenForPopupCallback(timeoutMs) {
808
+ if (!isBrowser()) {
809
+ return Promise.reject(new Error("OAuth popup callback can only be used in browser environments"));
810
+ }
811
+ return new Promise((resolve, reject) => {
812
+ const timeout = setTimeout(() => {
813
+ this.cleanup();
814
+ reject(new Error("OAuth authorization timed out"));
815
+ }, timeoutMs);
816
+ const messageHandler = (event) => {
817
+ if (event.data && event.data.type === "oauth_callback") {
818
+ clearTimeout(timeout);
819
+ if (this.popupCheckTimeout) {
820
+ clearTimeout(this.popupCheckTimeout);
821
+ this.popupCheckTimeout = null;
822
+ }
823
+ window.removeEventListener("message", messageHandler);
824
+ const { code, state, error } = event.data;
825
+ if (error) {
826
+ this.cleanup();
827
+ reject(new Error(`OAuth error: ${error}`));
828
+ return;
829
+ }
830
+ if (!code || !state) {
831
+ this.cleanup();
832
+ reject(new Error("Invalid OAuth callback: missing code or state"));
833
+ return;
834
+ }
835
+ this.cleanup();
836
+ resolve({ code, state });
837
+ }
838
+ };
839
+ window.addEventListener("message", messageHandler);
840
+ this.popupCheckTimeout = setTimeout(() => {
841
+ this.popupCheckTimeout = null;
842
+ this.popupCheckInterval = setInterval(() => {
843
+ if (this.popupWindow?.closed) {
844
+ clearTimeout(timeout);
845
+ clearInterval(this.popupCheckInterval);
846
+ window.removeEventListener("message", messageHandler);
847
+ this.cleanup();
848
+ reject(new Error("OAuth popup was closed by user"));
849
+ }
850
+ }, 500);
851
+ }, 2000);
852
+ });
853
+ }
854
+ listenForRedirectCallback() {
855
+ if (!isBrowser()) {
856
+ return Promise.reject(new Error("OAuth redirect callback can only be used in browser environments"));
857
+ }
858
+ return new Promise((resolve, reject) => {
859
+ const params = new URLSearchParams(window.location.search);
860
+ let code = params.get("code");
861
+ let state = params.get("state");
862
+ let error = params.get("error");
863
+ let errorDescription = params.get("error_description");
864
+ if (!code && !error && window.location.hash) {
865
+ try {
866
+ const hash = window.location.hash.substring(1);
867
+ const hashParams = new URLSearchParams(hash);
868
+ const oauthCallback = hashParams.get("oauth_callback");
869
+ if (oauthCallback) {
870
+ const parsed = JSON.parse(decodeURIComponent(oauthCallback));
871
+ code = parsed.code;
872
+ state = parsed.state;
873
+ window.history.replaceState(null, "", window.location.pathname + window.location.search);
874
+ }
875
+ } catch (e) {
876
+ console.error("Failed to parse OAuth callback params from hash:", e);
877
+ }
878
+ }
879
+ if (!code && !error) {
880
+ try {
881
+ const stored = sessionStorage.getItem("oauth_callback_params");
882
+ if (stored) {
883
+ const parsed = JSON.parse(stored);
884
+ code = parsed.code;
885
+ state = parsed.state;
886
+ sessionStorage.removeItem("oauth_callback_params");
887
+ }
888
+ } catch (e) {
889
+ console.error("Failed to parse OAuth callback params from sessionStorage:", e);
890
+ }
891
+ }
892
+ if (error) {
893
+ const errorMsg = errorDescription || error;
894
+ reject(new Error(`OAuth error: ${errorMsg}`));
895
+ return;
896
+ }
897
+ if (!code || !state) {
898
+ reject(new Error("Invalid OAuth callback: missing code or state in URL"));
899
+ return;
900
+ }
901
+ resolve({ code, state });
902
+ });
903
+ }
904
+ cleanup() {
905
+ if (this.popupWindow && !this.popupWindow.closed) {
906
+ this.popupWindow.close();
907
+ }
908
+ this.popupWindow = null;
909
+ if (this.popupCheckInterval) {
910
+ clearInterval(this.popupCheckInterval);
911
+ this.popupCheckInterval = null;
912
+ }
913
+ if (this.popupCheckTimeout) {
914
+ clearTimeout(this.popupCheckTimeout);
915
+ this.popupCheckTimeout = null;
916
+ }
917
+ }
918
+ close() {
919
+ this.cleanup();
920
+ }
921
+ }
922
+
923
+ // src/oauth/manager.ts
924
+ class OAuthManager {
925
+ pendingAuths = new Map;
926
+ providerTokens = new Map;
927
+ windowManager;
928
+ flowConfig;
929
+ oauthApiBase;
930
+ constructor(oauthApiBase, flowConfig) {
931
+ this.oauthApiBase = oauthApiBase;
932
+ this.windowManager = new OAuthWindowManager;
933
+ this.flowConfig = {
934
+ mode: flowConfig?.mode || "redirect",
935
+ popupOptions: flowConfig?.popupOptions,
936
+ onAuthCallback: flowConfig?.onAuthCallback
937
+ };
938
+ this.cleanupExpiredPendingAuths();
939
+ }
940
+ async initiateFlow(provider, config, returnUrl) {
941
+ const codeVerifier = generateCodeVerifier();
942
+ const codeChallenge = await generateCodeChallenge(codeVerifier);
943
+ const state = generateStateWithReturnUrl(returnUrl);
944
+ const pendingAuth = {
945
+ provider,
946
+ state,
947
+ codeVerifier,
948
+ codeChallenge,
949
+ scopes: config.scopes,
950
+ redirectUri: config.redirectUri,
951
+ returnUrl,
952
+ initiatedAt: Date.now()
953
+ };
954
+ this.pendingAuths.set(state, pendingAuth);
955
+ this.savePendingAuthToStorage(state, pendingAuth);
956
+ const authUrl = await this.getAuthorizationUrl(provider, config.scopes, state, codeChallenge, config.redirectUri);
957
+ if (this.flowConfig.mode === "popup") {
958
+ this.windowManager.openPopup(authUrl, this.flowConfig.popupOptions);
959
+ try {
960
+ const callbackParams = await this.windowManager.listenForCallback("popup");
961
+ await this.handleCallback(callbackParams.code, callbackParams.state);
962
+ } catch (error) {
963
+ this.pendingAuths.delete(state);
964
+ throw error;
965
+ }
966
+ } else {
967
+ this.windowManager.openRedirect(authUrl);
968
+ }
969
+ }
970
+ async handleCallback(code, state) {
971
+ let pendingAuth = this.pendingAuths.get(state);
972
+ if (!pendingAuth) {
973
+ pendingAuth = this.loadPendingAuthFromStorage(state);
974
+ }
975
+ if (!pendingAuth) {
976
+ throw new Error("Invalid state parameter: no matching OAuth flow found");
977
+ }
978
+ const fiveMinutes = 5 * 60 * 1000;
979
+ if (Date.now() - pendingAuth.initiatedAt > fiveMinutes) {
980
+ this.pendingAuths.delete(state);
981
+ this.removePendingAuthFromStorage(state);
982
+ throw new Error("OAuth flow expired: please try again");
983
+ }
984
+ if (this.flowConfig.onAuthCallback) {
985
+ try {
986
+ await this.flowConfig.onAuthCallback(pendingAuth.provider, code, state);
987
+ } catch (error) {
988
+ console.error("Custom OAuth callback handler failed:", error);
989
+ }
990
+ }
991
+ try {
992
+ const response = await this.exchangeCodeForToken(pendingAuth.provider, code, pendingAuth.codeVerifier, state);
993
+ const tokenData = {
994
+ accessToken: response.accessToken,
995
+ refreshToken: response.refreshToken,
996
+ tokenType: response.tokenType,
997
+ expiresIn: response.expiresIn,
998
+ expiresAt: response.expiresAt,
999
+ scopes: response.scopes
1000
+ };
1001
+ this.providerTokens.set(pendingAuth.provider, tokenData);
1002
+ this.saveProviderToken(pendingAuth.provider, tokenData);
1003
+ this.pendingAuths.delete(state);
1004
+ this.removePendingAuthFromStorage(state);
1005
+ return { ...tokenData, provider: pendingAuth.provider };
1006
+ } catch (error) {
1007
+ this.pendingAuths.delete(state);
1008
+ this.removePendingAuthFromStorage(state);
1009
+ throw error;
1010
+ }
1011
+ }
1012
+ async checkAuthStatus(provider) {
1013
+ const tokenData = this.providerTokens.get(provider);
1014
+ if (!tokenData) {
1015
+ return {
1016
+ authorized: false,
1017
+ provider
1018
+ };
1019
+ }
1020
+ return {
1021
+ authorized: true,
1022
+ provider,
1023
+ scopes: tokenData.scopes,
1024
+ expiresAt: tokenData.expiresAt
1025
+ };
1026
+ }
1027
+ async disconnectProvider(provider) {
1028
+ const tokenData = this.providerTokens.get(provider);
1029
+ if (!tokenData) {
1030
+ throw new Error(`No access token available for provider "${provider}". Cannot disconnect provider.`);
1031
+ }
1032
+ this.providerTokens.delete(provider);
1033
+ this.clearProviderToken(provider);
1034
+ }
1035
+ getProviderToken(provider) {
1036
+ return this.providerTokens.get(provider);
1037
+ }
1038
+ getAllProviderTokens() {
1039
+ return new Map(this.providerTokens);
1040
+ }
1041
+ setProviderToken(provider, tokenData) {
1042
+ this.providerTokens.set(provider, tokenData);
1043
+ this.saveProviderToken(provider, tokenData);
1044
+ }
1045
+ clearProviderToken(provider) {
1046
+ this.providerTokens.delete(provider);
1047
+ if (typeof window !== "undefined" && window.localStorage) {
1048
+ try {
1049
+ window.localStorage.removeItem(`integrate_token_${provider}`);
1050
+ } catch (error) {
1051
+ console.error(`Failed to clear token for ${provider} from localStorage:`, error);
1052
+ }
1053
+ }
1054
+ }
1055
+ clearAllProviderTokens() {
1056
+ const providers = Array.from(this.providerTokens.keys());
1057
+ this.providerTokens.clear();
1058
+ if (typeof window !== "undefined" && window.localStorage) {
1059
+ for (const provider of providers) {
1060
+ try {
1061
+ window.localStorage.removeItem(`integrate_token_${provider}`);
1062
+ } catch (error) {
1063
+ console.error(`Failed to clear token for ${provider} from localStorage:`, error);
1064
+ }
1065
+ }
1066
+ }
1067
+ }
1068
+ clearAllPendingAuths() {
1069
+ this.pendingAuths.clear();
1070
+ if (typeof window !== "undefined" && window.localStorage) {
1071
+ try {
1072
+ const prefix = "integrate_oauth_pending_";
1073
+ const keysToRemove = [];
1074
+ for (let i = 0;i < window.localStorage.length; i++) {
1075
+ const key = window.localStorage.key(i);
1076
+ if (key && key.startsWith(prefix)) {
1077
+ keysToRemove.push(key);
1078
+ }
1079
+ }
1080
+ keysToRemove.forEach((key) => window.localStorage.removeItem(key));
1081
+ } catch (error) {
1082
+ console.error("Failed to clear pending auths from localStorage:", error);
1083
+ }
1084
+ }
1085
+ }
1086
+ saveProviderToken(provider, tokenData) {
1087
+ if (typeof window !== "undefined" && window.localStorage) {
1088
+ try {
1089
+ const key = `integrate_token_${provider}`;
1090
+ window.localStorage.setItem(key, JSON.stringify(tokenData));
1091
+ } catch (error) {
1092
+ console.error(`Failed to save token for ${provider} to localStorage:`, error);
1093
+ }
1094
+ }
1095
+ }
1096
+ loadProviderToken(provider) {
1097
+ if (typeof window !== "undefined" && window.localStorage) {
1098
+ try {
1099
+ const key = `integrate_token_${provider}`;
1100
+ const stored = window.localStorage.getItem(key);
1101
+ if (stored) {
1102
+ return JSON.parse(stored);
1103
+ }
1104
+ } catch (error) {
1105
+ console.error(`Failed to load token for ${provider} from localStorage:`, error);
1106
+ }
1107
+ }
1108
+ return;
1109
+ }
1110
+ loadAllProviderTokens(providers) {
1111
+ for (const provider of providers) {
1112
+ const tokenData = this.loadProviderToken(provider);
1113
+ if (tokenData) {
1114
+ this.providerTokens.set(provider, tokenData);
1115
+ }
1116
+ }
1117
+ }
1118
+ savePendingAuthToStorage(state, pendingAuth) {
1119
+ if (typeof window !== "undefined" && window.localStorage) {
1120
+ try {
1121
+ const key = `integrate_oauth_pending_${state}`;
1122
+ window.localStorage.setItem(key, JSON.stringify(pendingAuth));
1123
+ } catch (error) {
1124
+ console.error("Failed to save pending auth to localStorage:", error);
1125
+ }
1126
+ }
1127
+ }
1128
+ loadPendingAuthFromStorage(state) {
1129
+ if (typeof window !== "undefined" && window.localStorage) {
1130
+ try {
1131
+ const key = `integrate_oauth_pending_${state}`;
1132
+ const stored = window.localStorage.getItem(key);
1133
+ if (stored) {
1134
+ return JSON.parse(stored);
1135
+ }
1136
+ } catch (error) {
1137
+ console.error("Failed to load pending auth from localStorage:", error);
1138
+ }
1139
+ }
1140
+ return;
1141
+ }
1142
+ removePendingAuthFromStorage(state) {
1143
+ if (typeof window !== "undefined" && window.localStorage) {
1144
+ try {
1145
+ const key = `integrate_oauth_pending_${state}`;
1146
+ window.localStorage.removeItem(key);
1147
+ } catch (error) {
1148
+ console.error("Failed to remove pending auth from localStorage:", error);
1149
+ }
1150
+ }
1151
+ }
1152
+ cleanupExpiredPendingAuths() {
1153
+ if (typeof window !== "undefined" && window.localStorage) {
1154
+ try {
1155
+ const prefix = "integrate_oauth_pending_";
1156
+ const fiveMinutes = 5 * 60 * 1000;
1157
+ const now = Date.now();
1158
+ const keysToRemove = [];
1159
+ for (let i = 0;i < window.localStorage.length; i++) {
1160
+ const key = window.localStorage.key(i);
1161
+ if (key && key.startsWith(prefix)) {
1162
+ try {
1163
+ const stored = window.localStorage.getItem(key);
1164
+ if (stored) {
1165
+ const pendingAuth = JSON.parse(stored);
1166
+ if (now - pendingAuth.initiatedAt > fiveMinutes) {
1167
+ keysToRemove.push(key);
1168
+ }
1169
+ }
1170
+ } catch (error) {
1171
+ keysToRemove.push(key);
1172
+ }
1173
+ }
1174
+ }
1175
+ keysToRemove.forEach((key) => window.localStorage.removeItem(key));
1176
+ } catch (error) {
1177
+ console.error("Failed to cleanup expired pending auths:", error);
1178
+ }
1179
+ }
1180
+ }
1181
+ async getAuthorizationUrl(provider, scopes, state, codeChallenge, redirectUri) {
1182
+ const url = `${this.oauthApiBase}/authorize`;
1183
+ const response = await fetch(url, {
1184
+ method: "POST",
1185
+ headers: {
1186
+ "Content-Type": "application/json"
1187
+ },
1188
+ body: JSON.stringify({
1189
+ provider,
1190
+ scopes,
1191
+ state,
1192
+ codeChallenge,
1193
+ codeChallengeMethod: "S256",
1194
+ redirectUri
1195
+ })
1196
+ });
1197
+ if (!response.ok) {
1198
+ const error = await response.text();
1199
+ throw new Error(`Failed to get authorization URL: ${error}`);
1200
+ }
1201
+ const data = await response.json();
1202
+ return data.authorizationUrl;
1203
+ }
1204
+ async exchangeCodeForToken(provider, code, codeVerifier, state) {
1205
+ const url = `${this.oauthApiBase}/callback`;
1206
+ const response = await fetch(url, {
1207
+ method: "POST",
1208
+ headers: {
1209
+ "Content-Type": "application/json"
1210
+ },
1211
+ body: JSON.stringify({
1212
+ provider,
1213
+ code,
1214
+ codeVerifier,
1215
+ state
1216
+ })
1217
+ });
1218
+ if (!response.ok) {
1219
+ const error = await response.text();
1220
+ throw new Error(`Failed to exchange code for token: ${error}`);
1221
+ }
1222
+ const data = await response.json();
1223
+ return data;
1224
+ }
1225
+ close() {
1226
+ this.windowManager.close();
1227
+ }
1228
+ }
1229
+
1230
+ // src/client.ts
1231
+ class SimpleEventEmitter {
1232
+ handlers = new Map;
1233
+ on(event, handler) {
1234
+ if (!this.handlers.has(event)) {
1235
+ this.handlers.set(event, new Set);
1236
+ }
1237
+ this.handlers.get(event).add(handler);
1238
+ }
1239
+ off(event, handler) {
1240
+ const handlers = this.handlers.get(event);
1241
+ if (handlers) {
1242
+ handlers.delete(handler);
1243
+ }
1244
+ }
1245
+ emit(event, payload) {
1246
+ const handlers = this.handlers.get(event);
1247
+ if (handlers) {
1248
+ handlers.forEach((handler) => {
1249
+ try {
1250
+ handler(payload);
1251
+ } catch (error) {
1252
+ console.error(`Error in event handler for ${event}:`, error);
1253
+ }
1254
+ });
1255
+ }
1256
+ }
1257
+ removeAllListeners(event) {
1258
+ if (event) {
1259
+ this.handlers.delete(event);
1260
+ } else {
1261
+ this.handlers.clear();
1262
+ }
1263
+ }
1264
+ }
1265
+ var MCP_SERVER_URL2 = "https://mcp.integrate.dev/api/v1/mcp";
1266
+ var clientCache = new Map;
1267
+ var cleanupClients = new Set;
1268
+ class MCPClient {
1269
+ transport;
1270
+ plugins;
1271
+ availableTools = new Map;
1272
+ enabledToolNames = new Set;
1273
+ initialized = false;
1274
+ clientInfo;
1275
+ onReauthRequired;
1276
+ maxReauthRetries;
1277
+ authState = new Map;
1278
+ connectionMode;
1279
+ connecting = null;
1280
+ oauthManager;
1281
+ eventEmitter = new SimpleEventEmitter;
1282
+ github;
1283
+ gmail;
1284
+ server;
1285
+ constructor(config) {
1286
+ this.transport = new HttpSessionTransport({
1287
+ url: config.serverUrl || MCP_SERVER_URL2,
1288
+ headers: config.headers,
1289
+ timeout: config.timeout
1290
+ });
1291
+ const oauthApiBase = config.oauthApiBase || "/api/integrate/oauth";
1292
+ const defaultRedirectUri = this.getDefaultRedirectUri(oauthApiBase);
1293
+ this.plugins = config.plugins.map((plugin) => {
1294
+ if (plugin.oauth && !plugin.oauth.redirectUri) {
1295
+ return {
1296
+ ...plugin,
1297
+ oauth: {
1298
+ ...plugin.oauth,
1299
+ redirectUri: defaultRedirectUri
1300
+ }
1301
+ };
1302
+ }
1303
+ return plugin;
1304
+ });
1305
+ this.clientInfo = config.clientInfo || {
1306
+ name: "integrate-sdk",
1307
+ version: "0.1.0"
1308
+ };
1309
+ this.onReauthRequired = config.onReauthRequired;
1310
+ this.maxReauthRetries = config.maxReauthRetries ?? 1;
1311
+ this.connectionMode = config.connectionMode ?? "lazy";
1312
+ this.oauthManager = new OAuthManager(oauthApiBase, config.oauthFlow);
1313
+ const providers = this.plugins.filter((p) => p.oauth).map((p) => p.oauth.provider);
1314
+ this.oauthManager.loadAllProviderTokens(providers);
1315
+ for (const plugin of this.plugins) {
1316
+ for (const toolName of plugin.tools) {
1317
+ this.enabledToolNames.add(toolName);
1318
+ }
1319
+ if (plugin.oauth) {
1320
+ const hasToken = this.oauthManager.getProviderToken(plugin.oauth.provider) !== undefined;
1321
+ this.authState.set(plugin.oauth.provider, { authenticated: hasToken });
1322
+ }
1323
+ }
1324
+ this.github = this.createPluginProxy("github");
1325
+ this.gmail = this.createPluginProxy("gmail");
1326
+ this.server = this.createServerProxy();
1327
+ this.initializePlugins();
1328
+ }
1329
+ getDefaultRedirectUri(oauthApiBase) {
1330
+ if (typeof window === "undefined" || !window.location) {
1331
+ return "http://localhost:3000/api/integrate/oauth/callback";
1332
+ }
1333
+ const origin = window.location.origin;
1334
+ const normalizedPath = oauthApiBase.replace(/\/$/, "");
1335
+ return `${origin}${normalizedPath}/callback`;
1336
+ }
1337
+ async ensureConnected() {
1338
+ if (this.initialized && this.transport.isConnected()) {
1339
+ return;
1340
+ }
1341
+ if (this.connecting) {
1342
+ return this.connecting;
1343
+ }
1344
+ if (this.connectionMode === "manual" && !this.initialized) {
1345
+ throw new Error("Client not connected. Call connect() first when using manual connection mode.");
1346
+ }
1347
+ this.connecting = this.connect();
1348
+ try {
1349
+ await this.connecting;
1350
+ } finally {
1351
+ this.connecting = null;
1352
+ }
1353
+ }
1354
+ createPluginProxy(pluginId) {
1355
+ return new Proxy({}, {
1356
+ get: (_target, methodName) => {
1357
+ return async (args) => {
1358
+ await this.ensureConnected();
1359
+ const toolName = methodToToolName(methodName, pluginId);
1360
+ return await this.callToolWithRetry(toolName, args, 0);
1361
+ };
1362
+ }
1363
+ });
1364
+ }
1365
+ createServerProxy() {
1366
+ return new Proxy({}, {
1367
+ get: (_target, methodName) => {
1368
+ return async (args) => {
1369
+ await this.ensureConnected();
1370
+ const toolName = methodToToolName(methodName, "");
1371
+ const finalToolName = toolName.startsWith("_") ? toolName.substring(1) : toolName;
1372
+ return await this.callServerToolInternal(finalToolName, args);
1373
+ };
1374
+ }
1375
+ });
1376
+ }
1377
+ async callServerToolInternal(name, args) {
1378
+ if (!this.initialized) {
1379
+ throw new Error("Client not initialized. Call connect() first.");
1380
+ }
1381
+ if (!this.availableTools.has(name)) {
1382
+ throw new Error(`Tool "${name}" is not available on the server. Available tools: ${Array.from(this.availableTools.keys()).join(", ")}`);
1383
+ }
1384
+ const params = {
1385
+ name,
1386
+ arguments: args
1387
+ };
1388
+ try {
1389
+ const response = await this.transport.sendRequest("tools/call" /* TOOLS_CALL */, params);
1390
+ return response;
1391
+ } catch (error) {
1392
+ const parsedError = parseServerError(error, { toolName: name });
1393
+ throw parsedError;
1394
+ }
1395
+ }
1396
+ async initializePlugins() {
1397
+ for (const plugin of this.plugins) {
1398
+ if (plugin.onInit) {
1399
+ await plugin.onInit(this);
1400
+ }
1401
+ }
1402
+ }
1403
+ async connect() {
1404
+ for (const plugin of this.plugins) {
1405
+ if (plugin.onBeforeConnect) {
1406
+ await plugin.onBeforeConnect(this);
1407
+ }
1408
+ }
1409
+ await this.transport.connect();
1410
+ await this.initialize();
1411
+ await this.discoverTools();
1412
+ for (const plugin of this.plugins) {
1413
+ if (plugin.onAfterConnect) {
1414
+ await plugin.onAfterConnect(this);
1415
+ }
1416
+ }
1417
+ }
1418
+ async initialize() {
1419
+ const params = {
1420
+ protocolVersion: "2024-11-05",
1421
+ capabilities: {
1422
+ tools: {}
1423
+ },
1424
+ clientInfo: this.clientInfo
1425
+ };
1426
+ const response = await this.transport.sendRequest("initialize" /* INITIALIZE */, params);
1427
+ this.initialized = true;
1428
+ return response;
1429
+ }
1430
+ async discoverTools() {
1431
+ const response = await this.transport.sendRequest("tools/list" /* TOOLS_LIST */);
1432
+ for (const tool of response.tools) {
1433
+ this.availableTools.set(tool.name, tool);
1434
+ }
1435
+ const enabledTools = response.tools.filter((tool) => this.enabledToolNames.has(tool.name));
1436
+ console.log(`Discovered ${response.tools.length} tools, ${enabledTools.length} enabled by plugins`);
1437
+ }
1438
+ async _callToolByName(name, args) {
1439
+ return await this.callToolWithRetry(name, args, 0);
1440
+ }
1441
+ async callServerTool(name, args) {
1442
+ if (!this.initialized) {
1443
+ throw new Error("Client not initialized. Call connect() first.");
1444
+ }
1445
+ if (!this.availableTools.has(name)) {
1446
+ throw new Error(`Tool "${name}" is not available on the server. Available tools: ${Array.from(this.availableTools.keys()).join(", ")}`);
1447
+ }
1448
+ const params = {
1449
+ name,
1450
+ arguments: args
1451
+ };
1452
+ try {
1453
+ const response = await this.transport.sendRequest("tools/call" /* TOOLS_CALL */, params);
1454
+ return response;
1455
+ } catch (error) {
1456
+ const parsedError = parseServerError(error, { toolName: name });
1457
+ throw parsedError;
1458
+ }
1459
+ }
1460
+ async callToolWithRetry(name, args, retryCount = 0) {
1461
+ if (!this.initialized) {
1462
+ throw new Error("Client not initialized. Call connect() first.");
1463
+ }
1464
+ if (!this.enabledToolNames.has(name)) {
1465
+ throw new Error(`Tool "${name}" is not enabled. Enable it by adding the appropriate plugin.`);
1466
+ }
1467
+ if (!this.availableTools.has(name)) {
1468
+ throw new Error(`Tool "${name}" is not available on the server. Available tools: ${Array.from(this.availableTools.keys()).join(", ")}`);
1469
+ }
1470
+ const provider = this.getProviderForTool(name);
1471
+ if (provider) {
1472
+ const tokenData = this.oauthManager.getProviderToken(provider);
1473
+ if (tokenData) {
1474
+ this.transport.setHeader("Authorization", `Bearer ${tokenData.accessToken}`);
1475
+ }
1476
+ }
1477
+ const params = {
1478
+ name,
1479
+ arguments: args
1480
+ };
1481
+ try {
1482
+ const response = await this.transport.sendRequest("tools/call" /* TOOLS_CALL */, params);
1483
+ if (provider) {
1484
+ this.authState.set(provider, { authenticated: true });
1485
+ }
1486
+ return response;
1487
+ } catch (error) {
1488
+ const provider2 = this.getProviderForTool(name);
1489
+ const parsedError = parseServerError(error, { toolName: name, provider: provider2 });
1490
+ if (isAuthError(parsedError) && retryCount < this.maxReauthRetries) {
1491
+ if (provider2) {
1492
+ this.authState.set(provider2, {
1493
+ authenticated: false,
1494
+ lastError: parsedError
1495
+ });
1496
+ }
1497
+ if (this.onReauthRequired && provider2) {
1498
+ const reauthSuccess = await this.onReauthRequired({
1499
+ provider: provider2,
1500
+ error: parsedError,
1501
+ toolName: name
1502
+ });
1503
+ if (reauthSuccess) {
1504
+ return await this.callToolWithRetry(name, args, retryCount + 1);
1505
+ }
1506
+ }
1507
+ }
1508
+ throw parsedError;
1509
+ }
1510
+ }
1511
+ getProviderForTool(toolName) {
1512
+ for (const plugin of this.plugins) {
1513
+ if (plugin.tools.includes(toolName) && plugin.oauth) {
1514
+ return plugin.oauth.provider;
1515
+ }
1516
+ }
1517
+ return;
1518
+ }
1519
+ getTool(name) {
1520
+ return this.availableTools.get(name);
1521
+ }
1522
+ getAvailableTools() {
1523
+ return Array.from(this.availableTools.values());
1524
+ }
1525
+ setRequestHeader(key, value) {
1526
+ this.transport.setHeader(key, value);
1527
+ }
1528
+ getEnabledTools() {
1529
+ return Array.from(this.availableTools.values()).filter((tool) => this.enabledToolNames.has(tool.name));
1530
+ }
1531
+ getOAuthConfig(pluginId) {
1532
+ const plugin = this.plugins.find((p) => p.id === pluginId);
1533
+ return plugin?.oauth;
1534
+ }
1535
+ getAllOAuthConfigs() {
1536
+ const configs = new Map;
1537
+ for (const plugin of this.plugins) {
1538
+ if (plugin.oauth) {
1539
+ configs.set(plugin.id, plugin.oauth);
1540
+ }
1541
+ }
1542
+ return configs;
1543
+ }
1544
+ onMessage(handler) {
1545
+ return this.transport.onMessage(handler);
1546
+ }
1547
+ on(event, handler) {
1548
+ this.eventEmitter.on(event, handler);
1549
+ }
1550
+ off(event, handler) {
1551
+ this.eventEmitter.off(event, handler);
1552
+ }
1553
+ clearSessionToken() {
1554
+ this.oauthManager.clearAllProviderTokens();
1555
+ }
1556
+ async disconnectProvider(provider) {
1557
+ const plugin = this.plugins.find((p) => p.oauth?.provider === provider);
1558
+ if (!plugin?.oauth) {
1559
+ throw new Error(`No OAuth configuration found for provider: ${provider}`);
1560
+ }
1561
+ try {
1562
+ await this.oauthManager.disconnectProvider(provider);
1563
+ this.authState.set(provider, { authenticated: false });
1564
+ this.eventEmitter.emit("auth:disconnect", { provider });
1565
+ } catch (error) {
1566
+ this.eventEmitter.emit("auth:error", {
1567
+ provider,
1568
+ error
1569
+ });
1570
+ throw error;
1571
+ }
1572
+ }
1573
+ async logout() {
1574
+ this.clearSessionToken();
1575
+ this.oauthManager.clearAllPendingAuths();
1576
+ this.authState.clear();
1577
+ for (const plugin of this.plugins) {
1578
+ if (plugin.oauth) {
1579
+ this.authState.set(plugin.oauth.provider, { authenticated: false });
1580
+ }
1581
+ }
1582
+ this.eventEmitter.emit("auth:logout", {});
1583
+ }
1584
+ async disconnect() {
1585
+ for (const plugin of this.plugins) {
1586
+ if (plugin.onDisconnect) {
1587
+ await plugin.onDisconnect(this);
1588
+ }
1589
+ }
1590
+ await this.transport.disconnect();
1591
+ this.initialized = false;
1592
+ }
1593
+ isConnected() {
1594
+ return this.transport.isConnected();
1595
+ }
1596
+ isInitialized() {
1597
+ return this.initialized;
1598
+ }
1599
+ getAuthState(provider) {
1600
+ return this.authState.get(provider);
1601
+ }
1602
+ isProviderAuthenticated(provider) {
1603
+ return this.authState.get(provider)?.authenticated ?? false;
1604
+ }
1605
+ async isAuthorized(provider) {
1606
+ const status = await this.oauthManager.checkAuthStatus(provider);
1607
+ return status.authorized;
1608
+ }
1609
+ async authorizedProviders() {
1610
+ const authorized = [];
1611
+ for (const plugin of this.plugins) {
1612
+ if (plugin.oauth) {
1613
+ const status = await this.oauthManager.checkAuthStatus(plugin.oauth.provider);
1614
+ if (status.authorized) {
1615
+ authorized.push(plugin.oauth.provider);
1616
+ }
1617
+ }
1618
+ }
1619
+ return authorized;
1620
+ }
1621
+ async getAuthorizationStatus(provider) {
1622
+ return await this.oauthManager.checkAuthStatus(provider);
1623
+ }
1624
+ async authorize(provider, options) {
1625
+ const plugin = this.plugins.find((p) => p.oauth?.provider === provider);
1626
+ if (!plugin?.oauth) {
1627
+ const error = new Error(`No OAuth configuration found for provider: ${provider}`);
1628
+ this.eventEmitter.emit("auth:error", { provider, error });
1629
+ throw error;
1630
+ }
1631
+ this.eventEmitter.emit("auth:started", { provider });
1632
+ try {
1633
+ await this.oauthManager.initiateFlow(provider, plugin.oauth, options?.returnUrl);
1634
+ const tokenData = this.oauthManager.getProviderToken(provider);
1635
+ if (tokenData) {
1636
+ this.eventEmitter.emit("auth:complete", {
1637
+ provider,
1638
+ accessToken: tokenData.accessToken,
1639
+ expiresAt: tokenData.expiresAt
1640
+ });
1641
+ }
1642
+ this.authState.set(provider, { authenticated: true });
1643
+ } catch (error) {
1644
+ this.eventEmitter.emit("auth:error", { provider, error });
1645
+ throw error;
1646
+ }
1647
+ }
1648
+ async handleOAuthCallback(params) {
1649
+ try {
1650
+ const result = await this.oauthManager.handleCallback(params.code, params.state);
1651
+ this.authState.set(result.provider, { authenticated: true });
1652
+ this.eventEmitter.emit("auth:complete", {
1653
+ provider: result.provider,
1654
+ accessToken: result.accessToken,
1655
+ expiresAt: result.expiresAt
1656
+ });
1657
+ } catch (error) {
1658
+ this.eventEmitter.emit("auth:error", {
1659
+ provider: "unknown",
1660
+ error
1661
+ });
1662
+ throw error;
1663
+ }
1664
+ }
1665
+ getProviderToken(provider) {
1666
+ return this.oauthManager.getProviderToken(provider);
1667
+ }
1668
+ setProviderToken(provider, tokenData) {
1669
+ this.oauthManager.setProviderToken(provider, tokenData);
1670
+ this.authState.set(provider, { authenticated: true });
1671
+ }
1672
+ getAllProviderTokens() {
1673
+ const tokens = {};
1674
+ const allTokens = this.oauthManager.getAllProviderTokens();
1675
+ for (const [provider, tokenData] of allTokens.entries()) {
1676
+ tokens[provider] = tokenData.accessToken;
1677
+ }
1678
+ return tokens;
1679
+ }
1680
+ async reauthenticate(provider) {
1681
+ const state = this.authState.get(provider);
1682
+ if (!state) {
1683
+ throw new Error(`Provider "${provider}" not found in configured plugins`);
1684
+ }
1685
+ if (!this.onReauthRequired) {
1686
+ throw new Error("No re-authentication handler configured. Set onReauthRequired in client config.");
1687
+ }
1688
+ const lastError = state.lastError || new (await Promise.resolve().then(() => (init_errors(), exports_errors))).AuthenticationError("Manual re-authentication requested", undefined, provider);
1689
+ const success = await this.onReauthRequired({
1690
+ provider,
1691
+ error: lastError
1692
+ });
1693
+ if (success) {
1694
+ this.authState.set(provider, { authenticated: true });
1695
+ }
1696
+ return success;
1697
+ }
1698
+ }
1699
+ // src/server.ts
1700
+ function toAstroHandler(baseHandler, options) {
1701
+ const defaultRedirectUrl = options?.redirectUrl || "/";
1702
+ const errorRedirectUrl = options?.errorRedirectUrl || "/auth-error";
1703
+ return async (ctx) => {
1704
+ const wrappedHandler = async (request, context) => {
1705
+ const url = new URL(request.url);
1706
+ const method = request.method.toUpperCase();
1707
+ const pathParts = url.pathname.split("/").filter(Boolean);
1708
+ const oauthIndex = pathParts.indexOf("oauth");
1709
+ if (method === "GET" && oauthIndex >= 0 && pathParts[oauthIndex + 1] === "callback") {
1710
+ const searchParams = url.searchParams;
1711
+ const code = searchParams.get("code");
1712
+ const state = searchParams.get("state");
1713
+ const error = searchParams.get("error");
1714
+ const errorDescription = searchParams.get("error_description");
1715
+ if (error) {
1716
+ const errorMsg = errorDescription || error;
1717
+ console.error("[OAuth Redirect] Error:", errorMsg);
1718
+ return Response.redirect(new URL(`${errorRedirectUrl}?error=${encodeURIComponent(errorMsg)}`, request.url));
1719
+ }
1720
+ if (!code || !state) {
1721
+ console.error("[OAuth Redirect] Missing code or state parameter");
1722
+ return Response.redirect(new URL(`${errorRedirectUrl}?error=${encodeURIComponent("Invalid OAuth callback")}`, request.url));
1723
+ }
1724
+ let returnUrl = defaultRedirectUrl;
1725
+ try {
1726
+ const { parseState: parseState2 } = await Promise.resolve().then(() => exports_pkce);
1727
+ const stateData = parseState2(state);
1728
+ if (stateData.returnUrl) {
1729
+ returnUrl = stateData.returnUrl;
1730
+ }
1731
+ } catch (e) {
1732
+ try {
1733
+ const referrer = request.headers.get("referer") || request.headers.get("referrer");
1734
+ if (referrer) {
1735
+ const referrerUrl = new URL(referrer);
1736
+ const currentUrl = new URL(request.url);
1737
+ if (referrerUrl.origin === currentUrl.origin) {
1738
+ returnUrl = referrerUrl.pathname + referrerUrl.search;
1739
+ }
1740
+ }
1741
+ } catch {}
1742
+ }
1743
+ const targetUrl = new URL(returnUrl, request.url);
1744
+ targetUrl.hash = `oauth_callback=${encodeURIComponent(JSON.stringify({ code, state }))}`;
1745
+ return Response.redirect(targetUrl);
1746
+ }
1747
+ return baseHandler(request, context);
1748
+ };
1749
+ return wrappedHandler(ctx.request, { params: { all: ctx.params.all } });
1750
+ };
1751
+ }
1752
+ function toSolidStartHandler(baseHandler, options) {
1753
+ const wrappedHandler = toAstroHandler(baseHandler, options);
14
1754
  const handler = async (event) => {
15
- return baseHandler(event.request);
1755
+ return wrappedHandler({ request: event.request, params: {} });
16
1756
  };
17
1757
  return {
18
1758
  GET: handler,
@@ -22,6 +1762,13 @@ function toSolidStartHandler(baseHandler) {
22
1762
  DELETE: handler
23
1763
  };
24
1764
  }
1765
+ function toSvelteKitHandler(baseHandler, options) {
1766
+ const wrappedHandler = toAstroHandler(baseHandler, options);
1767
+ return async (event) => {
1768
+ const all = event.params?.all;
1769
+ return wrappedHandler({ request: event.request, params: { all } });
1770
+ };
1771
+ }
25
1772
  export {
26
1773
  toSolidStartHandler
27
1774
  };