104 lines · 3.8 KB · TypeScript
RawDownload
import { useMemo, useState } from "react";import { useAtomValue } from "@effect/atom-react";import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"; import {  AuthTemplateSlug,  IntegrationSlug,  type Connection,  type Owner,} from "@executor-js/sdk/shared";import { connectionsAllAtom } from "@executor-js/react/api/atoms";import { AddAccountModal } from "@executor-js/react/components/add-account-modal";import { OAuthSignInButton } from "@executor-js/react/plugins/oauth-sign-in";import type { AuthMethod } from "@executor-js/react/lib/auth-placements"; import { mcpServerAtom } from "./atoms";import type { McpAuthMethod } from "../sdk/types"; // ---------------------------------------------------------------------------// McpSignInButton — top-bar action on the integration detail page (v2).//// Reads the integration's declared auth methods; when one is `oauth2` it runs// the OAuth flow to mint a connection through that method. "Connected" is// derived from whether ANY owner already has a connection for this integration// (the global owner toggle is retired, so the check merges both owners). The// NEW connection's owner is a real create-target — chosen EXPLICITLY via the// `owner` prop (default Workspace `org` on an org-scoped host, Local `org` on// a non-org host like local), never read from an ambient owner.// --------------------------------------------------------------------------- export default function McpSignInButton(props: { integrationId: string; owner?: Owner }) {  const slug = IntegrationSlug.make(props.integrationId);  const targetOwner: Owner = props.owner ?? "org";  const serverResult = useAtomValue(mcpServerAtom(slug));  const connectionsResult = useAtomValue(connectionsAllAtom);  const [modalOpen, setModalOpen] = useState(false);   const server = AsyncResult.isSuccess(serverResult) ? serverResult.value : null;  const remote = server !== null && server.config.transport === "remote" ? server.config : null;  const oauthMethod =    remote?.authenticationTemplate.find((method: McpAuthMethod) => method.kind === "oauth2") ??    null;  const connections: readonly Connection[] = AsyncResult.isSuccess(connectionsResult)    ? connectionsResult.value    : [];  const hasConnection = connections.some(    (connection: Connection) => connection.integration === slug,  );   const methods = useMemo<readonly AuthMethod[]>(    () =>      remote === null || oauthMethod === null        ? []        : [            {              id: oauthMethod.slug,              label: "OAuth",              kind: "oauth",              source: "spec",              template: AuthTemplateSlug.make(oauthMethod.slug),              placements: [],              oauth: { discoveryUrl: remote.endpoint, supportsDynamicRegistration: true },            },          ],    [remote, oauthMethod],  );  const initialState = useMemo(    () =>      modalOpen && server && oauthMethod        ? {            key: `${String(slug)}:${targetOwner}:oauth`,            owner: targetOwner,            template: oauthMethod.slug,            label: `${server.description || String(slug)} OAuth`,          }        : null,    [modalOpen, oauthMethod, server, slug, targetOwner],  );   if (oauthMethod === null) return null;   return (    <>      <OAuthSignInButton        busy={false}        error={null}        isConnected={hasConnection}        onSignIn={() => setModalOpen(true)}        reconnectingLabel="Reconnecting…"        signingInLabel="Signing in…"      />      {server ? (        <AddAccountModal          integration={slug}          integrationName={server.description || String(slug)}          methods={methods}          open={modalOpen}          onOpenChange={setModalOpen}          initialState={initialState}        />      ) : null}    </>  );}