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