@mcp-ts/sdk 1.6.1 → 1.6.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.
- package/dist/client/react.js +96 -44
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +96 -44
- package/dist/client/react.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client/react/oauth-popup.tsx +111 -51
package/dist/client/react.js
CHANGED
|
@@ -627,14 +627,31 @@ function useMcp(options) {
|
|
|
627
627
|
}
|
|
628
628
|
var AUTH_CODE_MESSAGE = "MCP_AUTH_CODE";
|
|
629
629
|
var AUTH_RESULT_MESSAGE = "MCP_AUTH_RESULT";
|
|
630
|
+
var AUTH_CHANNEL_NAME = "mcp-auth-channel";
|
|
631
|
+
function createAuthBroadcastChannel() {
|
|
632
|
+
if (typeof BroadcastChannel === "undefined") {
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
try {
|
|
636
|
+
return new BroadcastChannel(AUTH_CHANNEL_NAME);
|
|
637
|
+
} catch {
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
630
641
|
function postPopupResult(popupWindow, result) {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
window.location.origin
|
|
637
|
-
|
|
642
|
+
const payload = {
|
|
643
|
+
type: AUTH_RESULT_MESSAGE,
|
|
644
|
+
...result
|
|
645
|
+
};
|
|
646
|
+
try {
|
|
647
|
+
popupWindow?.postMessage(payload, window.location.origin);
|
|
648
|
+
} catch {
|
|
649
|
+
}
|
|
650
|
+
const channel = createAuthBroadcastChannel();
|
|
651
|
+
if (channel) {
|
|
652
|
+
channel.postMessage(payload);
|
|
653
|
+
channel.close();
|
|
654
|
+
}
|
|
638
655
|
}
|
|
639
656
|
function openCenteredPopup(url, options = {}) {
|
|
640
657
|
const {
|
|
@@ -675,60 +692,87 @@ function createOAuthPopupRedirectHandler(options = {}) {
|
|
|
675
692
|
}
|
|
676
693
|
function useMcpOAuthPopup(connections, finishAuth) {
|
|
677
694
|
const pendingPopupsRef = react.useRef(/* @__PURE__ */ new Map());
|
|
695
|
+
const processingCodesRef = react.useRef(/* @__PURE__ */ new Set());
|
|
678
696
|
react.useEffect(() => {
|
|
679
697
|
const handleMessage = async (event) => {
|
|
680
|
-
if (event.origin !== window.location.origin) {
|
|
698
|
+
if (event.origin && event.origin !== window.location.origin) {
|
|
681
699
|
return;
|
|
682
700
|
}
|
|
683
|
-
|
|
701
|
+
const code = typeof event.data?.code === "string" ? event.data.code : "";
|
|
702
|
+
if (event.data?.type !== AUTH_CODE_MESSAGE || !code) {
|
|
684
703
|
return;
|
|
685
704
|
}
|
|
686
705
|
const popupWindow = event.source && "postMessage" in event.source ? event.source : null;
|
|
687
706
|
const targetSessionId = typeof event.data.sessionId === "string" ? event.data.sessionId : "";
|
|
707
|
+
if (popupWindow && targetSessionId) {
|
|
708
|
+
pendingPopupsRef.current.set(targetSessionId, popupWindow);
|
|
709
|
+
}
|
|
688
710
|
if (!targetSessionId) {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
711
|
+
if (popupWindow) {
|
|
712
|
+
postPopupResult(popupWindow, {
|
|
713
|
+
success: false,
|
|
714
|
+
error: "Missing OAuth session identifier"
|
|
715
|
+
});
|
|
716
|
+
}
|
|
693
717
|
return;
|
|
694
718
|
}
|
|
695
719
|
const targetSession = connections.find((connection) => connection.sessionId === targetSessionId);
|
|
696
720
|
if (!targetSession) {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
721
|
+
if (popupWindow) {
|
|
722
|
+
postPopupResult(popupWindow, {
|
|
723
|
+
sessionId: targetSessionId,
|
|
724
|
+
success: false,
|
|
725
|
+
error: "OAuth session not found in the current client state"
|
|
726
|
+
});
|
|
727
|
+
}
|
|
702
728
|
return;
|
|
703
729
|
}
|
|
704
|
-
|
|
705
|
-
|
|
730
|
+
const codeKey = `${targetSession.sessionId}:${code}`;
|
|
731
|
+
if (processingCodesRef.current.has(codeKey)) {
|
|
732
|
+
return;
|
|
706
733
|
}
|
|
734
|
+
processingCodesRef.current.add(codeKey);
|
|
707
735
|
try {
|
|
708
|
-
await finishAuth(targetSession.sessionId,
|
|
736
|
+
await finishAuth(targetSession.sessionId, code);
|
|
709
737
|
} catch (error) {
|
|
738
|
+
processingCodesRef.current.delete(codeKey);
|
|
710
739
|
pendingPopupsRef.current.delete(targetSession.sessionId);
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
740
|
+
if (popupWindow) {
|
|
741
|
+
postPopupResult(popupWindow, {
|
|
742
|
+
sessionId: targetSession.sessionId,
|
|
743
|
+
success: false,
|
|
744
|
+
error: error instanceof Error ? error.message : "Failed to finish auth"
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
const channel = createAuthBroadcastChannel();
|
|
750
|
+
const handleChannelMessage = (event) => {
|
|
751
|
+
if (event.data?.type === AUTH_CODE_MESSAGE) {
|
|
752
|
+
void handleMessage(event);
|
|
716
753
|
}
|
|
717
754
|
};
|
|
718
755
|
window.addEventListener("message", handleMessage);
|
|
719
|
-
|
|
756
|
+
channel?.addEventListener("message", handleChannelMessage);
|
|
757
|
+
return () => {
|
|
758
|
+
window.removeEventListener("message", handleMessage);
|
|
759
|
+
channel?.removeEventListener("message", handleChannelMessage);
|
|
760
|
+
channel?.close();
|
|
761
|
+
};
|
|
720
762
|
}, [connections, finishAuth]);
|
|
721
763
|
react.useEffect(() => {
|
|
722
764
|
for (const connection of connections) {
|
|
723
|
-
const popupWindow = pendingPopupsRef.current.get(connection.sessionId);
|
|
724
|
-
if (
|
|
725
|
-
continue;
|
|
726
|
-
}
|
|
727
|
-
if (connection.state === "AUTHENTICATED") {
|
|
765
|
+
const popupWindow = pendingPopupsRef.current.get(connection.sessionId) || null;
|
|
766
|
+
if (connection.state === "AUTHENTICATED" || connection.state === "READY" || connection.state === "CONNECTED") {
|
|
728
767
|
postPopupResult(popupWindow, {
|
|
729
768
|
sessionId: connection.sessionId,
|
|
730
769
|
success: true
|
|
731
770
|
});
|
|
771
|
+
for (const codeKey of processingCodesRef.current) {
|
|
772
|
+
if (codeKey.startsWith(`${connection.sessionId}:`)) {
|
|
773
|
+
processingCodesRef.current.delete(codeKey);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
732
776
|
pendingPopupsRef.current.delete(connection.sessionId);
|
|
733
777
|
continue;
|
|
734
778
|
}
|
|
@@ -738,6 +782,11 @@ function useMcpOAuthPopup(connections, finishAuth) {
|
|
|
738
782
|
success: false,
|
|
739
783
|
error: connection.error || "Failed to complete authorization"
|
|
740
784
|
});
|
|
785
|
+
for (const codeKey of processingCodesRef.current) {
|
|
786
|
+
if (codeKey.startsWith(`${connection.sessionId}:`)) {
|
|
787
|
+
processingCodesRef.current.delete(codeKey);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
741
790
|
pendingPopupsRef.current.delete(connection.sessionId);
|
|
742
791
|
}
|
|
743
792
|
}
|
|
@@ -758,10 +807,9 @@ function McpOAuthCallbackContent({
|
|
|
758
807
|
}) {
|
|
759
808
|
const [phase, setPhase] = react.useState(debugPhase || "loading");
|
|
760
809
|
const [errorMessage, setErrorMessage] = react.useState("");
|
|
761
|
-
const openerMissing = typeof window !== "undefined" ? !window.opener : false;
|
|
762
810
|
const missingCode = !code;
|
|
763
811
|
const missingSessionId = !sessionId;
|
|
764
|
-
const blockingError =
|
|
812
|
+
const blockingError = missingCode ? "Error: No authorization code received." : missingSessionId ? "Error: No OAuth state received." : null;
|
|
765
813
|
react.useEffect(() => {
|
|
766
814
|
if (debugPhase) {
|
|
767
815
|
setPhase(debugPhase);
|
|
@@ -774,8 +822,9 @@ function McpOAuthCallbackContent({
|
|
|
774
822
|
return;
|
|
775
823
|
}
|
|
776
824
|
let closed = false;
|
|
825
|
+
const channel = createAuthBroadcastChannel();
|
|
777
826
|
const handleResult = (event) => {
|
|
778
|
-
if (event.origin !== window.location.origin) {
|
|
827
|
+
if (event.origin && event.origin !== window.location.origin) {
|
|
779
828
|
return;
|
|
780
829
|
}
|
|
781
830
|
if (event.data?.type !== AUTH_RESULT_MESSAGE) {
|
|
@@ -787,6 +836,7 @@ function McpOAuthCallbackContent({
|
|
|
787
836
|
if (event.data.success) {
|
|
788
837
|
setPhase("success");
|
|
789
838
|
window.removeEventListener("message", handleResult);
|
|
839
|
+
channel?.close();
|
|
790
840
|
closed = true;
|
|
791
841
|
window.setTimeout(() => window.close(), 1200);
|
|
792
842
|
return;
|
|
@@ -796,21 +846,23 @@ function McpOAuthCallbackContent({
|
|
|
796
846
|
setErrorMessage(message);
|
|
797
847
|
};
|
|
798
848
|
window.addEventListener("message", handleResult);
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
console.error("Failed to communicate with opener:", error);
|
|
806
|
-
window.setTimeout(() => {
|
|
849
|
+
channel?.addEventListener("message", handleResult);
|
|
850
|
+
const payload = { type: AUTH_CODE_MESSAGE, code, sessionId };
|
|
851
|
+
if (window.opener) {
|
|
852
|
+
try {
|
|
853
|
+
window.opener.postMessage(payload, window.location.origin);
|
|
854
|
+
} catch {
|
|
807
855
|
setPhase("error");
|
|
808
856
|
setErrorMessage("Error: Could not communicate with main window.");
|
|
809
|
-
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
if (channel) {
|
|
860
|
+
channel.postMessage(payload);
|
|
810
861
|
}
|
|
811
862
|
return () => {
|
|
812
863
|
if (!closed) {
|
|
813
864
|
window.removeEventListener("message", handleResult);
|
|
865
|
+
channel?.close();
|
|
814
866
|
}
|
|
815
867
|
};
|
|
816
868
|
}, [blockingError, code, sessionId, debugPhase]);
|