Skip to main content
Glama

Convex MCP server

Official
by get-convex
environment_variables.rs8.07 kB
use application::EnvVarChange; use axum::{ extract::{ FromRef, State, }, response::IntoResponse, }; use common::http::{ extract::Json, HttpResponseError, }; use http::StatusCode; use model::environment_variables::{ types::{ EnvVarName, EnvVarValue, EnvironmentVariable, }, EnvironmentVariablesModel, }; use serde::{ Deserialize, Serialize, }; use utoipa::ToSchema; use utoipa_axum::router::OpenApiRouter; use crate::{ admin::{ must_be_admin, must_be_admin_with_write_access, }, authentication::ExtractIdentity, LocalAppState, }; #[derive(Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct UpdateEnvVarRequest { name: String, value: Option<String>, // None → delete existing } impl UpdateEnvVarRequest { pub async fn into_env_var_changes(self) -> anyhow::Result<Vec<EnvVarChange>> { match self { UpdateEnvVarRequest { name, value: Some(value), } => { let env_var = validate_env_var(&name, &value)?; Ok(vec![EnvVarChange::Set(env_var)]) }, UpdateEnvVarRequest { name, value: None } => { let name = name.parse()?; Ok(vec![EnvVarChange::Unset(name)]) }, } } } #[derive(Deserialize, ToSchema)] pub struct UpdateEnvVarsRequest { changes: Vec<UpdateEnvVarRequest>, } /// Update environment variables /// /// Update one or many environment variables in a deployment. /// This will invalidate all subscriptions, since environment variables /// are accessible in queries but are not part of the cache key of a query /// result. #[utoipa::path( post, path = "/update_environment_variables", request_body = UpdateEnvVarsRequest, responses((status = 200)), )] pub async fn update_environment_variables( State(st): State<LocalAppState>, ExtractIdentity(identity): ExtractIdentity, Json(UpdateEnvVarsRequest { changes }): Json<UpdateEnvVarsRequest>, ) -> Result<impl IntoResponse, HttpResponseError> { must_be_admin_with_write_access(&identity)?; let mut env_var_changes = vec![]; for change in changes { env_var_changes.extend(change.into_env_var_changes().await?); } env_var_changes.sort(); let mut tx = st.application.begin(identity).await?; let audit_events = st .application .update_environment_variables(&mut tx, env_var_changes) .await?; st.application .commit_with_audit_log_events(tx, audit_events, "update_env_vars") .await?; Ok(StatusCode::OK) } #[derive(Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct ListEnvVarsResponse { environment_variables: std::collections::BTreeMap<String, String>, } /// List environment variables /// /// Get all environment variables in a deployment. /// In the future this might not include "secret" environment /// variables. #[utoipa::path( get, path = "/list_environment_variables", responses( (status = 200, body = ListEnvVarsResponse) ), )] pub async fn list_environment_variables( State(st): State<LocalAppState>, ExtractIdentity(identity): ExtractIdentity, ) -> Result<impl IntoResponse, HttpResponseError> { must_be_admin(&identity)?; let mut tx = st.application.begin(identity).await?; let env_vars = EnvironmentVariablesModel::new(&mut tx).get_all().await?; let environment_variables = env_vars .into_iter() .map(|(name, value)| (name.to_string(), value.to_string())) .collect(); Ok(Json(ListEnvVarsResponse { environment_variables, })) } fn validate_env_var(name: &String, value: &String) -> anyhow::Result<EnvironmentVariable> { let name: EnvVarName = name.parse()?; let value: EnvVarValue = value.parse()?; Ok(EnvironmentVariable::new(name, value)) } pub fn platform_router<S>() -> OpenApiRouter<S> where LocalAppState: FromRef<S>, S: Clone + Send + Sync + 'static, { OpenApiRouter::new().routes(utoipa_axum::routes!( update_environment_variables, list_environment_variables )) } #[cfg(test)] mod tests { use std::collections::BTreeMap; use axum_extra::headers::authorization::Credentials; use common::types::{ EnvVarName, EnvVarValue, }; use http::Request; use keybroker::Identity; use maplit::btreemap; use model::environment_variables::EnvironmentVariablesModel; use runtime::prod::ProdRuntime; use serde_json::json; use crate::test_helpers::{ setup_backend_for_test, TestLocalBackend, }; async fn update_environment_variables( backend: &TestLocalBackend, changes: serde_json::Value, ) -> anyhow::Result<()> { let json_body = json!({"changes": changes}); let body = axum::body::Body::from(serde_json::to_vec(&json_body)?); let req = Request::builder() .uri("/api/update_environment_variables") .method("POST") .header("Content-Type", "application/json") .header("Authorization", backend.admin_auth_header.0.encode()) .body(body)?; let () = backend.expect_success(req).await?; Ok(()) } async fn list_environment_variables( backend: &TestLocalBackend, ) -> anyhow::Result<BTreeMap<EnvVarName, EnvVarValue>> { let mut tx = backend.st.application.begin(Identity::system()).await?; let envs = EnvironmentVariablesModel::new(&mut tx).get_all().await?; Ok(envs) } #[convex_macro::prod_rt_test] async fn test_create_env_vars(rt: ProdRuntime) -> anyhow::Result<()> { let backend = setup_backend_for_test(rt).await?; update_environment_variables( &backend, json!([ {"name": "name1", "value": "value1"}, {"name": "name2", "value": "value2"}, ]), ) .await?; assert_eq!( list_environment_variables(&backend).await?, btreemap! { "name1".parse()? => "value1".parse()?, "name2".parse()? => "value2".parse()?, } ); Ok(()) } #[convex_macro::prod_rt_test] async fn test_update_env_vars(rt: ProdRuntime) -> anyhow::Result<()> { let backend = setup_backend_for_test(rt).await?; update_environment_variables( &backend, json!([ {"name": "name1", "value": "value1"}, {"name": "name2", "value": "value2"}, ]), ) .await?; update_environment_variables( &backend, json!([ {"name": "name2", "value": "value2b"}, {"name": "name3", "value": "value3"}, ]), ) .await?; assert_eq!( list_environment_variables(&backend).await?, btreemap! { "name1".parse()? => "value1".parse()?, "name2".parse()? => "value2b".parse()?, "name3".parse()? => "value3".parse()?, } ); Ok(()) } #[convex_macro::prod_rt_test] async fn test_delete_env_vars(rt: ProdRuntime) -> anyhow::Result<()> { let backend = setup_backend_for_test(rt).await?; update_environment_variables( &backend, json!([ {"name": "name1", "value": "value1"}, {"name": "name2", "value": "value2"}, ]), ) .await?; update_environment_variables( &backend, json!([ {"name": "name2"}, {"name": "name3"}, ]), ) .await?; assert_eq!( list_environment_variables(&backend).await?, btreemap! { "name1".parse()? => "value1".parse()?, } ); 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