const SMITHERY_API_BASE_URL = (process.env.SMITHERY_API_URL || "https://api.smithery.ai").replace(/\/+$/, ""); export class SmitheryConnectError extends Error { status: number; constructor(message: string, status: number) { super(message); this.name = "SmitheryConnectError"; this.status = status; } } type SmitheryNamespace = { name: string; }; type SmitheryNamespacesResponse = { namespaces?: SmitheryNamespace[]; }; type SmitheryConnectionStatus = | { state: "connected" } | { state: "auth_required"; authorizationUrl?: string } | { state: "error"; message: string } | { state: string; [key: string]: unknown }; export type SmitheryConnection = { connectionId: string; mcpUrl: string; name: string; status?: SmitheryConnectionStatus; createdAt?: string; }; type SmitheryConnectionsResponse = { connections?: SmitheryConnection[]; nextCursor?: string | null; }; function buildAuthHeaders(apiKey: string): Headers { const headers = new Headers(); headers.set("Authorization", `Bearer ${apiKey}`); headers.set("Content-Type", "application/json"); return headers; } function toApiUrl(path: string): string { return `${SMITHERY_API_BASE_URL}${path}`; } async function expectOk(response: Response, context: string): Promise { if (response.ok) return; const responseText = await response.text().catch(() => ""); const suffix = responseText ? `: ${responseText}` : ""; throw new SmitheryConnectError(`${context}: ${response.status} ${response.statusText}${suffix}`, response.status); } export function getSmitheryApiBaseUrl(): string { return SMITHERY_API_BASE_URL; } export async function listSmitheryNamespaces(apiKey: string): Promise { const response = await fetch(toApiUrl("/namespaces"), { headers: buildAuthHeaders(apiKey), }); await expectOk(response, "Failed to list Smithery namespaces"); const payload = (await response.json()) as SmitheryNamespacesResponse; return payload.namespaces ?? []; } export async function createSmitheryNamespace(apiKey: string): Promise { const response = await fetch(toApiUrl("/namespaces"), { method: "POST", headers: buildAuthHeaders(apiKey), }); await expectOk(response, "Failed to create Smithery namespace"); return (await response.json()) as SmitheryNamespace; } export async function resolveSmitheryNamespace(apiKey: string): Promise { const namespaces = await listSmitheryNamespaces(apiKey); if (namespaces.length > 0) { return namespaces[0]?.name ?? ""; } const created = await createSmitheryNamespace(apiKey); return created.name; } export async function listSmitheryConnectionsByUrl( apiKey: string, namespace: string, mcpUrl: string, ): Promise { const endpoint = new URL(toApiUrl(`/connect/${encodeURIComponent(namespace)}`)); endpoint.searchParams.set("mcpUrl", mcpUrl); const response = await fetch(endpoint.toString(), { headers: buildAuthHeaders(apiKey), }); await expectOk(response, "Failed to list Smithery connections"); const payload = (await response.json()) as SmitheryConnectionsResponse; return payload.connections ?? []; } export async function createSmitheryConnection( apiKey: string, namespace: string, params: { mcpUrl: string; name?: string }, ): Promise { const response = await fetch(toApiUrl(`/connect/${encodeURIComponent(namespace)}`), { method: "POST", headers: buildAuthHeaders(apiKey), body: JSON.stringify({ mcpUrl: params.mcpUrl, name: params.name, }), }); await expectOk(response, "Failed to create Smithery connection"); return (await response.json()) as SmitheryConnection; } export async function getSmitheryConnection( apiKey: string, namespace: string, connectionId: string, ): Promise { const response = await fetch( toApiUrl(`/connect/${encodeURIComponent(namespace)}/${encodeURIComponent(connectionId)}`), { headers: buildAuthHeaders(apiKey), }, ); await expectOk(response, "Failed to get Smithery connection"); return (await response.json()) as SmitheryConnection; } export async function deleteSmitheryConnection(apiKey: string, namespace: string, connectionId: string): Promise { const response = await fetch( toApiUrl(`/connect/${encodeURIComponent(namespace)}/${encodeURIComponent(connectionId)}`), { method: "DELETE", headers: buildAuthHeaders(apiKey), }, ); await expectOk(response, "Failed to delete Smithery connection"); }