Skip to main content
Glama

Convex MCP server

Official
by get-convex
internal.rs6.2 kB
use std::{ collections::BTreeMap, str::FromStr, }; use common::{ components::{ CanonicalizedComponentFunctionPath, ComponentId, ComponentPath, PublicFunctionPath, }, types::{ AllowedVisibility, MemberId, UdfType, }, version::Version, }; use keybroker::{ AdminIdentity, Identity, DEV_INSTANCE_NAME, }; use model::{ config::ConfigModel, source_packages::SourcePackageModel, udf_config::types::UdfConfig, }; use must_let::must_let; use runtime::testing::TestRuntime; use sync_types::CanonicalizedUdfPath; use udf::validation::ValidatedPathAndArgs; use value::{ ConvexArray, TableNamespace, }; use crate::test_helpers::UdfTest; #[convex_macro::test_runtime] async fn test_udf_visibility(rt: TestRuntime) -> anyhow::Result<()> { let t = UdfTest::default(rt).await?; let internal_function = CanonicalizedComponentFunctionPath { component: ComponentPath::test_user(), udf_path: CanonicalizedUdfPath::from_str("internal.js:myInternalMutation")?, }; let public_function = CanonicalizedComponentFunctionPath { component: ComponentPath::test_user(), udf_path: CanonicalizedUdfPath::from_str("internal.js:publicMutation")?, }; let non_existent_function = CanonicalizedComponentFunctionPath { component: ComponentPath::test_user(), udf_path: CanonicalizedUdfPath::from_str("internal.js:doesNotExist")?, }; let post_internal_npm_version = Version::parse("1.0.0").unwrap(); let mut tx = t.database.begin(Identity::system()).await?; let (config_metadata, module_configs, _udf_config) = ConfigModel::new(&mut tx, ComponentId::test_user()) .get_with_module_source(t.module_loader.as_ref()) .await?; let modules_by_path = module_configs .iter() .map(|c| (c.path.clone().canonicalize(), c.clone())) .collect(); let udf_config = UdfConfig::new_for_test(&t.rt, "1000.0.0".parse()?); let analyze_results = t .isolate .analyze( udf_config, modules_by_path, BTreeMap::new(), DEV_INSTANCE_NAME.to_string(), ) .await??; let source_package = SourcePackageModel::new(&mut tx, TableNamespace::test_user()) .get_latest() .await? .unwrap(); drop(tx); // Newer version + analyze results tx = t.database.begin(Identity::system()).await?; ConfigModel::new(&mut tx, ComponentId::test_user()) .apply( config_metadata.clone(), module_configs.clone(), UdfConfig::new_for_test(&t.rt, post_internal_npm_version.clone()), Some(source_package.into_value()), analyze_results.clone(), None, ) .await?; t.database.commit(tx).await?; tx = t.database.begin(Identity::Unknown(None)).await?; let result = ValidatedPathAndArgs::new( AllowedVisibility::PublicOnly, &mut tx, PublicFunctionPath::Component(internal_function.clone()), ConvexArray::empty(), UdfType::Mutation, ) .await; must_let!(let Ok(Err(js_error)) = result); assert!(js_error .message .starts_with("Could not find public function for 'internal:myInternalMutation'")); let result = ValidatedPathAndArgs::new( AllowedVisibility::PublicOnly, &mut tx, PublicFunctionPath::Component(public_function.clone()), ConvexArray::empty(), UdfType::Mutation, ) .await; must_let!(let Ok(Ok(_)) = result); // Error message should be the same so we don't leak information about which // internal functions exist let result = ValidatedPathAndArgs::new( AllowedVisibility::PublicOnly, &mut tx, PublicFunctionPath::Component(non_existent_function.clone()), ConvexArray::empty(), UdfType::Mutation, ) .await; must_let!(let Ok(Err(js_error)) = result); assert!(js_error .message .starts_with("Could not find public function for 'internal:doesNotExist'")); // Calling query as a mutation should fail let result = ValidatedPathAndArgs::new( AllowedVisibility::PublicOnly, &mut tx, PublicFunctionPath::Component(public_function.clone()), ConvexArray::empty(), UdfType::Query, ) .await; must_let!(let Ok(Err(js_error)) = result); assert_eq!( js_error.message, "Trying to execute internal.js:publicMutation as Query, but it is defined as Mutation." ); tx = t .database .begin(Identity::InstanceAdmin(AdminIdentity::new_for_test_only( "happy-animal-123".to_string(), MemberId(123), ))) .await?; // Admins should be allowed to call internal functions from public APIs let result = ValidatedPathAndArgs::new( AllowedVisibility::PublicOnly, &mut tx, PublicFunctionPath::Component(internal_function.clone()), ConvexArray::empty(), UdfType::Mutation, ) .await; must_let!(let Ok(Ok(_)) = result); // Calling a missing function should fail even as admin. let result = ValidatedPathAndArgs::new( AllowedVisibility::PublicOnly, &mut tx, PublicFunctionPath::Component(non_existent_function.clone()), ConvexArray::empty(), UdfType::Mutation, ) .await; must_let!(let Ok(Err(js_error)) = result); assert!(js_error .message .starts_with("Could not find public function for 'internal:doesNotExist'")); // Calling query as a mutation should fail even with admin. let result = ValidatedPathAndArgs::new( AllowedVisibility::PublicOnly, &mut tx, PublicFunctionPath::Component(public_function.clone()), ConvexArray::empty(), UdfType::Query, ) .await; must_let!(let Ok(Err(js_error)) = result); assert_eq!( js_error.message, "Trying to execute internal.js:publicMutation as Query, but it is defined as Mutation." ); Ok(()) }

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