Skip to main content
Glama

Convex MCP server

Official
by get-convex
environment.rs5.47 kB
use std::{ collections::BTreeMap, sync::{ Arc, LazyLock, }, time::Duration, }; use common::{ log_lines::LogLevel, runtime::{ JoinSet, Runtime, UnixTimestamp, }, types::EnvVarValue, value::NamespacedTableMapping, }; use deno_core::{ sourcemap::SourceMap, v8, }; use futures::{ future, FutureExt, }; use isolate::{ environment::{ crypto_rng::CryptoRng, AsyncOpRequest, IsolateEnvironment, ModuleCodeCacheResult, }, ConcurrencyPermit, Timeout, }; use model::modules::module_versions::FullModuleSource; use rand::{ Rng, SeedableRng, }; use rand_chacha::ChaCha12Rng; use runtime::testing::TestRuntime; use serde_json::Value as JsonValue; // NB: These files are generated by the *isolate* crate's build script. pub const TEST_SOURCE: &str = include_str!("../../../../../npm-packages/simulation/dist/main.js"); pub const TEST_SOURCE_MAP_STR: &str = include_str!("../../../../../npm-packages/simulation/dist/main.js.map"); pub static TEST_SOURCE_MAP: LazyLock<SourceMap> = LazyLock::new(|| { SourceMap::from_slice(TEST_SOURCE_MAP_STR.as_bytes()).expect("Invalid source map") }); pub struct TestEnvironment { rt: TestRuntime, rng: ChaCha12Rng, next_timer_id: usize, timers: JoinSet<usize>, timer_resolvers: BTreeMap<usize, v8::Global<v8::PromiseResolver>>, } impl TestEnvironment { pub fn new(rt: TestRuntime) -> Self { let rng = ChaCha12Rng::from_seed(rt.rng().random()); Self { rt, rng, next_timer_id: 0, timers: JoinSet::new(), timer_resolvers: BTreeMap::new(), } } } impl IsolateEnvironment<TestRuntime> for TestEnvironment { async fn lookup_source( &mut self, path: &str, _timeout: &mut Timeout<TestRuntime>, _permit: &mut Option<ConcurrencyPermit>, ) -> anyhow::Result<Option<(Arc<FullModuleSource>, ModuleCodeCacheResult)>> { if path != "test.js" { return Ok(None); } Ok(Some(( Arc::new(FullModuleSource { source: TEST_SOURCE.into(), source_map: Some(TEST_SOURCE_MAP_STR.to_string()), }), ModuleCodeCacheResult::noop(), ))) } fn syscall(&mut self, _name: &str, _args: JsonValue) -> anyhow::Result<JsonValue> { panic!("syscall() unimplemented"); } fn start_async_syscall( &mut self, name: String, args: JsonValue, _resolver: v8::Global<v8::PromiseResolver>, ) -> anyhow::Result<()> { tracing::info!("Ignoring async syscall: {name:?} {args:?}"); Ok(()) } fn trace(&mut self, level: LogLevel, messages: Vec<String>) -> anyhow::Result<()> { for message in messages { match level { LogLevel::Debug => tracing::debug!("[console] {message}"), LogLevel::Error => tracing::error!("[console] {message}"), LogLevel::Warn => tracing::warn!("[console] {message}"), LogLevel::Info => tracing::info!("[console] {message}"), LogLevel::Log => tracing::info!("[console] {message}"), } } Ok(()) } fn rng(&mut self) -> anyhow::Result<&mut ChaCha12Rng> { Ok(&mut self.rng) } fn crypto_rng(&mut self) -> anyhow::Result<CryptoRng> { anyhow::bail!("CryptoRng not allowed in simulation") } fn unix_timestamp(&mut self) -> anyhow::Result<UnixTimestamp> { Ok(self.rt.unix_timestamp()) } fn get_environment_variable( &mut self, _name: common::types::EnvVarName, ) -> anyhow::Result<Option<EnvVarValue>> { Ok(None) } fn get_all_table_mappings(&mut self) -> anyhow::Result<NamespacedTableMapping> { panic!("get_all_table_mappings() unimplemented"); } fn start_async_op( &mut self, request: AsyncOpRequest, resolver: v8::Global<v8::PromiseResolver>, ) -> anyhow::Result<()> { match request { AsyncOpRequest::Sleep { until, .. } => { let id = self.next_timer_id; self.next_timer_id += 1; let now = self.rt.unix_timestamp(); let duration = if until > now { until - now } else { Duration::ZERO }; self.timers .spawn("timer", tokio::time::sleep(duration).map(move |_| id)); self.timer_resolvers.insert(id, resolver); }, req => { tracing::debug!("Ignoring async op request: {req:?}"); }, } Ok(()) } fn user_timeout(&self) -> Duration { Duration::from_secs(60 * 60 * 24) } fn system_timeout(&self) -> Duration { Duration::from_secs(60 * 60 * 24) } } impl TestEnvironment { pub async fn next_timer(&mut self) -> anyhow::Result<v8::Global<v8::PromiseResolver>> { let Some(timer) = self.timers.join_next().await else { return future::pending().await; }; let timer_id = timer?; let resolver = self .timer_resolvers .remove(&timer_id) .ok_or_else(|| anyhow::anyhow!("Timer resolver not found"))?; Ok(resolver) } }

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