import { inject, injectable } from "@codemation/core"; import type { McpServerDeclaration } from "@codemation/core"; import type { AppGalleryEntry, AppsResponse, CredentialInstanceDto } from "../contracts/CredentialContractsRegistry"; import { CredentialTypeRegistryImpl } from "../../domain/credentials/CredentialServices"; @injectable() export class AppGalleryProjector { constructor( @inject(CredentialTypeRegistryImpl) private readonly credentialTypeRegistry: CredentialTypeRegistryImpl, ) {} project( mcpServers: ReadonlyArray | null, instances: ReadonlyArray, ): AppsResponse { if (mcpServers === null) { return { apps: [], customInstances: [...instances] }; } const mcpInstancedSetIds = new Set(); const apps: AppGalleryEntry[] = mcpServers.map((mcp) => { const accepted = mcp.acceptedCredentialTypes ?? []; const acceptedSet = new Set(accepted); const mcpInstances = instances.filter((i) => acceptedSet.has(i.typeId)); for (const i of mcpInstances) { mcpInstancedSetIds.add(i.instanceId); } const primaryOAuthTypeId = this.findPrimaryOAuthTypeId(accepted); return { mcpId: mcp.id, displayName: mcp.displayName, description: mcp.description, iconUrl: null, acceptedCredentialTypes: [...accepted], primaryOAuthTypeId, instances: mcpInstances, }; }); const customInstances = instances.filter((i) => !mcpInstancedSetIds.has(i.instanceId)); return { apps, customInstances }; } private findPrimaryOAuthTypeId(acceptedTypes: ReadonlyArray): string | null { for (const typeId of acceptedTypes) { const credType = this.credentialTypeRegistry.getCredentialType(typeId); if (credType?.definition.auth?.kind === "oauth2") { return typeId; } } return null; } }