Skip to main content
Glama

Convex MCP server

Official
by get-convex
database_impl.ts6.2 kB
import { convexToJson, GenericId, jsonToConvex, Value, } from "../../values/index.js"; import { performAsyncSyscall, performSyscall } from "./syscall.js"; import { GenericDatabaseReader, GenericDatabaseReaderWithTable, GenericDatabaseWriter, GenericDatabaseWriterWithTable, } from "../database.js"; import { QueryInitializerImpl } from "./query_impl.js"; import { GenericDataModel, GenericDocument } from "../data_model.js"; import { validateArg } from "./validate.js"; import { version } from "../../index.js"; import { patchValueToJson } from "../../values/value.js"; async function get( table: string | undefined, id: GenericId<string>, isSystem: boolean, ) { // If the user doesn’t provide any arguments, we use the new signature in the error message. // We don’t do argument validation on the table argument since it’s not provided when using the old signature. validateArg(id, 1, "get", "id"); if (typeof id !== "string") { throw new Error( `Invalid argument \`id\` for \`db.get\`, expected string but got '${typeof id}': ${ id as any }`, ); } const args = { id: convexToJson(id), isSystem, version, table, }; const syscallJSON = await performAsyncSyscall("1.0/get", args); return jsonToConvex(syscallJSON) as GenericDocument; } export function setupReader(): GenericDatabaseReader<GenericDataModel> { const reader = ( isSystem = false, ): GenericDatabaseReader<GenericDataModel> & GenericDatabaseReaderWithTable<GenericDataModel> => { return { get: async (arg0: any, arg1?: any) => { return arg1 !== undefined ? await get(arg0, arg1, isSystem) : await get(undefined, arg0, isSystem); }, query: (tableName: string) => { return new TableReader(tableName, isSystem).query(); }, normalizeId: <TableName extends string>( tableName: TableName, id: string, ): GenericId<TableName> | null => { validateArg(tableName, 1, "normalizeId", "tableName"); validateArg(id, 2, "normalizeId", "id"); const accessingSystemTable = tableName.startsWith("_"); if (accessingSystemTable !== isSystem) { throw new Error( `${ accessingSystemTable ? "System" : "User" } tables can only be accessed from db.${ isSystem ? "" : "system." }normalizeId().`, ); } const syscallJSON = performSyscall("1.0/db/normalizeId", { table: tableName, idString: id, }); const syscallResult = jsonToConvex(syscallJSON) as any; return syscallResult.id; }, // We set the system reader on the next line system: null as any, table: (tableName) => { return new TableReader(tableName, isSystem); }, }; }; const { system: _, ...rest } = reader(true); const r = reader(); r.system = rest as any; return r; } async function insert(tableName: string, value: any) { if (tableName.startsWith("_")) { throw new Error("System tables (prefixed with `_`) are read-only."); } validateArg(tableName, 1, "insert", "table"); validateArg(value, 2, "insert", "value"); const syscallJSON = await performAsyncSyscall("1.0/insert", { table: tableName, value: convexToJson(value), }); const syscallResult = jsonToConvex(syscallJSON) as any; return syscallResult._id; } async function patch(table: string | undefined, id: any, value: any) { validateArg(id, 1, "patch", "id"); validateArg(value, 2, "patch", "value"); await performAsyncSyscall("1.0/shallowMerge", { id: convexToJson(id), value: patchValueToJson(value as Value), table, }); } async function replace(table: string | undefined, id: any, value: any) { validateArg(id, 1, "replace", "id"); validateArg(value, 2, "replace", "value"); await performAsyncSyscall("1.0/replace", { id: convexToJson(id), value: convexToJson(value), table, }); } async function delete_(table: string | undefined, id: any) { validateArg(id, 1, "delete", "id"); await performAsyncSyscall("1.0/remove", { id: convexToJson(id), table, }); } export function setupWriter(): GenericDatabaseWriter<GenericDataModel> & GenericDatabaseWriterWithTable<GenericDataModel> { const reader = setupReader(); return { get: reader.get, query: reader.query, normalizeId: reader.normalizeId, system: reader.system as any, insert: async (table, value) => { return await insert(table, value); }, patch: async (arg0: any, arg1: any, arg2?: any) => { return arg2 !== undefined ? await patch(arg0, arg1, arg2) : await patch(undefined, arg0, arg1); }, replace: async (arg0: any, arg1: any, arg2?: any) => { return arg2 !== undefined ? await replace(arg0, arg1, arg2) : await replace(undefined, arg0, arg1); }, delete: async (arg0: any, arg1?: any) => { return arg1 !== undefined ? await delete_(arg0, arg1) : await delete_(undefined, arg0); }, table: (tableName) => { return new TableWriter(tableName, false); }, }; } class TableReader { constructor( protected readonly tableName: string, protected readonly isSystem: boolean, ) {} async get(id: GenericId<string>) { return get(this.tableName, id, this.isSystem); } query() { const accessingSystemTable = this.tableName.startsWith("_"); if (accessingSystemTable !== this.isSystem) { throw new Error( `${ accessingSystemTable ? "System" : "User" } tables can only be accessed from db.${ this.isSystem ? "" : "system." }query().`, ); } return new QueryInitializerImpl(this.tableName); } } class TableWriter extends TableReader { async insert(value: any) { return insert(this.tableName, value); } async patch(id: any, value: any) { return patch(this.tableName, id, value); } async replace(id: any, value: any) { return replace(this.tableName, id, value); } async delete(id: any) { return delete_(this.tableName, id); } }

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