Skip to main content
Glama

Convex MCP server

Official
by get-convex
utils.ts3.99 kB
import * as base64 from "js-base64"; import { ReactNode } from "react"; import { toast as sonnerToast } from "sonner"; import * as IdEncoding from "id-encoding"; import { FilterExpression } from "system-udfs/convex/_system/frontend/lib/filters"; export function dismissToast(id: string) { sonnerToast.dismiss(id); } export async function copyTextToClipboard(text: string) { try { if ("clipboard" in navigator) { return await navigator.clipboard.writeText(text); } return document.execCommand("copy", true, text); } catch (e) { toast( "error", "Error copying text to clipboard. Please try again.", undefined, ); // Re-throw so the caller can handle it throw e; } } export const isUserTableName = (name: string) => !name.startsWith("_"); /** * @param type What type of toast to render (decides which icon and colors to use). * @param message The message to display with the toast. * @param id If set, we will update the current toast if a toast with `id` * is already displayed instead of opening a new one. * @param duration The duration (in ms) before the toast is automatically close. * Use `false` to never auto-close this toast. */ export function toast( type: "success" | "error" | "info", message: ReactNode, id?: string, duration?: number | false, ) { sonnerToast[type](message, { id, duration: duration !== false ? duration : Number.POSITIVE_INFINITY, }); } // Backoff numbers are in milliseconds. const INITIAL_BACKOFF = 500; const MAX_BACKOFF = 16000; export const backoffWithJitter = (numRetries: number) => { const baseBackoff = INITIAL_BACKOFF * 2 ** (numRetries - 1); const actualBackoff = Math.min(baseBackoff, MAX_BACKOFF); const jitter = actualBackoff * (Math.random() - 0.5); return actualBackoff + jitter; }; export function getReferencedTableName( tableMapping: Record<number, string> | undefined, possibleId: any, ): string | null { if (!tableMapping) { return null; } if (typeof possibleId !== "string") { return null; } let tableNumber; try { tableNumber = IdEncoding.decodeId(possibleId).tableNumber; } catch { return null; } return tableMapping[tableNumber] ?? null; } /** * System tables _file_storage and _scheduled_jobs have a different name * in user-facing contexts. */ export function getVisibleTableName(tableName: string) { if (tableName === "_file_storage") { return "_storage"; } if (tableName === "_scheduled_jobs") { return "_scheduled_functions"; } return tableName; } export function documentHref({ deploymentsURI, tableName, id, componentId, captureMessage, }: { deploymentsURI: string; tableName: string; id: string; componentId: string | null; captureMessage: (message: string, severity: "error") => void; }): { pathname: string; query: { [key: string]: string }; } { if (tableName === "_scheduled_jobs") { return { pathname: `${deploymentsURI}/schedules/functions`, query: { // FIXME: This could include query parameters one day to link to a specific job }, }; } if (tableName === "_file_storage") { return { pathname: `${deploymentsURI}/files`, query: { id }, }; } if (tableName.startsWith("_")) { captureMessage( `Linking to an unsupported system table: ${tableName}`, "error", ); } const filter: FilterExpression = { clauses: [ { id: "0", field: "_id", op: "eq", value: id, }, ], }; return { pathname: `${deploymentsURI}/data`, query: { table: tableName, filters: base64.encodeURI(JSON.stringify(filter)), ...(componentId ? { component: componentId } : {}), }, }; } export function formatUsd(usd: number) { return new Intl.NumberFormat(undefined, { style: "currency", currency: "USD", minimumFractionDigits: 0, }).format(usd); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/get-convex/convex-backend'

If you have feedback or need assistance with the MCP directory API, please join our Discord server