integrate-sdk 0.3.7 → 0.3.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.
- package/dist/index.js +207 -11
- package/dist/server.js +182 -11
- package/dist/src/adapters/nextjs-oauth-redirect.d.ts +34 -0
- package/dist/src/adapters/nextjs-oauth-redirect.d.ts.map +1 -0
- package/dist/src/client.d.ts +43 -5
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/config/types.d.ts +15 -0
- package/dist/src/config/types.d.ts.map +1 -1
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/oauth/manager.d.ts +4 -0
- package/dist/src/oauth/manager.d.ts.map +1 -1
- package/dist/src/oauth/types.d.ts +30 -6
- package/dist/src/oauth/types.d.ts.map +1 -1
- package/dist/src/oauth/window-manager.d.ts +1 -1
- package/dist/src/oauth/window-manager.d.ts.map +1 -1
- package/package.json +3 -15
- package/dist/src/adapters/nextjs-callback.d.ts +0 -44
- package/dist/src/adapters/nextjs-callback.d.ts.map +0 -1
- package/src/adapters/auto-routes.ts +0 -217
- package/src/adapters/base-handler.ts +0 -212
- package/src/adapters/nextjs-callback.tsx +0 -160
- package/src/adapters/nextjs.ts +0 -318
- package/src/adapters/tanstack-start.ts +0 -264
- package/src/client.ts +0 -952
- package/src/config/types.ts +0 -180
- package/src/errors.ts +0 -207
- package/src/index.ts +0 -110
- package/src/integrations/vercel-ai.ts +0 -104
- package/src/oauth/manager.ts +0 -307
- package/src/oauth/pkce.ts +0 -127
- package/src/oauth/types.ts +0 -101
- package/src/oauth/window-manager.ts +0 -322
- package/src/plugins/generic.ts +0 -119
- package/src/plugins/github-client.ts +0 -345
- package/src/plugins/github.ts +0 -122
- package/src/plugins/gmail-client.ts +0 -114
- package/src/plugins/gmail.ts +0 -108
- package/src/plugins/server-client.ts +0 -20
- package/src/plugins/types.ts +0 -89
- package/src/protocol/jsonrpc.ts +0 -88
- package/src/protocol/messages.ts +0 -145
- package/src/server.ts +0 -117
- package/src/transport/http-session.ts +0 -322
- package/src/transport/http-stream.ts +0 -331
- package/src/utils/naming.ts +0 -52
package/dist/index.js
CHANGED
|
@@ -591,6 +591,7 @@ function base64UrlEncode(array) {
|
|
|
591
591
|
}
|
|
592
592
|
|
|
593
593
|
// src/oauth/window-manager.ts
|
|
594
|
+
"use client";
|
|
594
595
|
function isBrowser() {
|
|
595
596
|
return typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
596
597
|
}
|
|
@@ -699,6 +700,21 @@ class OAuthWindowManager {
|
|
|
699
700
|
let state = params.get("state");
|
|
700
701
|
let error = params.get("error");
|
|
701
702
|
let errorDescription = params.get("error_description");
|
|
703
|
+
if (!code && !error && window.location.hash) {
|
|
704
|
+
try {
|
|
705
|
+
const hash = window.location.hash.substring(1);
|
|
706
|
+
const hashParams = new URLSearchParams(hash);
|
|
707
|
+
const oauthCallback = hashParams.get("oauth_callback");
|
|
708
|
+
if (oauthCallback) {
|
|
709
|
+
const parsed = JSON.parse(decodeURIComponent(oauthCallback));
|
|
710
|
+
code = parsed.code;
|
|
711
|
+
state = parsed.state;
|
|
712
|
+
window.history.replaceState(null, "", window.location.pathname + window.location.search);
|
|
713
|
+
}
|
|
714
|
+
} catch (e) {
|
|
715
|
+
console.error("Failed to parse OAuth callback params from hash:", e);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
702
718
|
if (!code && !error) {
|
|
703
719
|
try {
|
|
704
720
|
const stored = sessionStorage.getItem("oauth_callback_params");
|
|
@@ -761,6 +777,8 @@ function sendCallbackToOpener(params) {
|
|
|
761
777
|
}
|
|
762
778
|
|
|
763
779
|
// src/oauth/manager.ts
|
|
780
|
+
"use client";
|
|
781
|
+
|
|
764
782
|
class OAuthManager {
|
|
765
783
|
pendingAuths = new Map;
|
|
766
784
|
sessionToken;
|
|
@@ -824,6 +842,7 @@ class OAuthManager {
|
|
|
824
842
|
try {
|
|
825
843
|
const response = await this.exchangeCodeForToken(pendingAuth.provider, code, pendingAuth.codeVerifier, state);
|
|
826
844
|
this.sessionToken = response.sessionToken;
|
|
845
|
+
this.saveSessionToken(response.sessionToken);
|
|
827
846
|
this.pendingAuths.delete(state);
|
|
828
847
|
return response.sessionToken;
|
|
829
848
|
} catch (error) {
|
|
@@ -867,9 +886,26 @@ class OAuthManager {
|
|
|
867
886
|
}
|
|
868
887
|
setSessionToken(token) {
|
|
869
888
|
this.sessionToken = token;
|
|
889
|
+
this.saveSessionToken(token);
|
|
870
890
|
}
|
|
871
891
|
clearSessionToken() {
|
|
872
892
|
this.sessionToken = undefined;
|
|
893
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
894
|
+
try {
|
|
895
|
+
window.sessionStorage.removeItem("integrate_session_token");
|
|
896
|
+
} catch (error) {
|
|
897
|
+
console.error("Failed to clear session token from sessionStorage:", error);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
saveSessionToken(token) {
|
|
902
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
903
|
+
try {
|
|
904
|
+
window.sessionStorage.setItem("integrate_session_token", token);
|
|
905
|
+
} catch (error) {
|
|
906
|
+
console.error("Failed to save session token to sessionStorage:", error);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
873
909
|
}
|
|
874
910
|
async getAuthorizationUrl(provider, scopes, state, codeChallenge, redirectUri) {
|
|
875
911
|
const url = `${this.oauthApiBase}/authorize`;
|
|
@@ -921,6 +957,42 @@ class OAuthManager {
|
|
|
921
957
|
}
|
|
922
958
|
|
|
923
959
|
// src/client.ts
|
|
960
|
+
"use client";
|
|
961
|
+
|
|
962
|
+
class SimpleEventEmitter {
|
|
963
|
+
handlers = new Map;
|
|
964
|
+
on(event, handler) {
|
|
965
|
+
if (!this.handlers.has(event)) {
|
|
966
|
+
this.handlers.set(event, new Set);
|
|
967
|
+
}
|
|
968
|
+
this.handlers.get(event).add(handler);
|
|
969
|
+
}
|
|
970
|
+
off(event, handler) {
|
|
971
|
+
const handlers = this.handlers.get(event);
|
|
972
|
+
if (handlers) {
|
|
973
|
+
handlers.delete(handler);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
emit(event, payload) {
|
|
977
|
+
const handlers = this.handlers.get(event);
|
|
978
|
+
if (handlers) {
|
|
979
|
+
handlers.forEach((handler) => {
|
|
980
|
+
try {
|
|
981
|
+
handler(payload);
|
|
982
|
+
} catch (error) {
|
|
983
|
+
console.error(`Error in event handler for ${event}:`, error);
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
removeAllListeners(event) {
|
|
989
|
+
if (event) {
|
|
990
|
+
this.handlers.delete(event);
|
|
991
|
+
} else {
|
|
992
|
+
this.handlers.clear();
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
924
996
|
var MCP_SERVER_URL = "https://mcp.integrate.dev/api/v1/mcp";
|
|
925
997
|
var clientCache = new Map;
|
|
926
998
|
var cleanupClients = new Set;
|
|
@@ -939,6 +1011,7 @@ class MCPClient {
|
|
|
939
1011
|
connectionMode;
|
|
940
1012
|
connecting = null;
|
|
941
1013
|
oauthManager;
|
|
1014
|
+
eventEmitter = new SimpleEventEmitter;
|
|
942
1015
|
github;
|
|
943
1016
|
gmail;
|
|
944
1017
|
server;
|
|
@@ -957,9 +1030,16 @@ class MCPClient {
|
|
|
957
1030
|
this.maxReauthRetries = config.maxReauthRetries ?? 1;
|
|
958
1031
|
this.connectionMode = config.connectionMode ?? "lazy";
|
|
959
1032
|
this.oauthManager = new OAuthManager(config.oauthApiBase || "/api/integrate/oauth", config.oauthFlow);
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
this.
|
|
1033
|
+
let sessionToken = config.sessionToken;
|
|
1034
|
+
if (!sessionToken) {
|
|
1035
|
+
const storedToken = this.loadSessionToken();
|
|
1036
|
+
if (storedToken) {
|
|
1037
|
+
sessionToken = storedToken;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
if (sessionToken) {
|
|
1041
|
+
this.oauthManager.setSessionToken(sessionToken);
|
|
1042
|
+
this.transport.setHeader("X-Session-Token", sessionToken);
|
|
963
1043
|
}
|
|
964
1044
|
for (const plugin of this.plugins) {
|
|
965
1045
|
for (const toolName of plugin.tools) {
|
|
@@ -1175,6 +1255,42 @@ class MCPClient {
|
|
|
1175
1255
|
onMessage(handler) {
|
|
1176
1256
|
return this.transport.onMessage(handler);
|
|
1177
1257
|
}
|
|
1258
|
+
on(event, handler) {
|
|
1259
|
+
this.eventEmitter.on(event, handler);
|
|
1260
|
+
}
|
|
1261
|
+
off(event, handler) {
|
|
1262
|
+
this.eventEmitter.off(event, handler);
|
|
1263
|
+
}
|
|
1264
|
+
saveSessionToken(token) {
|
|
1265
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
1266
|
+
try {
|
|
1267
|
+
window.sessionStorage.setItem("integrate_session_token", token);
|
|
1268
|
+
} catch (error) {
|
|
1269
|
+
console.error("Failed to save session token to sessionStorage:", error);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
loadSessionToken() {
|
|
1274
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
1275
|
+
try {
|
|
1276
|
+
return window.sessionStorage.getItem("integrate_session_token") || undefined;
|
|
1277
|
+
} catch (error) {
|
|
1278
|
+
console.error("Failed to load session token from sessionStorage:", error);
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
clearSessionToken() {
|
|
1285
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
1286
|
+
try {
|
|
1287
|
+
window.sessionStorage.removeItem("integrate_session_token");
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
console.error("Failed to clear session token from sessionStorage:", error);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
this.oauthManager.clearSessionToken();
|
|
1293
|
+
}
|
|
1178
1294
|
async disconnect() {
|
|
1179
1295
|
for (const plugin of this.plugins) {
|
|
1180
1296
|
if (plugin.onDisconnect) {
|
|
@@ -1218,18 +1334,44 @@ class MCPClient {
|
|
|
1218
1334
|
async authorize(provider) {
|
|
1219
1335
|
const plugin = this.plugins.find((p) => p.oauth?.provider === provider);
|
|
1220
1336
|
if (!plugin?.oauth) {
|
|
1221
|
-
|
|
1337
|
+
const error = new Error(`No OAuth configuration found for provider: ${provider}`);
|
|
1338
|
+
this.eventEmitter.emit("auth:error", { provider, error });
|
|
1339
|
+
throw error;
|
|
1340
|
+
}
|
|
1341
|
+
this.eventEmitter.emit("auth:started", { provider });
|
|
1342
|
+
try {
|
|
1343
|
+
await this.oauthManager.initiateFlow(provider, plugin.oauth);
|
|
1344
|
+
const sessionToken = this.oauthManager.getSessionToken();
|
|
1345
|
+
if (sessionToken) {
|
|
1346
|
+
this.saveSessionToken(sessionToken);
|
|
1347
|
+
this.eventEmitter.emit("auth:complete", { provider, sessionToken });
|
|
1348
|
+
}
|
|
1349
|
+
this.authState.set(provider, { authenticated: true });
|
|
1350
|
+
} catch (error) {
|
|
1351
|
+
this.eventEmitter.emit("auth:error", { provider, error });
|
|
1352
|
+
throw error;
|
|
1222
1353
|
}
|
|
1223
|
-
await this.oauthManager.initiateFlow(provider, plugin.oauth);
|
|
1224
|
-
this.authState.set(provider, { authenticated: true });
|
|
1225
1354
|
}
|
|
1226
1355
|
async handleOAuthCallback(params) {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1356
|
+
try {
|
|
1357
|
+
const sessionToken = await this.oauthManager.handleCallback(params.code, params.state);
|
|
1358
|
+
this.saveSessionToken(sessionToken);
|
|
1359
|
+
this.transport.setHeader("X-Session-Token", sessionToken);
|
|
1360
|
+
for (const plugin of this.plugins) {
|
|
1361
|
+
if (plugin.oauth) {
|
|
1362
|
+
this.authState.set(plugin.oauth.provider, { authenticated: true });
|
|
1363
|
+
this.eventEmitter.emit("auth:complete", {
|
|
1364
|
+
provider: plugin.oauth.provider,
|
|
1365
|
+
sessionToken
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1232
1368
|
}
|
|
1369
|
+
} catch (error) {
|
|
1370
|
+
this.eventEmitter.emit("auth:error", {
|
|
1371
|
+
provider: "unknown",
|
|
1372
|
+
error
|
|
1373
|
+
});
|
|
1374
|
+
throw error;
|
|
1233
1375
|
}
|
|
1234
1376
|
}
|
|
1235
1377
|
getSessionToken() {
|
|
@@ -1324,6 +1466,9 @@ function createMCPClient(config) {
|
|
|
1324
1466
|
console.error("Failed to connect client:", error);
|
|
1325
1467
|
});
|
|
1326
1468
|
}
|
|
1469
|
+
if (config.autoHandleOAuthCallback !== false) {
|
|
1470
|
+
processOAuthCallbackFromHash(client);
|
|
1471
|
+
}
|
|
1327
1472
|
return client;
|
|
1328
1473
|
} else {
|
|
1329
1474
|
const client = new MCPClient(config);
|
|
@@ -1336,9 +1481,35 @@ function createMCPClient(config) {
|
|
|
1336
1481
|
console.error("Failed to connect client:", error);
|
|
1337
1482
|
});
|
|
1338
1483
|
}
|
|
1484
|
+
if (config.autoHandleOAuthCallback !== false) {
|
|
1485
|
+
processOAuthCallbackFromHash(client);
|
|
1486
|
+
}
|
|
1339
1487
|
return client;
|
|
1340
1488
|
}
|
|
1341
1489
|
}
|
|
1490
|
+
function processOAuthCallbackFromHash(client) {
|
|
1491
|
+
if (typeof window === "undefined") {
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
try {
|
|
1495
|
+
const hash = window.location.hash;
|
|
1496
|
+
if (hash && hash.includes("oauth_callback=")) {
|
|
1497
|
+
const hashParams = new URLSearchParams(hash.substring(1));
|
|
1498
|
+
const oauthCallbackData = hashParams.get("oauth_callback");
|
|
1499
|
+
if (oauthCallbackData) {
|
|
1500
|
+
const callbackParams = JSON.parse(decodeURIComponent(oauthCallbackData));
|
|
1501
|
+
if (callbackParams.code && callbackParams.state) {
|
|
1502
|
+
client.handleOAuthCallback(callbackParams).catch((error) => {
|
|
1503
|
+
console.error("Failed to process OAuth callback:", error);
|
|
1504
|
+
});
|
|
1505
|
+
window.history.replaceState(null, "", window.location.pathname + window.location.search);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
} catch (error) {
|
|
1510
|
+
console.error("Failed to process OAuth callback from hash:", error);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1342
1513
|
async function clearClientCache() {
|
|
1343
1514
|
const clients = Array.from(clientCache.values());
|
|
1344
1515
|
clientCache.clear();
|
|
@@ -1356,6 +1527,30 @@ async function clearClientCache() {
|
|
|
1356
1527
|
// src/index.ts
|
|
1357
1528
|
init_nextjs();
|
|
1358
1529
|
|
|
1530
|
+
// src/adapters/nextjs-oauth-redirect.ts
|
|
1531
|
+
function createOAuthRedirectHandler(config) {
|
|
1532
|
+
const redirectUrl = config?.redirectUrl || "/";
|
|
1533
|
+
const errorRedirectUrl = config?.errorRedirectUrl || "/auth-error";
|
|
1534
|
+
return async function GET(req) {
|
|
1535
|
+
const { searchParams } = new URL(req.url);
|
|
1536
|
+
const code = searchParams.get("code");
|
|
1537
|
+
const state = searchParams.get("state");
|
|
1538
|
+
const error = searchParams.get("error");
|
|
1539
|
+
const errorDescription = searchParams.get("error_description");
|
|
1540
|
+
if (error) {
|
|
1541
|
+
const errorMsg = errorDescription || error;
|
|
1542
|
+
console.error("[OAuth Redirect] Error:", errorMsg);
|
|
1543
|
+
return Response.redirect(new URL(`${errorRedirectUrl}?error=${encodeURIComponent(errorMsg)}`, req.url));
|
|
1544
|
+
}
|
|
1545
|
+
if (!code || !state) {
|
|
1546
|
+
console.error("[OAuth Redirect] Missing code or state parameter");
|
|
1547
|
+
return Response.redirect(new URL(`${errorRedirectUrl}?error=${encodeURIComponent("Invalid OAuth callback")}`, req.url));
|
|
1548
|
+
}
|
|
1549
|
+
const targetUrl = new URL(redirectUrl, req.url);
|
|
1550
|
+
targetUrl.hash = `oauth_callback=${encodeURIComponent(JSON.stringify({ code, state }))}`;
|
|
1551
|
+
return Response.redirect(targetUrl);
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1359
1554
|
// src/adapters/tanstack-start.ts
|
|
1360
1555
|
function createTanStackOAuthHandler(config) {
|
|
1361
1556
|
const handler = new OAuthHandler(config);
|
|
@@ -1590,6 +1785,7 @@ export {
|
|
|
1590
1785
|
generateCodeChallenge,
|
|
1591
1786
|
createTanStackOAuthHandler,
|
|
1592
1787
|
createSimplePlugin,
|
|
1788
|
+
createOAuthRedirectHandler,
|
|
1593
1789
|
createNextOAuthHandler,
|
|
1594
1790
|
createMCPClient,
|
|
1595
1791
|
convertMCPToolsToVercelAI,
|
package/dist/server.js
CHANGED
|
@@ -591,6 +591,7 @@ function base64UrlEncode(array) {
|
|
|
591
591
|
}
|
|
592
592
|
|
|
593
593
|
// src/oauth/window-manager.ts
|
|
594
|
+
"use client";
|
|
594
595
|
function isBrowser() {
|
|
595
596
|
return typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
596
597
|
}
|
|
@@ -699,6 +700,21 @@ class OAuthWindowManager {
|
|
|
699
700
|
let state = params.get("state");
|
|
700
701
|
let error = params.get("error");
|
|
701
702
|
let errorDescription = params.get("error_description");
|
|
703
|
+
if (!code && !error && window.location.hash) {
|
|
704
|
+
try {
|
|
705
|
+
const hash = window.location.hash.substring(1);
|
|
706
|
+
const hashParams = new URLSearchParams(hash);
|
|
707
|
+
const oauthCallback = hashParams.get("oauth_callback");
|
|
708
|
+
if (oauthCallback) {
|
|
709
|
+
const parsed = JSON.parse(decodeURIComponent(oauthCallback));
|
|
710
|
+
code = parsed.code;
|
|
711
|
+
state = parsed.state;
|
|
712
|
+
window.history.replaceState(null, "", window.location.pathname + window.location.search);
|
|
713
|
+
}
|
|
714
|
+
} catch (e) {
|
|
715
|
+
console.error("Failed to parse OAuth callback params from hash:", e);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
702
718
|
if (!code && !error) {
|
|
703
719
|
try {
|
|
704
720
|
const stored = sessionStorage.getItem("oauth_callback_params");
|
|
@@ -761,6 +777,8 @@ function sendCallbackToOpener(params) {
|
|
|
761
777
|
}
|
|
762
778
|
|
|
763
779
|
// src/oauth/manager.ts
|
|
780
|
+
"use client";
|
|
781
|
+
|
|
764
782
|
class OAuthManager {
|
|
765
783
|
pendingAuths = new Map;
|
|
766
784
|
sessionToken;
|
|
@@ -824,6 +842,7 @@ class OAuthManager {
|
|
|
824
842
|
try {
|
|
825
843
|
const response = await this.exchangeCodeForToken(pendingAuth.provider, code, pendingAuth.codeVerifier, state);
|
|
826
844
|
this.sessionToken = response.sessionToken;
|
|
845
|
+
this.saveSessionToken(response.sessionToken);
|
|
827
846
|
this.pendingAuths.delete(state);
|
|
828
847
|
return response.sessionToken;
|
|
829
848
|
} catch (error) {
|
|
@@ -867,9 +886,26 @@ class OAuthManager {
|
|
|
867
886
|
}
|
|
868
887
|
setSessionToken(token) {
|
|
869
888
|
this.sessionToken = token;
|
|
889
|
+
this.saveSessionToken(token);
|
|
870
890
|
}
|
|
871
891
|
clearSessionToken() {
|
|
872
892
|
this.sessionToken = undefined;
|
|
893
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
894
|
+
try {
|
|
895
|
+
window.sessionStorage.removeItem("integrate_session_token");
|
|
896
|
+
} catch (error) {
|
|
897
|
+
console.error("Failed to clear session token from sessionStorage:", error);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
saveSessionToken(token) {
|
|
902
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
903
|
+
try {
|
|
904
|
+
window.sessionStorage.setItem("integrate_session_token", token);
|
|
905
|
+
} catch (error) {
|
|
906
|
+
console.error("Failed to save session token to sessionStorage:", error);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
873
909
|
}
|
|
874
910
|
async getAuthorizationUrl(provider, scopes, state, codeChallenge, redirectUri) {
|
|
875
911
|
const url = `${this.oauthApiBase}/authorize`;
|
|
@@ -921,6 +957,42 @@ class OAuthManager {
|
|
|
921
957
|
}
|
|
922
958
|
|
|
923
959
|
// src/client.ts
|
|
960
|
+
"use client";
|
|
961
|
+
|
|
962
|
+
class SimpleEventEmitter {
|
|
963
|
+
handlers = new Map;
|
|
964
|
+
on(event, handler) {
|
|
965
|
+
if (!this.handlers.has(event)) {
|
|
966
|
+
this.handlers.set(event, new Set);
|
|
967
|
+
}
|
|
968
|
+
this.handlers.get(event).add(handler);
|
|
969
|
+
}
|
|
970
|
+
off(event, handler) {
|
|
971
|
+
const handlers = this.handlers.get(event);
|
|
972
|
+
if (handlers) {
|
|
973
|
+
handlers.delete(handler);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
emit(event, payload) {
|
|
977
|
+
const handlers = this.handlers.get(event);
|
|
978
|
+
if (handlers) {
|
|
979
|
+
handlers.forEach((handler) => {
|
|
980
|
+
try {
|
|
981
|
+
handler(payload);
|
|
982
|
+
} catch (error) {
|
|
983
|
+
console.error(`Error in event handler for ${event}:`, error);
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
removeAllListeners(event) {
|
|
989
|
+
if (event) {
|
|
990
|
+
this.handlers.delete(event);
|
|
991
|
+
} else {
|
|
992
|
+
this.handlers.clear();
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
924
996
|
var MCP_SERVER_URL = "https://mcp.integrate.dev/api/v1/mcp";
|
|
925
997
|
var clientCache = new Map;
|
|
926
998
|
var cleanupClients = new Set;
|
|
@@ -939,6 +1011,7 @@ class MCPClient {
|
|
|
939
1011
|
connectionMode;
|
|
940
1012
|
connecting = null;
|
|
941
1013
|
oauthManager;
|
|
1014
|
+
eventEmitter = new SimpleEventEmitter;
|
|
942
1015
|
github;
|
|
943
1016
|
gmail;
|
|
944
1017
|
server;
|
|
@@ -957,9 +1030,16 @@ class MCPClient {
|
|
|
957
1030
|
this.maxReauthRetries = config.maxReauthRetries ?? 1;
|
|
958
1031
|
this.connectionMode = config.connectionMode ?? "lazy";
|
|
959
1032
|
this.oauthManager = new OAuthManager(config.oauthApiBase || "/api/integrate/oauth", config.oauthFlow);
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
this.
|
|
1033
|
+
let sessionToken = config.sessionToken;
|
|
1034
|
+
if (!sessionToken) {
|
|
1035
|
+
const storedToken = this.loadSessionToken();
|
|
1036
|
+
if (storedToken) {
|
|
1037
|
+
sessionToken = storedToken;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
if (sessionToken) {
|
|
1041
|
+
this.oauthManager.setSessionToken(sessionToken);
|
|
1042
|
+
this.transport.setHeader("X-Session-Token", sessionToken);
|
|
963
1043
|
}
|
|
964
1044
|
for (const plugin of this.plugins) {
|
|
965
1045
|
for (const toolName of plugin.tools) {
|
|
@@ -1175,6 +1255,42 @@ class MCPClient {
|
|
|
1175
1255
|
onMessage(handler) {
|
|
1176
1256
|
return this.transport.onMessage(handler);
|
|
1177
1257
|
}
|
|
1258
|
+
on(event, handler) {
|
|
1259
|
+
this.eventEmitter.on(event, handler);
|
|
1260
|
+
}
|
|
1261
|
+
off(event, handler) {
|
|
1262
|
+
this.eventEmitter.off(event, handler);
|
|
1263
|
+
}
|
|
1264
|
+
saveSessionToken(token) {
|
|
1265
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
1266
|
+
try {
|
|
1267
|
+
window.sessionStorage.setItem("integrate_session_token", token);
|
|
1268
|
+
} catch (error) {
|
|
1269
|
+
console.error("Failed to save session token to sessionStorage:", error);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
loadSessionToken() {
|
|
1274
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
1275
|
+
try {
|
|
1276
|
+
return window.sessionStorage.getItem("integrate_session_token") || undefined;
|
|
1277
|
+
} catch (error) {
|
|
1278
|
+
console.error("Failed to load session token from sessionStorage:", error);
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
clearSessionToken() {
|
|
1285
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
1286
|
+
try {
|
|
1287
|
+
window.sessionStorage.removeItem("integrate_session_token");
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
console.error("Failed to clear session token from sessionStorage:", error);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
this.oauthManager.clearSessionToken();
|
|
1293
|
+
}
|
|
1178
1294
|
async disconnect() {
|
|
1179
1295
|
for (const plugin of this.plugins) {
|
|
1180
1296
|
if (plugin.onDisconnect) {
|
|
@@ -1218,18 +1334,44 @@ class MCPClient {
|
|
|
1218
1334
|
async authorize(provider) {
|
|
1219
1335
|
const plugin = this.plugins.find((p) => p.oauth?.provider === provider);
|
|
1220
1336
|
if (!plugin?.oauth) {
|
|
1221
|
-
|
|
1337
|
+
const error = new Error(`No OAuth configuration found for provider: ${provider}`);
|
|
1338
|
+
this.eventEmitter.emit("auth:error", { provider, error });
|
|
1339
|
+
throw error;
|
|
1340
|
+
}
|
|
1341
|
+
this.eventEmitter.emit("auth:started", { provider });
|
|
1342
|
+
try {
|
|
1343
|
+
await this.oauthManager.initiateFlow(provider, plugin.oauth);
|
|
1344
|
+
const sessionToken = this.oauthManager.getSessionToken();
|
|
1345
|
+
if (sessionToken) {
|
|
1346
|
+
this.saveSessionToken(sessionToken);
|
|
1347
|
+
this.eventEmitter.emit("auth:complete", { provider, sessionToken });
|
|
1348
|
+
}
|
|
1349
|
+
this.authState.set(provider, { authenticated: true });
|
|
1350
|
+
} catch (error) {
|
|
1351
|
+
this.eventEmitter.emit("auth:error", { provider, error });
|
|
1352
|
+
throw error;
|
|
1222
1353
|
}
|
|
1223
|
-
await this.oauthManager.initiateFlow(provider, plugin.oauth);
|
|
1224
|
-
this.authState.set(provider, { authenticated: true });
|
|
1225
1354
|
}
|
|
1226
1355
|
async handleOAuthCallback(params) {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1356
|
+
try {
|
|
1357
|
+
const sessionToken = await this.oauthManager.handleCallback(params.code, params.state);
|
|
1358
|
+
this.saveSessionToken(sessionToken);
|
|
1359
|
+
this.transport.setHeader("X-Session-Token", sessionToken);
|
|
1360
|
+
for (const plugin of this.plugins) {
|
|
1361
|
+
if (plugin.oauth) {
|
|
1362
|
+
this.authState.set(plugin.oauth.provider, { authenticated: true });
|
|
1363
|
+
this.eventEmitter.emit("auth:complete", {
|
|
1364
|
+
provider: plugin.oauth.provider,
|
|
1365
|
+
sessionToken
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1232
1368
|
}
|
|
1369
|
+
} catch (error) {
|
|
1370
|
+
this.eventEmitter.emit("auth:error", {
|
|
1371
|
+
provider: "unknown",
|
|
1372
|
+
error
|
|
1373
|
+
});
|
|
1374
|
+
throw error;
|
|
1233
1375
|
}
|
|
1234
1376
|
}
|
|
1235
1377
|
getSessionToken() {
|
|
@@ -1324,6 +1466,9 @@ function createMCPClient(config) {
|
|
|
1324
1466
|
console.error("Failed to connect client:", error);
|
|
1325
1467
|
});
|
|
1326
1468
|
}
|
|
1469
|
+
if (config.autoHandleOAuthCallback !== false) {
|
|
1470
|
+
processOAuthCallbackFromHash(client);
|
|
1471
|
+
}
|
|
1327
1472
|
return client;
|
|
1328
1473
|
} else {
|
|
1329
1474
|
const client = new MCPClient(config);
|
|
@@ -1336,9 +1481,35 @@ function createMCPClient(config) {
|
|
|
1336
1481
|
console.error("Failed to connect client:", error);
|
|
1337
1482
|
});
|
|
1338
1483
|
}
|
|
1484
|
+
if (config.autoHandleOAuthCallback !== false) {
|
|
1485
|
+
processOAuthCallbackFromHash(client);
|
|
1486
|
+
}
|
|
1339
1487
|
return client;
|
|
1340
1488
|
}
|
|
1341
1489
|
}
|
|
1490
|
+
function processOAuthCallbackFromHash(client) {
|
|
1491
|
+
if (typeof window === "undefined") {
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
try {
|
|
1495
|
+
const hash = window.location.hash;
|
|
1496
|
+
if (hash && hash.includes("oauth_callback=")) {
|
|
1497
|
+
const hashParams = new URLSearchParams(hash.substring(1));
|
|
1498
|
+
const oauthCallbackData = hashParams.get("oauth_callback");
|
|
1499
|
+
if (oauthCallbackData) {
|
|
1500
|
+
const callbackParams = JSON.parse(decodeURIComponent(oauthCallbackData));
|
|
1501
|
+
if (callbackParams.code && callbackParams.state) {
|
|
1502
|
+
client.handleOAuthCallback(callbackParams).catch((error) => {
|
|
1503
|
+
console.error("Failed to process OAuth callback:", error);
|
|
1504
|
+
});
|
|
1505
|
+
window.history.replaceState(null, "", window.location.pathname + window.location.search);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
} catch (error) {
|
|
1510
|
+
console.error("Failed to process OAuth callback from hash:", error);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1342
1513
|
async function clearClientCache() {
|
|
1343
1514
|
const clients = Array.from(clientCache.values());
|
|
1344
1515
|
clientCache.clear();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js OAuth Redirect Handler
|
|
3
|
+
* Handles OAuth callback redirects and forwards parameters to the client
|
|
4
|
+
*/
|
|
5
|
+
type NextRequest = any;
|
|
6
|
+
type NextResponse = any;
|
|
7
|
+
export interface OAuthRedirectConfig {
|
|
8
|
+
/** URL to redirect to after OAuth callback (default: '/') */
|
|
9
|
+
redirectUrl?: string;
|
|
10
|
+
/** URL to redirect to on OAuth error (default: '/auth-error') */
|
|
11
|
+
errorRedirectUrl?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create OAuth redirect handler for Next.js
|
|
15
|
+
*
|
|
16
|
+
* This handler processes OAuth callbacks from providers and redirects
|
|
17
|
+
* to your application with the OAuth parameters encoded in the URL.
|
|
18
|
+
*
|
|
19
|
+
* @param config - Redirect configuration
|
|
20
|
+
* @returns Next.js route handler
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* // app/oauth/callback/route.ts
|
|
25
|
+
* import { createOAuthRedirectHandler } from 'integrate-sdk';
|
|
26
|
+
*
|
|
27
|
+
* export const GET = createOAuthRedirectHandler({
|
|
28
|
+
* redirectUrl: '/dashboard',
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function createOAuthRedirectHandler(config?: OAuthRedirectConfig): (req: NextRequest) => Promise<NextResponse>;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=nextjs-oauth-redirect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nextjs-oauth-redirect.d.ts","sourceRoot":"","sources":["../../../src/adapters/nextjs-oauth-redirect.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,KAAK,WAAW,GAAG,GAAG,CAAC;AACvB,KAAK,YAAY,GAAG,GAAG,CAAC;AAExB,MAAM,WAAW,mBAAmB;IAClC,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,CAAC,EAAE,mBAAmB,IAI3C,KAAK,WAAW,KAAG,OAAO,CAAC,YAAY,CAAC,CAkCnE"}
|