Skip to main content
Glama

Convex MCP server

Official
by get-convex
index.ts5.89 kB
import { Expand, FunctionHandle, FunctionReference, GenericDataModel, GenericDatabaseWriter, GenericMutationCtx, QueryInitializer, TableNamesInDataModel, createFunctionHandle, } from "convex/server"; import { api } from "../triggers/_generated/api.js"; import { GenericId } from "convex/values"; import { TriggerArgs } from "../types.js"; import { AtomicMutators, atomicMutators } from "./atomicMutators.js"; export type { AtomicMutators }; export { atomicMutators }; export { triggerArgsValidator } from "../types.js"; type InternalizeApi<API> = Expand<{ [mod in keyof API]: API[mod] extends FunctionReference<any, any, any, any> ? FunctionReference< API[mod]["_type"], "internal", API[mod]["_args"], API[mod]["_returnType"], API[mod]["_componentPath"] > : InternalizeApi<API[mod]>; }>; type InstalledAPI = InternalizeApi<typeof api>; export type Triggers<DataModel extends GenericDataModel> = { [TableName in TableNamesInDataModel<DataModel>]?: { atomicMutators: AtomicMutators; triggers: FunctionReference< "mutation", any, TriggerArgs<DataModel, TableName>, null >[]; }; }; type TriggerHandles<DataModel extends GenericDataModel> = { [TableName in TableNamesInDataModel<DataModel>]?: { atomicMutators: { [Mutator in keyof AtomicMutators]: FunctionHandle<"mutation">; }; triggers: FunctionHandle< "mutation", TriggerArgs<DataModel, TableName>, null >[]; }; }; export type WithTriggers<DataModel extends GenericDataModel> = { args: Record<string, never>; input: ( ctx: GenericMutationCtx<DataModel>, args: any, ) => Promise<{ args: Record<string, never>; ctx: { db: WrapWriter<DataModel> }; }>; }; export function withTriggers<DataModel extends GenericDataModel>( api: InstalledAPI, triggers: Triggers<DataModel>, ): WithTriggers<DataModel> { return { args: {}, input: async (ctx: GenericMutationCtx<DataModel>, _args: any) => { const handles: TriggerHandles<DataModel> = {}; for (const tableNameStr of Object.keys(triggers)) { const tableName = tableNameStr as TableNamesInDataModel<DataModel>; const tableTrigger = triggers[tableName]!; handles[tableName] = { atomicMutators: { atomicInsert: await createFunctionHandle( tableTrigger.atomicMutators.atomicInsert, ), atomicPatch: await createFunctionHandle( tableTrigger.atomicMutators.atomicPatch, ), atomicReplace: await createFunctionHandle( tableTrigger.atomicMutators.atomicReplace, ), atomicDelete: await createFunctionHandle( tableTrigger.atomicMutators.atomicDelete, ), }, triggers: await Promise.all( tableTrigger.triggers.map(createFunctionHandle), ), }; } const db = new WrapWriter(ctx, api, handles); return { ctx: { db }, args: {}, }; }, }; } class WrapWriter<DataModel extends GenericDataModel> { ctx: GenericMutationCtx<DataModel>; system: GenericDatabaseWriter<DataModel>["system"]; api: InstalledAPI; triggers: TriggerHandles<DataModel>; constructor( ctx: GenericMutationCtx<DataModel>, api: InstalledAPI, triggers: TriggerHandles<DataModel>, ) { this.ctx = ctx; this.system = ctx.db.system; this.api = api; this.triggers = triggers; } normalizeId<TableName extends TableNamesInDataModel<DataModel>>( tableName: TableName, id: string, ): GenericId<TableName> | null { return this.ctx.db.normalizeId(tableName, id); } async insert<TableName extends string>( table: TableName, value: any, ): Promise<GenericId<TableName>> { if (table in this.triggers) { const tableTrigger = this.triggers[table]!; return await this.ctx.runMutation(this.api.documents.insert, { value, atomicInsert: tableTrigger.atomicMutators.atomicInsert, triggers: tableTrigger.triggers, }); } else { return await this.ctx.db.insert(table, value); } } async patch<TableName extends string>( table: TableName, id: GenericId<TableName>, value: Partial<any>, ): Promise<void> { if (table in this.triggers) { const tableTrigger = this.triggers[table]!; await this.ctx.runMutation(this.api.documents.patch, { id, value, atomicPatch: tableTrigger.atomicMutators.atomicPatch, triggers: tableTrigger.triggers, }); } else { await this.ctx.db.patch(id, value); } } async replace<TableName extends string>( table: TableName, id: GenericId<TableName>, value: any, ): Promise<void> { if (table in this.triggers) { const tableTrigger = this.triggers[table]!; await this.ctx.runMutation(this.api.documents.replace, { id, value, atomicReplace: tableTrigger.atomicMutators.atomicReplace, triggers: tableTrigger.triggers, }); } else { await this.ctx.db.replace(id, value); } } async delete<TableName extends string>( table: TableName, id: GenericId<TableName>, ): Promise<void> { if (table in this.triggers) { const tableTrigger = this.triggers[table]!; await this.ctx.runMutation(this.api.documents.deleteDoc, { id, atomicDelete: tableTrigger.atomicMutators.atomicDelete, triggers: tableTrigger.triggers, }); } else { await this.ctx.db.delete(id); } } get<TableName extends string>(id: GenericId<TableName>): Promise<any> { return this.ctx.db.get(id); } query<TableName extends string>(tableName: TableName): QueryInitializer<any> { return this.ctx.db.query(tableName); } }

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