Skip to main content
Glama

Convex MCP server

Official
by get-convex
schema.rs26.5 kB
use std::sync::LazyLock; use errors::ErrorMetadata; use itertools::Itertools; use json_trait::JsonForm as _; use serde_json::{ json, Value as JsonValue, }; use crate::{ bootstrap_model::index::{ MAX_INDEX_FIELDS_SIZE, MAX_TEXT_INDEX_FILTER_FIELDS_SIZE, MAX_VECTOR_INDEX_FILTER_FIELDS_SIZE, }, schemas::{ DatabaseSchema, MAX_INDEXES_PER_TABLE, }, }; static TOO_MANY_INDEXES: LazyLock<Vec<JsonValue>> = LazyLock::new(|| { (0..MAX_INDEXES_PER_TABLE + 1) .map(|i| { json!({ "indexDescriptor": format!("index{i}"), "fields": ["x"], }) }) .collect() }); static TOO_MANY_INDEX_FIELDS: LazyLock<Vec<String>> = LazyLock::new(|| { (0..MAX_INDEX_FIELDS_SIZE + 1) .map(|i| format!("field_{i}")) .collect() }); fn index_validation_test(schema_value: JsonValue) -> ErrorMetadata { let e = DatabaseSchema::json_deserialize_value(schema_value) .expect_err("Successfully created invalid schema"); e.downcast::<ErrorMetadata>() .unwrap_or_else(|e| panic!("Error <{e}> is not an ErrorMetadata")) } #[test] fn test_empty_index() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [ { "indexDescriptor": "by_email", "fields": [], } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "EmptyIndex", "<{err}> does not match expected error type" ); } #[test] fn test_index_fields_not_unique() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [ { "indexDescriptor": "by_email", "fields": ["email", "email"], }, ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "FieldsNotUniqueWithinIndex", "<{err}> does not match expected error type" ); // Test the full string since there's some complex interpolation involved assert_eq!( err.msg, "In table \"test\": In index \"by_email\": Duplicate field \"email\". Index fields must \ be unique within an index.", "<{err}> does not match expected error string" ); } #[test] fn test_search_index_fields_not_unique() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "searchIndexes": [ { "indexDescriptor": "firstIndex", "searchField": "text", "filterFields": [] }, { "indexDescriptor": "secondIndex", "searchField": "text", "filterFields": [] } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "SearchIndexFieldNotUnique", "<{err}> does not match expected error type" ); } #[test] fn test_search_index_fields_not_unique_but_filter_fields_are_unique_fails() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "searchIndexes": [ { "indexDescriptor": "firstIndex", "searchField": "text", "filterFields": ["first"] }, { "indexDescriptor": "secondIndex", "searchField": "text", "filterFields": ["second"] } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "SearchIndexFieldNotUnique", "<{err}> does not match expected error type" ); } #[test] fn test_vector_indexes_not_unique() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "vectorIndexes": [ { "indexDescriptor": "firstIndex", "vectorField": "fieldName", "filterFields": [], "dimensions": 1536, }, { "indexDescriptor": "secondIndex", "vectorField": "fieldName", "filterFields": [], "dimensions": 1536, } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "VectorIndexFieldNotUnique", "<{err}> does not match expected error type" ); } #[test] fn test_vector_fields_and_dimensions_not_unique_but_filter_fields_are_unique_fails() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "vectorIndexes": [ { "indexDescriptor": "firstIndex", "vectorField": "fieldName", "filterFields": ["first"], "dimensions": 1536, }, { "indexDescriptor": "secondIndex", "vectorField": "fieldName", "filterFields": ["second"], "dimensions": 1536, } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "VectorIndexFieldNotUnique", "<{err}> does not match expected error type" ); } #[test] fn test_vector_indexes_same_fields_different_dimensions_are_valid() -> anyhow::Result<()> { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "vectorIndexes": [ { "indexDescriptor": "firstIndex", "vectorField": "fieldName", "filterFields": [], "dimensions": 1536, }, { "indexDescriptor": "secondIndex", "vectorField": "fieldName", "filterFields": [], "dimensions": 3, } ], }, ], "schemaValidation": true, }); DatabaseSchema::json_deserialize_value(value)?; Ok(()) } #[test] fn test_index_fields_contain_id() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [ { "indexDescriptor": "my_index", "fields": ["_id"], }, ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "IndexFieldsContainId", "<{err}> does not match expected error type" ); } #[test] fn test_index_paths_not_unique() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [ { "indexDescriptor": "by_email", "fields": ["email"], }, { "indexDescriptor": "by_email2", "fields": ["email"], } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "IndexNotUnique", "<{err}> does not match expected error type" ); } #[test] fn test_index_names_reserved() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [ { "indexDescriptor": "by_id", "fields": ["id"], } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "IndexNameReserved", "<{err}> does not match expected error type" ); } #[test] fn test_search_index_name_reserved() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "searchIndexes": [ { "indexDescriptor": "by_id", "searchField": "text", "filterFields": [] } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "IndexNameReserved", "<{err}> does not match expected error type" ); } #[test] fn test_index_names_not_unique() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [ { "indexDescriptor": "by_email", "fields": ["email"], }, { "indexDescriptor": "by_email", "fields": ["email2"], } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "IndexNamesNotUnique", "<{err}> does not match expected error type" ); } #[test] fn test_search_index_names_not_unique() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "searchIndexes": [ { "indexDescriptor": "index_name", "searchField": "field1", "filterFields": [] }, { "indexDescriptor": "index_name", "searchField": "field2", "filterFields": [] } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "IndexNamesNotUnique", "<{err}> does not match expected error type" ); } #[test] fn test_vector_index_names_not_unique() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "vectorIndexes": [ { "indexDescriptor": "index_name", "vectorField": "fieldName", "filterFields": [], "dimensions": 1536, }, { "indexDescriptor": "index_name", "vectorField": "fieldName2", "filterFields": [], "dimensions": 1536, }, ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "IndexNamesNotUnique", "<{err}> does not match expected error type" ); } #[test] fn test_reused_index_name_between_database_and_search_indexes() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [ { "indexDescriptor": "index_name", "fields": ["field1"], } ], "searchIndexes": [ { "indexDescriptor": "index_name", "searchField": "field1", "filterFields": [] }, ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "IndexNamesNotUnique", "<{err}> does not match expected error type" ); } #[test] fn test_reused_index_name_between_database_and_vector_indexes() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [ { "indexDescriptor": "index_name", "fields": ["field1"], } ], "vectorIndexes": [ { "indexDescriptor": "index_name", "vectorField": "fieldName", "filterFields": [], "dimensions": 1536, } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "IndexNamesNotUnique", "<{err}> does not match expected error type" ); } #[test] fn test_reused_index_name_between_search_and_vector_indexes() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "searchIndexes": [ { "indexDescriptor": "index_name", "searchField": "field1", "filterFields": [] }, ], "vectorIndexes": [ { "indexDescriptor": "index_name", "vectorField": "fieldName", "filterFields": [], "dimensions": 1536, } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "IndexNamesNotUnique", "<{err}> does not match expected error type" ); } #[test] fn test_invalid_index_descriptor() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [ { "indexDescriptor": "_", "fields": ["email"], } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "InvalidIndexName", "<{err}> does not match expected error type" ); } #[test] fn test_invalid_search_index_descriptor() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "searchIndexes": [ { "indexDescriptor": "_", "searchField": "field1", "filterFields": [] }, ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "InvalidIndexName", "<{err}> does not match expected error type" ); } #[test] fn test_invalid_vector_index_descriptor() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "vectorIndexes": [ { "indexDescriptor": "_", "vectorField": "fieldName", "filterFields": [], "dimensions": 1536, } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "InvalidIndexName", "<{err}> does not match expected error type" ); } #[test] fn test_invalid_field_name() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [ { "indexDescriptor": "by_invalid", "fields": ["_"], } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "InvalidIndexField", "<{err}> does not match expected error type" ); } #[test] fn test_invalid_search_field_name() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "searchIndexes": [ { "indexDescriptor": "index_name", "searchField": "_", "filterFields": [] }, ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "InvalidIndexField", "<{err}> does not match expected error type" ); } #[test] fn test_invalid_search_filter_field_name() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "searchIndexes": [ { "indexDescriptor": "index_name", "searchField": "field", "filterFields": ["_"] }, ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "InvalidIndexField", "<{err}> does not match expected error type" ); } #[test] fn test_invalid_vector_field_name() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "vectorIndexes": [ { "indexDescriptor": "index_name", "vectorField": "_", "filterFields": [], "dimensions": 1536, } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "InvalidIndexField", "<{err}> does not match expected error type" ); } #[test] fn test_invalid_vector_filter_field_name() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "vectorIndexes": [ { "indexDescriptor": "index_name", "vectorField": "fieldName", "filterFields": ["_"], "dimensions": 1536, } ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "InvalidIndexField", "<{err}> does not match expected error type" ); } #[test] fn test_invalid_json() { let value = json!({ "tables": "blah" }); let err = index_validation_test(value); assert_eq!( err.short_msg, "InvalidJson", "<{err}> does not match expected error type" ); } #[test] fn test_invalid_table_name() { let value = json!({ "tables": [ { "tableName": "_", "indexes": [], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "InvalidTableName", "<{err}> does not match expected error type" ); } #[test] fn test_too_many_fields() { let value = json!({ "tables": [ { "tableName": "test", "indexes": [ { "indexDescriptor": "by_toomany", "fields": *TOO_MANY_INDEX_FIELDS, }, ], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "IndexTooManyFields", "<{err}> does not match expected error type" ); } #[test] fn test_too_many_search_filter_fields() { let fields: Vec<_> = (0..MAX_TEXT_INDEX_FILTER_FIELDS_SIZE + 1) .map(|i| format!("field_{i}")) .collect(); let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "searchIndexes": [{ "indexDescriptor": "search_index", "searchField": "fieldName", "filterFields": fields }] }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "IndexTooManyFilterFields", "<{err}> does not match expected error type" ); } #[test] fn test_too_many_indexes() { let value = json!({ "tables": [ { "tableName": "test", "indexes": *TOO_MANY_INDEXES, }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "TooManyIndexes", "<{err}> does not match expected error type" ); } #[test] fn test_too_many_indexes_across_types() -> anyhow::Result<()> { let num_indexes_per_type = (MAX_INDEXES_PER_TABLE / 3) + 1; let indexes: Vec<JsonValue> = (0..num_indexes_per_type) .map(|i| { json!({ "indexDescriptor": format!("index{i}"), "fields": [format!("field{i}")] }) }) .collect_vec(); let text_indexes = (0..num_indexes_per_type) .map(|i| { json!({ "indexDescriptor": format!("search_index{}", i), "searchField": format!("fieldName{}", i), "filterFields": [], }) }) .collect_vec(); let vector_indexes = (0..num_indexes_per_type) .map(|i| { json!({ "indexDescriptor": format!("vector_index{}", i), "vectorField": format!("fieldName{}", i), "filterFields": [], "dimensions": 1536, }) }) .collect_vec(); let value = json!({ "tables": [ { "tableName": "test", "indexes": indexes, "searchIndexes": text_indexes, "vectorIndexes": vector_indexes, }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "TooManyIndexes", "<{err}> does not match expected error type" ); Ok(()) } #[test] fn test_many_search_indexes() -> anyhow::Result<()> { let indexes: Vec<_> = (0..7) .map(|i| { json!({ "indexDescriptor": format!("index{}", i), "searchField": format!("fieldName{}", i), "filterFields": [], }) }) .collect(); let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "searchIndexes": indexes, }, ], "schemaValidation": true, }); DatabaseSchema::json_deserialize_value(value)?; Ok(()) } #[test] fn test_many_vector_indexes() -> anyhow::Result<()> { let indexes: Vec<_> = (0..5) .map(|i| { json!({ "indexDescriptor": format!("index{}", i), "vectorField": format!("fieldName{}", i), "filterFields": [], "dimensions": 1536, }) }) .collect(); let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "vectorIndexes": indexes, }, ], "schemaValidation": true, }); DatabaseSchema::json_deserialize_value(value)?; Ok(()) } #[test] fn test_too_many_vector_filter_fields() { let fields: Vec<_> = (0..MAX_VECTOR_INDEX_FILTER_FIELDS_SIZE + 1) .map(|i| format!("field_{i}")) .collect(); let value = json!({ "tables": [ { "tableName": "test", "indexes": [], "vectorIndexes": [{ "indexDescriptor": "indexName", "vectorField": "fieldName", "filterFields": fields, "dimensions": 1536, }] }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "IndexTooManyFilterFields", "<{err}> does not match expected error type" ); } #[test] fn test_table_name_reserved() { let value = json!({ "tables": [ { "tableName": "_reserved", "indexes": [], }, ], "schemaValidation": true, }); let err = index_validation_test(value); assert_eq!( err.short_msg, "TableNameReserved", "<{err}> does not match expected error type" ); }

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