Skip to main content
Glama

Convex MCP server

Official
by get-convex
mod.rs3.39 kB
use std::{ collections::BTreeSet, sync::LazyLock, }; use common::{ auth::AuthInfo, document::{ ParseDocument, ParsedDocument, }, query::{ Order, Query, }, runtime::Runtime, }; use database::{ unauthorized_error, ResolvedQuery, SystemMetadataModel, Transaction, }; use value::{ TableName, TableNamespace, }; use self::types::AuthInfoPersisted; use crate::{ auth::types::AuthDiff, SystemIndex, SystemTable, }; pub mod types; pub static AUTH_TABLE: LazyLock<TableName> = LazyLock::new(|| "_auth".parse().expect("Invalid built-in auth table")); pub struct AuthTable; impl SystemTable for AuthTable { type Metadata = AuthInfoPersisted; fn table_name() -> &'static TableName { &AUTH_TABLE } fn indexes() -> Vec<SystemIndex<Self>> { vec![] } } pub struct AuthInfoModel<'a, RT: Runtime> { tx: &'a mut Transaction<RT>, } impl<'a, RT: Runtime> AuthInfoModel<'a, RT> { pub fn new(tx: &'a mut Transaction<RT>) -> Self { Self { tx } } #[fastrace::trace] pub async fn put(&mut self, auth_infos: Vec<AuthInfo>) -> anyhow::Result<AuthDiff> { if !(self.tx.identity().is_admin() || self.tx.identity().is_system()) { anyhow::bail!(unauthorized_error("put_auth_config")); } // Read out existing info first so we can diff it. let existing: Vec<ParsedDocument<AuthInfo>> = self.get_inner().await?; // Put the new data into a BTreeSet. let mut new_set = auth_infos.into_iter().collect::<BTreeSet<_>>(); let mut to_delete = vec![]; let mut removed_auth_infos: BTreeSet<AuthInfo> = BTreeSet::new(); // After this loop, `new_set` will contain only values that don't already exist // in the database, and `to_delete` will contain the ids of documents that need // to be deleted. for info in existing.into_iter() { if !new_set.remove(&info) { to_delete.push(info.id()); removed_auth_infos.insert(info.into_value()); } } // Make the changes. for id in to_delete { SystemMetadataModel::new_global(self.tx).delete(id).await?; } for info in new_set.clone().into_iter() { SystemMetadataModel::new_global(self.tx) .insert(&AUTH_TABLE, AuthInfoPersisted(info).try_into()?) .await?; } AuthDiff::new(new_set, removed_auth_infos) } pub async fn get(&mut self) -> anyhow::Result<Vec<ParsedDocument<AuthInfo>>> { if !(self.tx.identity().is_admin() || self.tx.identity().is_system()) { anyhow::bail!(unauthorized_error("get_auth_config")); } self.get_inner().await } async fn get_inner(&mut self) -> anyhow::Result<Vec<ParsedDocument<AuthInfo>>> { let auth_query = Query::full_table_scan(AUTH_TABLE.clone(), Order::Asc); let mut query_stream = ResolvedQuery::new(self.tx, TableNamespace::Global, auth_query)?; let mut auth_infos = vec![]; while let Some(auth_value) = query_stream.next(self.tx, None).await? { let parsed: ParsedDocument<AuthInfoPersisted> = auth_value.parse()?; auth_infos.push(parsed.map(|ai| Ok(ai.0))?); } Ok(auth_infos) } }

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