Completing sign-in...
+| {children} @@ -243,7 +243,7 @@ function renderMarkdown( ), td: ({ children, ...props }) => ( |
{children}
diff --git a/autogpt_platform/frontend/src/hooks/useCredentials.ts b/autogpt_platform/frontend/src/hooks/useCredentials.ts
index eda6ab0278..9a78e5b8f4 100644
--- a/autogpt_platform/frontend/src/hooks/useCredentials.ts
+++ b/autogpt_platform/frontend/src/hooks/useCredentials.ts
@@ -100,6 +100,11 @@ export default function useCredentials(
return false;
}
+ // Filter MCP OAuth2 credentials by server URL matching
+ if (c.type === "oauth2" && c.provider === "mcp") {
+ return discriminatorValue != null && c.host === discriminatorValue;
+ }
+
// Filter by OAuth credentials that have sufficient scopes for this block
if (c.type === "oauth2") {
const requiredScopes = credsInputSchema.credentials_scopes;
diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts
index 65625f1cfb..ffc21269e6 100644
--- a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts
+++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts
@@ -749,10 +749,12 @@ export enum BlockUIType {
AGENT = "Agent",
AI = "AI",
AYRSHARE = "Ayrshare",
+ MCP_TOOL = "MCP Tool",
}
export enum SpecialBlockID {
AGENT = "e189baac-8c20-45a1-94a7-55177ea42565",
+ MCP_TOOL = "a0a4b1c2-d3e4-4f56-a7b8-c9d0e1f2a3b4",
SMART_DECISION = "3b191d9f-356f-482d-8238-ba04b6d18381",
OUTPUT = "363ae599-353e-4804-937e-b2ee3cef3da4",
}
diff --git a/autogpt_platform/frontend/src/lib/oauth-popup.ts b/autogpt_platform/frontend/src/lib/oauth-popup.ts
new file mode 100644
index 0000000000..2927887751
--- /dev/null
+++ b/autogpt_platform/frontend/src/lib/oauth-popup.ts
@@ -0,0 +1,177 @@
+/**
+ * Shared utility for OAuth popup flows with cross-origin support.
+ *
+ * Handles BroadcastChannel, postMessage, and localStorage polling
+ * to reliably receive OAuth callback results even when COOP headers
+ * sever the window.opener relationship.
+ */
+
+const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
+
+export type OAuthPopupResult = {
+ code: string;
+ state: string;
+};
+
+export type OAuthPopupOptions = {
+ /** State token to validate against incoming messages */
+ stateToken: string;
+ /**
+ * Use BroadcastChannel + localStorage polling for cross-origin OAuth (MCP).
+ * Standard OAuth only uses postMessage via window.opener.
+ */
+ useCrossOriginListeners?: boolean;
+ /** BroadcastChannel name (default: "mcp_oauth") */
+ broadcastChannelName?: string;
+ /** localStorage key for cross-origin fallback (default: "mcp_oauth_result") */
+ localStorageKey?: string;
+ /** Message types to accept (default: ["oauth_popup_result", "mcp_oauth_result"]) */
+ acceptMessageTypes?: string[];
+ /** Timeout in ms (default: 5 minutes) */
+ timeout?: number;
+};
+
+type Cleanup = {
+ /** Abort the OAuth flow and close the popup */
+ abort: (reason?: string) => void;
+ /** The AbortController signal */
+ signal: AbortSignal;
+};
+
+/**
+ * Opens an OAuth popup and sets up listeners for the callback result.
+ *
+ * Opens a blank popup synchronously (to avoid popup blockers), then navigates
+ * it to the login URL. Returns a promise that resolves with the OAuth code/state.
+ *
+ * @param loginUrl - The OAuth authorization URL to navigate to
+ * @param options - Configuration for message handling
+ * @returns Object with `promise` (resolves with OAuth result) and `abort` (cancels flow)
+ */
+export function openOAuthPopup(
+ loginUrl: string,
+ options: OAuthPopupOptions,
+): { promise: Promise |
|---|