diff --git a/src/auth/customers.ts b/src/auth/customers.ts index 412f0a9..6dbfb1c 100644 --- a/src/auth/customers.ts +++ b/src/auth/customers.ts @@ -1,6 +1,6 @@ import { query, queryOne } from "../db/pool.js"; import { encrypt, decrypt } from "../utils/crypto.js"; -import { provisionLiteLLMKey } from "./litellm.js"; +import { provisionLiteLLMKey, provisionMcpServer } from "./litellm.js"; export interface Customer { id: string; @@ -67,6 +67,14 @@ export async function upsertCustomer(params: { ); existing.last_login_at = new Date(); existing.name = params.name; + + // Self-heal: ensure LiteLLM MCP server entry exists for this customer. + // Idempotent in provisionMcpServer; swallow errors so a LiteLLM hiccup + // doesn't block login. + provisionMcpServer(existing.id).catch((err) => { + console.error(`[customers] provisionMcpServer self-heal failed for ${existing.id}:`, err); + }); + return { customer: existing, virtualKey: getVirtualKey(existing), isNew: false }; } diff --git a/src/auth/litellm.ts b/src/auth/litellm.ts index 6556b13..29a53b2 100644 --- a/src/auth/litellm.ts +++ b/src/auth/litellm.ts @@ -25,8 +25,26 @@ function toServerAlias(customerId: string): string { return `sitebridge_${customerId.replace(/-/g, "_")}`; } +async function getMcpServer(serverAlias: string): Promise { + const res = await fetch(`${config.litellmUrl}/v1/mcp/server`, { + headers: { Authorization: `Bearer ${config.litellmMasterKey}` }, + }); + if (!res.ok) throw new Error(`LiteLLM /v1/mcp/server GET failed ${res.status}`); + const data = (await res.json()) as unknown; + const list = (Array.isArray(data) + ? data + : ((data as { data?: unknown; servers?: unknown }).data ?? + (data as { servers?: unknown }).servers ?? + [])) as LiteLLMMCPServerResponse[]; + return list.find((s) => s.server_name === serverAlias || s.alias === serverAlias) ?? null; +} + export async function provisionMcpServer(customerId: string): Promise { const serverAlias = toServerAlias(customerId); + + const existing = await getMcpServer(serverAlias); + if (existing) return serverAlias; + const demuxUrl = `${config.agilitonAccountUrl}/mcp/demux/${customerId}/mcp`; const body = {