Skip to main content
Glama

Convex MCP server

Official
by get-convex
lib.rs15.5 kB
#![feature(type_alias_impl_trait)] #![feature(iterator_try_collect)] #![feature(impl_trait_in_assoc_type)] #![feature(try_blocks)] use std::{ collections::{ BTreeMap, BTreeSet, }, ops::Deref, }; use flexbuffers::{ Blob, Buffer, Builder, FlexBufferType, MapReader, Reader, VectorReader, }; use value::{ heap_size::HeapSize, serde::ConvexSerializable, ConvexObject, ConvexValue, FieldPath, }; mod buffer; mod debug; mod flexbuilder; mod json; mod walk; #[cfg(test)] mod tests; pub use self::buffer::{ ByteBuffer, StringBuffer, }; use self::flexbuilder::FlexBuilder; #[cfg_attr(any(test, feature = "testing"), derive(PartialEq))] pub struct PackedValue<B: Buffer> where B::BufferString: Clone, { buf: B, } impl<B: Buffer> Clone for PackedValue<B> where B::BufferString: Clone, { fn clone(&self) -> Self { Self { buf: self.buf.shallow_copy(), } } } impl<B: Buffer> PackedValue<B> where B::BufferString: Clone, { pub fn new(buf: B) -> Self { Self { buf } } pub fn open(self) -> anyhow::Result<OpenedValue<B>> { OpenedValue::new(Reader::get_root(self.buf)?) } pub fn parse<T: ConvexSerializable>(self) -> anyhow::Result<T> { value::serde::from_value::<_, T::Serialized>(self.as_ref().open()?)? .try_into() .map_err(Into::<anyhow::Error>::into) } pub fn size(&self) -> usize { self.buf.len() } /// Get a shared reference to the PackedValue, so it can be opened multiple /// times without cloning the underlying buffer. pub fn as_ref(&self) -> PackedValue<&[u8]> { PackedValue::new(&self.buf) } pub fn open_path(self, field_path: &FieldPath) -> Option<OpenedValue<B>> { field_path.fields().first()?; // return None if empty path let mut v = self.open().expect("failed to open packed value"); for field in field_path.fields().iter() { match v { OpenedValue::Object(o) => { v = o.get(field).expect("failed to open packed object")?; }, _ => return None, } } Some(v) } /// Same behavior as Value::get_path but doesn't fully unpack. pub fn get_path(&self, field_path: &FieldPath) -> Option<ConvexValue> { self.as_ref() .open_path(field_path) .map(ConvexValue::try_from) .transpose() .expect("failed to unpack opened value") } } impl<B: Buffer> HeapSize for PackedValue<B> where B::BufferString: Clone, { fn heap_size(&self) -> usize { self.buf.len() } } impl PackedValue<ByteBuffer> { pub fn pack(value: &ConvexValue) -> Self { let mut builder = Builder::default(); Self::_pack(value, &mut builder); Self { buf: builder.take_buffer().into(), } } pub fn pack_object(value: &ConvexObject) -> Self { let mut builder = Builder::default(); Self::_pack_object(value, &mut builder); Self { buf: builder.take_buffer().into(), } } fn _pack(value: &ConvexValue, builder: &mut impl FlexBuilder) { match value { ConvexValue::Null => { builder.push(()); }, ConvexValue::Int64(i) => { builder.push(*i); }, ConvexValue::Float64(f) => { builder.push(*f); }, ConvexValue::Boolean(b) => { builder.push(*b); }, ConvexValue::String(s) => { builder.push(&s[..]); }, ConvexValue::Bytes(b) => { builder.push(Blob(&b[..])); }, ConvexValue::Array(ref values) => { let mut vector = builder.start_vector(); for value in values { Self::_pack(value, &mut vector); } vector.end_vector(); }, ConvexValue::Set(ref values) => { let mut map = builder.start_map(); let mut vector = map.start_vector("$set"); for value in values { Self::_pack(value, &mut vector); } vector.end_vector(); map.end_map(); }, ConvexValue::Map(ref values) => { let mut map = builder.start_map(); let mut vector = map.start_vector("$map"); for (key, value) in values { Self::_pack(key, &mut vector); Self::_pack(value, &mut vector); } vector.end_vector(); map.end_map(); }, ConvexValue::Object(ref fields) => { Self::_pack_object(fields, builder); }, } } fn _pack_object(object: &ConvexObject, builder: &mut impl FlexBuilder) { let mut map = builder.start_map(); for (field, value) in object.iter() { let mut builder = (&field[..], &mut map); Self::_pack(value, &mut builder); } map.end_map(); } } pub enum OpenedValue<B: Buffer = ByteBuffer> where B::BufferString: Clone, { Null, Int64(i64), Float64(f64), Boolean(bool), String(OpenedString<B>), Bytes(OpenedBytes<B>), Array(OpenedArray<B>), Set(OpenedSet<B>), Map(OpenedMap<B>), Object(OpenedObject<B>), } impl<B: Buffer> Clone for OpenedValue<B> where B::BufferString: Clone, { fn clone(&self) -> Self { match self { OpenedValue::Null => OpenedValue::Null, OpenedValue::Int64(i) => OpenedValue::Int64(*i), OpenedValue::Float64(f) => OpenedValue::Float64(*f), OpenedValue::Boolean(b) => OpenedValue::Boolean(*b), OpenedValue::String(ref s) => OpenedValue::String(s.clone()), OpenedValue::Bytes(ref b) => OpenedValue::Bytes(b.clone()), OpenedValue::Array(ref a) => OpenedValue::Array(a.clone()), OpenedValue::Set(ref s) => OpenedValue::Set(s.clone()), OpenedValue::Map(ref m) => OpenedValue::Map(m.clone()), OpenedValue::Object(ref o) => OpenedValue::Object(o.clone()), } } } impl<B: Buffer> OpenedValue<B> where B::BufferString: Clone, { fn new(reader: Reader<B>) -> anyhow::Result<Self> { let result = match reader.flexbuffer_type() { FlexBufferType::Null => OpenedValue::Null, FlexBufferType::Int | FlexBufferType::IndirectInt => { OpenedValue::Int64(reader.get_i64()?) }, FlexBufferType::Float | FlexBufferType::IndirectFloat => { OpenedValue::Float64(reader.get_f64()?) }, FlexBufferType::Bool => OpenedValue::Boolean(reader.get_bool()?), FlexBufferType::String => OpenedValue::String(OpenedString { buf: reader.get_str()?, }), FlexBufferType::Blob => OpenedValue::Bytes(OpenedBytes { buf: reader.get_blob()?.0, }), FlexBufferType::Map => { let reader = reader.get_map()?; if let Some(ix) = reader.index_key("$set") { anyhow::ensure!(reader.len() == 1); let reader = reader.index(ix)?.get_vector()?; OpenedValue::Set(OpenedSet { reader }) } else if let Some(ix) = reader.index_key("$map") { anyhow::ensure!(reader.len() == 1); let reader = reader.index(ix)?.get_vector()?; anyhow::ensure!(reader.len() % 2 == 0); OpenedValue::Map(OpenedMap { reader }) } else { OpenedValue::Object(OpenedObject { reader }) } }, // NB: Maps also satisfy `is_vector`, so be sure to check those first above. ty if ty.is_vector() => OpenedValue::Array(OpenedArray { reader: reader.get_vector()?, }), ty => anyhow::bail!("Unexpected buffer type: {ty:?}"), }; Ok(result) } } pub struct OpenedString<B: Buffer> where B::BufferString: Clone, { buf: B::BufferString, } impl<B: Buffer> Clone for OpenedString<B> where B::BufferString: Clone, { fn clone(&self) -> Self { Self { buf: self.buf.clone(), } } } impl<B: Buffer> Deref for OpenedString<B> where B::BufferString: Clone, { type Target = str; fn deref(&self) -> &str { &self.buf[..] } } pub struct OpenedBytes<B: Buffer> where B::BufferString: Clone, { buf: B, } impl<B: Buffer> Clone for OpenedBytes<B> where B::BufferString: Clone, { fn clone(&self) -> Self { Self { buf: self.buf.shallow_copy(), } } } impl<B: Buffer> Deref for OpenedBytes<B> where B::BufferString: Clone, { type Target = [u8]; fn deref(&self) -> &[u8] { &self.buf[..] } } pub struct OpenedArray<B: Buffer> where B::BufferString: Clone, { reader: VectorReader<B>, } impl<B: Buffer> Clone for OpenedArray<B> where B::BufferString: Clone, { fn clone(&self) -> Self { Self { reader: self.reader.clone(), } } } impl<B: Buffer> OpenedArray<B> where B::BufferString: Clone, { pub fn len(&self) -> usize { self.reader.len() } pub fn is_empty(&self) -> bool { self.reader.is_empty() } pub fn index(&self, i: usize) -> anyhow::Result<OpenedValue<B>> { OpenedValue::new(self.reader.index(i)?) } pub fn iter(&self) -> impl Iterator<Item = anyhow::Result<OpenedValue<B>>> + '_ { (0..self.reader.len()).map(|i| OpenedValue::new(self.reader.index(i)?)) } } pub struct OpenedSet<B: Buffer> where B::BufferString: Clone, { // Invariant: items are written in ascending `Value` order. reader: VectorReader<B>, } impl<B: Buffer> Clone for OpenedSet<B> where B::BufferString: Clone, { fn clone(&self) -> Self { Self { reader: self.reader.clone(), } } } impl<B: Buffer> OpenedSet<B> where B::BufferString: Clone, { pub fn len(&self) -> usize { self.reader.len() } pub fn is_empty(&self) -> bool { self.reader.is_empty() } pub fn iter(&self) -> impl Iterator<Item = anyhow::Result<OpenedValue<B>>> + '_ { (0..self.reader.len()).map(|i| OpenedValue::new(self.reader.index(i)?)) } } pub struct OpenedMap<B: Buffer> where B::BufferString: Clone, { // Invariant: alternate keys and values are written in ascending `Value` key order. reader: VectorReader<B>, } impl<B: Buffer> Clone for OpenedMap<B> where B::BufferString: Clone, { fn clone(&self) -> Self { Self { reader: self.reader.clone(), } } } impl<B: Buffer> OpenedMap<B> where B::BufferString: Clone, { pub fn len(&self) -> usize { self.reader.len() / 2 } pub fn is_empty(&self) -> bool { self.reader.is_empty() } pub fn iter( &self, ) -> impl Iterator<Item = anyhow::Result<(OpenedValue<B>, OpenedValue<B>)>> + '_ { assert_eq!(self.reader.len() % 2, 0); (0..(self.reader.len() / 2)).map(move |i| { let key = OpenedValue::new(self.reader.index(2 * i)?)?; let value = OpenedValue::new(self.reader.index(2 * i + 1)?)?; Ok((key, value)) }) } } pub struct OpenedObject<B: Buffer> where B::BufferString: Clone, { reader: MapReader<B>, } impl<B: Buffer> Clone for OpenedObject<B> where B::BufferString: Clone, { fn clone(&self) -> Self { Self { reader: self.reader.clone(), } } } impl<B: Buffer> OpenedObject<B> where B::BufferString: Clone, { pub fn len(&self) -> usize { self.reader.len() } pub fn is_empty(&self) -> bool { self.reader.is_empty() } pub fn get(&self, field: &str) -> anyhow::Result<Option<OpenedValue<B>>> { let Some(i) = self.reader.index_key(field) else { return Ok(None); }; let reader = self.reader.index(i)?; Ok(Some(OpenedValue::new(reader)?)) } pub fn iter(&self) -> impl Iterator<Item = anyhow::Result<(B::BufferString, OpenedValue<B>)>> { self.reader .iter_keys() .zip(self.reader.iter_values()) .map(|(key, value)| Ok((key, OpenedValue::new(value)?))) } } impl<B: Buffer> TryFrom<PackedValue<B>> for ConvexValue where B::BufferString: Clone, { type Error = anyhow::Error; fn try_from(value: PackedValue<B>) -> anyhow::Result<Self> { value.open()?.try_into() } } impl<B: Buffer> TryFrom<OpenedValue<B>> for ConvexValue where B::BufferString: Clone, { type Error = anyhow::Error; fn try_from(value: OpenedValue<B>) -> anyhow::Result<Self> { let result = match value { OpenedValue::Null => Self::Null, OpenedValue::Int64(i) => Self::from(i), OpenedValue::Float64(f) => Self::from(f), OpenedValue::Boolean(b) => Self::from(b), OpenedValue::String(s) => Self::try_from(s[..].to_owned())?, OpenedValue::Bytes(b) => Self::try_from(b[..].to_owned())?, OpenedValue::Array(packed_values) => { let values = packed_values .iter() .map(|r| Self::try_from(r?)) .collect::<anyhow::Result<Vec<_>>>()?; Self::Array(values.try_into()?) }, OpenedValue::Set(packed_values) => { let values = packed_values .iter() .map(|r| Self::try_from(r?)) .collect::<anyhow::Result<BTreeSet<_>>>()?; Self::Set(values.try_into()?) }, OpenedValue::Map(packed_values) => { let values = packed_values .iter() .map(|r| { let (k, v) = r?; Ok((Self::try_from(k)?, Self::try_from(v)?)) }) .collect::<anyhow::Result<BTreeMap<_, _>>>()?; Self::Map(values.try_into()?) }, OpenedValue::Object(packed_values) => { let values = packed_values .iter() .map(|r| { let (k, v) = r?; Ok((k.parse()?, Self::try_from(v)?)) }) .collect::<anyhow::Result<BTreeMap<_, _>>>()?; Self::Object(values.try_into()?) }, }; Ok(result) } } #[cfg(any(test, feature = "testing"))] mod proptest { use proptest::prelude::*; use value::ConvexValue; use super::{ buffer::ByteBuffer, PackedValue, }; impl Arbitrary for PackedValue<ByteBuffer> { type Parameters = (); type Strategy = impl Strategy<Value = PackedValue<ByteBuffer>>; fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { any::<ConvexValue>().prop_map(|v| PackedValue::pack(&v)) } } }

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