Skip to main content
Glama

Convex MCP server

Official
by get-convex
mod.rs5.5 kB
use std::{ cmp::Ordering, collections::BTreeMap, num::FpCategory, }; use anyhow::Context; use serde_json::{ json, Value as JsonValue, }; use crate::value::Value; mod bytes; mod float; mod integer; /// Is a floating point number native zero? fn is_negative_zero(n: f64) -> bool { matches!(n.total_cmp(&-0.0), Ordering::Equal) } impl From<Value> for JsonValue { fn from(value: Value) -> JsonValue { match value { Value::Null => JsonValue::Null, Value::Int64(n) => json!({ "$integer": integer::JsonInteger::encode(n) }), Value::Float64(n) => { let mut is_special = is_negative_zero(n); is_special |= match n.classify() { FpCategory::Zero | FpCategory::Normal | FpCategory::Subnormal => false, FpCategory::Infinite | FpCategory::Nan => true, }; if is_special { json!({ "$float": float::JsonFloat::encode(n) }) } else { json!(n) } }, Value::Boolean(b) => json!(b), Value::String(s) => json!(s), Value::Bytes(b) => json!({ "$bytes": bytes::JsonBytes::encode(&b) }), Value::Array(a) => JsonValue::from(a), Value::Object(o) => o.into_iter().collect(), } } } impl TryFrom<JsonValue> for Value { type Error = anyhow::Error; fn try_from(value: JsonValue) -> anyhow::Result<Self> { let r = match value { JsonValue::Null => Self::Null, JsonValue::Bool(b) => Self::from(b), JsonValue::Number(n) => { // TODO: JSON supports arbitrary precision numbers? let n = n .as_f64() .context("Arbitrary precision JSON integers unsupported")?; Value::from(n) }, JsonValue::String(s) => Self::from(s), JsonValue::Array(arr) => { let mut out = Vec::with_capacity(arr.len()); for a in arr { out.push(Value::try_from(a)?); } Value::Array(out) }, JsonValue::Object(map) => { if map.len() == 1 { let (key, value) = map.into_iter().next().unwrap(); match &key[..] { "$bytes" => { let i: String = serde_json::from_value(value)?; Self::Bytes(bytes::JsonBytes::decode(i)?) }, "$integer" => { let i: String = serde_json::from_value(value)?; Self::from(integer::JsonInteger::decode(i)?) }, "$float" => { let i: String = serde_json::from_value(value)?; let n = float::JsonFloat::decode(i)?; // Float64s encoded as a $float object must not fit into a regular // `number`. if !is_negative_zero(n) { if let FpCategory::Normal | FpCategory::Subnormal = n.classify() { anyhow::bail!("Float64 {} should be encoded as a number", n); } } Self::from(n) }, "$set" => { anyhow::bail!( "Received a Set which is no longer supported as a Convex type, \ with values: {value}" ); }, "$map" => { anyhow::bail!( "Received a Map which is no longer supported as a Convex type, \ with values: {value}" ); }, _ => { let mut fields = BTreeMap::new(); fields.insert(key, Self::try_from(value)?); Self::Object(fields) }, } } else { let mut fields = BTreeMap::new(); for (key, value) in map { fields.insert(key, Self::try_from(value)?); } Self::Object(fields) } }, }; Ok(r) } } #[cfg(test)] mod tests { use convex_sync_types::testing::assert_roundtrips; use proptest::prelude::*; use serde_json::Value as JsonValue; use crate::Value; proptest! { #![proptest_config( ProptestConfig { failure_persistence: None, ..ProptestConfig::default() } )] #[test] fn test_value_roundtrips(value in any::<Value>()) { assert_roundtrips::<Value, JsonValue>(value); } } #[test] fn test_value_roundtrips_trophies() { let trophies = vec![ Value::Float64(1.0), Value::Float64(f64::NAN), Value::Array(vec![Value::Float64(f64::NAN)]), ]; for trophy in trophies { assert_roundtrips::<Value, JsonValue>(trophy); } } }

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