115 lines · 4.2 KB · TypeScript
RawDownload
// ---------------------------------------------------------------------------// MCP ↔ generic auth-template converter.//// The shared add-time `AuthTemplateEditor` / `AuthMethodListEditor` speak the// plugin-agnostic `AuthTemplateEditorValue`; the hub speaks `AuthMethod`. MCP// stores a slugged `authenticationTemplate` array (`none` / `header` /// `oauth2`, one method per entry — a header method carries a single header,// not an array). These converters live with the MCP plugin because they touch// the transport-specific `McpAuthMethod` types.// --------------------------------------------------------------------------- import { AuthTemplateSlug } from "@executor-js/sdk/shared";import type { AuthTemplateEditorValue } from "@executor-js/react/components/auth-template-editor";import type { AuthMethod, Placement } from "@executor-js/react/lib/auth-placements"; import type { McpAuthMethod, McpAuthMethodInput } from "../sdk/types"; /** Convert a generic editor value into one MCP auth-method input (no slug — *  the backend assigns kind-based slugs). An apiKey method maps to a `header` *  method using its FIRST named header placement, preserving the prefix; an *  apiKey value with no usable header placement falls back to `none`. */export function mcpAuthMethodInputFromEditorValue(  value: AuthTemplateEditorValue,): McpAuthMethodInput {  if (value.kind === "oauth") return { kind: "oauth2" };  if (value.kind === "apikey") {    const header = value.placements.find(      (placement) => placement.carrier === "header" && placement.name.trim().length > 0,    );    if (!header) return { kind: "none" };    return {      kind: "header",      headerName: header.name.trim(),      ...(header.prefix ? { prefix: header.prefix } : {}),    };  }  return { kind: "none" };} /** Convert one stored MCP method into the generic editor value. */export function editorValueFromMcpAuthMethod(method: McpAuthMethod): AuthTemplateEditorValue {  if (method.kind === "oauth2") {    return { kind: "oauth", authorizationUrl: "", tokenUrl: "", scopes: [] };  }  if (method.kind === "header") {    return {      kind: "apikey",      placements: [{ carrier: "header", name: method.headerName, prefix: method.prefix ?? "" }],    };  }  return { kind: "none" };} /** Project the stored methods into the generic `AuthMethod[]` the hub renders. *  Mirrors the server's `describeMcpAuthMethods`; `custom_` slugs mark *  user-created methods (removable from the hub). `endpoint` feeds the oauth *  method's probe-at-connect `discoveryUrl`. */export function authMethodsFromConfig(  methods: readonly McpAuthMethod[],  endpoint: string,): AuthMethod[] {  return methods.map((method: McpAuthMethod): AuthMethod => {    const source: "spec" | "custom" = method.slug.startsWith("custom_") ? "custom" : "spec";    const template = AuthTemplateSlug.make(method.slug);    if (method.kind === "oauth2") {      return {        id: method.slug,        label: "OAuth",        kind: "oauth",        source,        template,        placements: [],        oauth: { discoveryUrl: endpoint, supportsDynamicRegistration: true },      };    }    if (method.kind === "header") {      return {        id: method.slug,        label: `API key (${method.headerName})`,        kind: "apikey",        source,        template,        placements: [{ carrier: "header", name: method.headerName, prefix: method.prefix ?? "" }],      };    }    return {      id: method.slug,      label: "No authentication",      kind: "none",      source,      template,      placements: [],    };  });} /** Build MCP header-method inputs for a custom method from generic placements. *  MCP carries one header per method, so only the FIRST named header placement *  is used (query placements have no MCP credential carrier). Empty when no *  usable header placement exists. */export function mcpAuthMethodInputsFromPlacements(  placements: readonly Placement[],): McpAuthMethodInput[] {  const header = placements.find(    (placement: Placement) => placement.carrier === "header" && placement.name.trim().length > 0,  );  if (!header) return [];  return [    {      kind: "header",      headerName: header.name.trim(),      ...(header.prefix ? { prefix: header.prefix } : {}),    },  ];}