import { useCallback } from "react";import { useAtomSet, useAtomValue } from "@effect/atom-react";import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"; import { useScope } from "@executor-js/react/api/scope-context";import { sourceWriteKeys } from "@executor-js/react/api/reactivity-keys";import { connectionsAtom } from "@executor-js/react/api/atoms";import { OAuthSignInButton, oauthCallbackUrl, oauthConnectionId, useOAuthPopupFlow, type OAuthCompletionPayload,} from "@executor-js/react/plugins/oauth-sign-in";import { slugifyNamespace } from "@executor-js/react/plugins/source-identity"; import { mcpSourceAtom, updateMcpSource } from "./atoms";import type { McpStoredSourceSchemaType } from "../sdk/stored-source"; // ---------------------------------------------------------------------------// McpSignInButton — top-bar action on the source detail page.//// Reads the source's stored endpoint + oauth2 pointer, re-runs the DCR /// authorization-code flow against a stable `mcp-oauth2-${namespace}`// connection id, and on success rewrites the source's auth pointer to// the freshly minted connection. Works whether or not the previous// Connection still exists — source-owned config is the source of truth.// --------------------------------------------------------------------------- export default function McpSignInButton(props: { sourceId: string }) { const scopeId = useScope(); const sourceResult = useAtomValue( mcpSourceAtom(scopeId, props.sourceId), ) as AsyncResult.AsyncResult<McpStoredSourceSchemaType | null, unknown>; const connectionsResult = useAtomValue(connectionsAtom(scopeId)); const doUpdate = useAtomSet(updateMcpSource, { mode: "promise" }); const oauth = useOAuthPopupFlow({ popupName: "mcp-oauth", }); const source = AsyncResult.isSuccess(sourceResult) && sourceResult.value ? sourceResult.value : null; const remote = source && source.config.transport === "remote" ? source.config : null; const oauth2 = remote && remote.auth.kind === "oauth2" ? remote.auth : null; const connections = AsyncResult.isSuccess(connectionsResult) ? (connectionsResult.value as readonly { readonly id: string }[]) : null; const isConnected = oauth2 !== null && connections !== null && connections.some((c) => c.id === oauth2.connectionId); const handleSignIn = useCallback(async () => { if (!remote || !oauth2 || !source) return; const namespaceSlug = slugifyNamespace(source.namespace) || "mcp"; await oauth.start({ payload: { endpoint: remote.endpoint, ...(remote.headers ? { headers: remote.headers } : {}), ...(remote.queryParams ? { queryParams: remote.queryParams } : {}), redirectUrl: oauthCallbackUrl(), connectionId: oauthConnectionId({ pluginId: "mcp", namespace: namespaceSlug, }), strategy: { kind: "dynamic-dcr" }, pluginId: "mcp", identityLabel: `${source.name.trim() || source.namespace || "MCP"} OAuth`, }, onSuccess: async (result: OAuthCompletionPayload) => { await doUpdate({ params: { scopeId, namespace: props.sourceId }, payload: { auth: { kind: "oauth2", connectionId: result.connectionId }, }, reactivityKeys: sourceWriteKeys, }); }, }); }, [remote, oauth2, source, scopeId, props.sourceId, doUpdate, oauth]); if (!oauth2) return null; return ( <OAuthSignInButton busy={oauth.busy} error={oauth.error} isConnected={isConnected} onSignIn={() => void handleSignIn()} reconnectingLabel="Reconnecting…" signingInLabel="Signing in…" /> );}
import { useCallback } from "react";
import { useAtomSet, useAtomValue } from "@effect/atom-react";
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
import { useScope } from "@executor-js/react/api/scope-context";
import { sourceWriteKeys } from "@executor-js/react/api/reactivity-keys";
import { connectionsAtom } from "@executor-js/react/api/atoms";
import {
OAuthSignInButton,
oauthCallbackUrl,
oauthConnectionId,
useOAuthPopupFlow,
type OAuthCompletionPayload,
} from "@executor-js/react/plugins/oauth-sign-in";
import { slugifyNamespace } from "@executor-js/react/plugins/source-identity";
import { mcpSourceAtom, updateMcpSource } from "./atoms";
import type { McpStoredSourceSchemaType } from "../sdk/stored-source";
// ---------------------------------------------------------------------------
// McpSignInButton — top-bar action on the source detail page.
//
// Reads the source's stored endpoint + oauth2 pointer, re-runs the DCR /
// authorization-code flow against a stable `mcp-oauth2-${namespace}`
// connection id, and on success rewrites the source's auth pointer to
// the freshly minted connection. Works whether or not the previous
// Connection still exists — source-owned config is the source of truth.
// ---------------------------------------------------------------------------
export default function McpSignInButton(props: { sourceId: string }) {
const scopeId = useScope();
const sourceResult = useAtomValue(
mcpSourceAtom(scopeId, props.sourceId),
) as AsyncResult.AsyncResult<McpStoredSourceSchemaType | null, unknown>;
const connectionsResult = useAtomValue(connectionsAtom(scopeId));
const doUpdate = useAtomSet(updateMcpSource, { mode: "promise" });
const oauth = useOAuthPopupFlow({
popupName: "mcp-oauth",
});
const source =
AsyncResult.isSuccess(sourceResult) && sourceResult.value ? sourceResult.value : null;
const remote = source && source.config.transport === "remote" ? source.config : null;
const oauth2 = remote && remote.auth.kind === "oauth2" ? remote.auth : null;
const connections = AsyncResult.isSuccess(connectionsResult)
? (connectionsResult.value as readonly { readonly id: string }[])
: null;
const isConnected =
oauth2 !== null &&
connections !== null &&
connections.some((c) => c.id === oauth2.connectionId);
const handleSignIn = useCallback(async () => {
if (!remote || !oauth2 || !source) return;
const namespaceSlug = slugifyNamespace(source.namespace) || "mcp";
await oauth.start({
payload: {
endpoint: remote.endpoint,
...(remote.headers ? { headers: remote.headers } : {}),
...(remote.queryParams ? { queryParams: remote.queryParams } : {}),
redirectUrl: oauthCallbackUrl(),
connectionId: oauthConnectionId({
pluginId: "mcp",
namespace: namespaceSlug,
}),
strategy: { kind: "dynamic-dcr" },
pluginId: "mcp",
identityLabel: `${source.name.trim() || source.namespace || "MCP"} OAuth`,
},
onSuccess: async (result: OAuthCompletionPayload) => {
await doUpdate({
params: { scopeId, namespace: props.sourceId },
payload: {
auth: { kind: "oauth2", connectionId: result.connectionId },
},
reactivityKeys: sourceWriteKeys,
});
},
});
}, [remote, oauth2, source, scopeId, props.sourceId, doUpdate, oauth]);
if (!oauth2) return null;
return (
<OAuthSignInButton
busy={oauth.busy}
error={oauth.error}
isConnected={isConnected}
onSignIn={() => void handleSignIn()}
reconnectingLabel="Reconnecting…"
signingInLabel="Signing in…"
/>
);
}