use std::sync::Arc;
use std::time::Duration;
use redis_cloud::databases::DatabaseCreateRequest;
use redis_cloud::flexible::{DatabaseHandler, SubscriptionHandler};
use redisctl_core::cloud::{
backup_database_and_wait, create_database_and_wait, delete_database_and_wait,
delete_subscription_and_wait, flush_database_and_wait, import_database_and_wait,
update_database_and_wait,
};
use schemars::JsonSchema;
use serde::Deserialize;
use tower_mcp::extract::{Json, State};
use tower_mcp::{CallToolResult, Error as McpError, McpRouter, ResultExt, Tool, ToolBuilder};
use crate::state::AppState;
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ListSubscriptionsInput {
#[serde(default)]
pub profile: Option<String>,
}
pub fn list_subscriptions(state: Arc<AppState>) -> Tool {
ToolBuilder::new("list_subscriptions")
.description("List all Redis Cloud subscriptions accessible with the current credentials. Returns JSON with subscription details.")
.read_only_safe()
.extractor_handler_typed::<_, _, _, ListSubscriptionsInput>(
state,
|State(state): State<Arc<AppState>>, Json(input): Json<ListSubscriptionsInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = SubscriptionHandler::new(client);
let account_subs = handler
.get_all_subscriptions()
.await
.tool_context("Failed to list subscriptions")?;
CallToolResult::from_serialize(&account_subs)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetSubscriptionInput {
pub subscription_id: i32,
#[serde(default)]
pub profile: Option<String>,
}
pub fn get_subscription(state: Arc<AppState>) -> Tool {
ToolBuilder::new("get_subscription")
.description("Get detailed information about a specific Redis Cloud subscription. Returns JSON with full subscription details.")
.read_only_safe()
.extractor_handler_typed::<_, _, _, GetSubscriptionInput>(
state,
|State(state): State<Arc<AppState>>, Json(input): Json<GetSubscriptionInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = SubscriptionHandler::new(client);
let subscription = handler
.get_subscription_by_id(input.subscription_id)
.await
.tool_context("Failed to get subscription")?;
CallToolResult::from_serialize(&subscription)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ListDatabasesInput {
pub subscription_id: i32,
#[serde(default)]
pub profile: Option<String>,
}
pub fn list_databases(state: Arc<AppState>) -> Tool {
ToolBuilder::new("list_databases")
.description(
"List all databases in a Redis Cloud subscription. Returns JSON with database details.",
)
.read_only_safe()
.extractor_handler_typed::<_, _, _, ListDatabasesInput>(
state,
|State(state): State<Arc<AppState>>, Json(input): Json<ListDatabasesInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = DatabaseHandler::new(client);
let databases = handler
.get_subscription_databases(input.subscription_id, None, None)
.await
.tool_context("Failed to list databases")?;
CallToolResult::from_serialize(&databases)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetDatabaseInput {
pub subscription_id: i32,
pub database_id: i32,
#[serde(default)]
pub profile: Option<String>,
}
pub fn get_database(state: Arc<AppState>) -> Tool {
ToolBuilder::new("get_database")
.description("Get detailed information about a specific Redis Cloud database. Returns JSON with full database configuration.")
.read_only_safe()
.extractor_handler_typed::<_, _, _, GetDatabaseInput>(
state,
|State(state): State<Arc<AppState>>, Json(input): Json<GetDatabaseInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = DatabaseHandler::new(client);
let database = handler
.get_subscription_database_by_id(input.subscription_id, input.database_id)
.await
.tool_context("Failed to get database")?;
CallToolResult::from_serialize(&database)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetBackupStatusInput {
pub subscription_id: i32,
pub database_id: i32,
#[serde(default)]
pub region_name: Option<String>,
#[serde(default)]
pub profile: Option<String>,
}
pub fn get_backup_status(state: Arc<AppState>) -> Tool {
ToolBuilder::new("get_backup_status")
.description("Get backup status and history for a Redis Cloud database.")
.read_only_safe()
.extractor_handler_typed::<_, _, _, GetBackupStatusInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<GetBackupStatusInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = DatabaseHandler::new(client);
let status = handler
.get_database_backup_status(
input.subscription_id,
input.database_id,
input.region_name,
)
.await
.tool_context("Failed to get backup status")?;
CallToolResult::from_serialize(&status)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetSlowLogInput {
pub subscription_id: i32,
pub database_id: i32,
#[serde(default)]
pub region_name: Option<String>,
#[serde(default)]
pub profile: Option<String>,
}
pub fn get_slow_log(state: Arc<AppState>) -> Tool {
ToolBuilder::new("get_slow_log")
.description(
"Get slow log entries for a Redis Cloud database. Shows slow queries for debugging.",
)
.read_only_safe()
.extractor_handler_typed::<_, _, _, GetSlowLogInput>(
state,
|State(state): State<Arc<AppState>>, Json(input): Json<GetSlowLogInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = DatabaseHandler::new(client);
let log = handler
.get_slow_log(input.subscription_id, input.database_id, input.region_name)
.await
.tool_context("Failed to get slow log")?;
CallToolResult::from_serialize(&log)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetTagsInput {
pub subscription_id: i32,
pub database_id: i32,
#[serde(default)]
pub profile: Option<String>,
}
pub fn get_tags(state: Arc<AppState>) -> Tool {
ToolBuilder::new("get_database_tags")
.description("Get tags attached to a Redis Cloud database.")
.read_only_safe()
.extractor_handler_typed::<_, _, _, GetTagsInput>(
state,
|State(state): State<Arc<AppState>>, Json(input): Json<GetTagsInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = DatabaseHandler::new(client);
let tags = handler
.get_tags(input.subscription_id, input.database_id)
.await
.tool_context("Failed to get tags")?;
CallToolResult::from_serialize(&tags)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetCertificateInput {
pub subscription_id: i32,
pub database_id: i32,
#[serde(default)]
pub profile: Option<String>,
}
pub fn get_database_certificate(state: Arc<AppState>) -> Tool {
ToolBuilder::new("get_database_certificate")
.description(
"Get the TLS/SSL certificate for a Redis Cloud database. \
Returns the public certificate in PEM format for TLS connections.",
)
.read_only_safe()
.extractor_handler_typed::<_, _, _, GetCertificateInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<GetCertificateInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = DatabaseHandler::new(client);
let cert = handler
.get_subscription_database_certificate(
input.subscription_id,
input.database_id,
)
.await
.tool_context("Failed to get certificate")?;
CallToolResult::from_serialize(&cert)
},
)
.build()
}
fn default_replication() -> bool {
true
}
fn default_protocol() -> String {
"redis".to_string()
}
fn default_timeout() -> u64 {
600
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct CreateDatabaseInput {
pub subscription_id: i32,
pub name: String,
pub memory_limit_in_gb: f64,
#[serde(default = "default_replication")]
pub replication: bool,
#[serde(default = "default_protocol")]
pub protocol: String,
#[serde(default)]
pub data_persistence: Option<String>,
#[serde(default = "default_timeout")]
pub timeout_seconds: u64,
#[serde(default)]
pub profile: Option<String>,
}
pub fn create_database(state: Arc<AppState>) -> Tool {
ToolBuilder::new("create_database")
.description(
"Create a new Redis Cloud database and wait for it to be ready. \
Returns the created database details. Requires write permission.",
)
.non_destructive()
.extractor_handler_typed::<_, _, _, CreateDatabaseInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<CreateDatabaseInput>| async move {
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let request = match (input.protocol.as_str(), input.data_persistence.as_ref()) {
("redis", None) => DatabaseCreateRequest::builder()
.name(&input.name)
.memory_limit_in_gb(input.memory_limit_in_gb)
.replication(input.replication)
.build(),
("redis", Some(persistence)) => DatabaseCreateRequest::builder()
.name(&input.name)
.memory_limit_in_gb(input.memory_limit_in_gb)
.replication(input.replication)
.data_persistence(persistence)
.build(),
(protocol, None) => DatabaseCreateRequest::builder()
.name(&input.name)
.memory_limit_in_gb(input.memory_limit_in_gb)
.replication(input.replication)
.protocol(protocol)
.build(),
(protocol, Some(persistence)) => DatabaseCreateRequest::builder()
.name(&input.name)
.memory_limit_in_gb(input.memory_limit_in_gb)
.replication(input.replication)
.protocol(protocol)
.data_persistence(persistence)
.build(),
};
let database = create_database_and_wait(
&client,
input.subscription_id,
&request,
Duration::from_secs(input.timeout_seconds),
None, )
.await
.tool_context("Failed to create database")?;
CallToolResult::from_serialize(&database)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct UpdateDatabaseInput {
pub subscription_id: i32,
pub database_id: i32,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub memory_limit_in_gb: Option<f64>,
#[serde(default)]
pub replication: Option<bool>,
#[serde(default)]
pub data_persistence: Option<String>,
#[serde(default)]
pub data_eviction_policy: Option<String>,
#[serde(default = "default_timeout")]
pub timeout_seconds: u64,
#[serde(default)]
pub profile: Option<String>,
}
pub fn update_database(state: Arc<AppState>) -> Tool {
ToolBuilder::new("update_database")
.description(
"Update an existing Redis Cloud database configuration. \
Returns the updated database details. Requires write permission.",
)
.non_destructive()
.extractor_handler_typed::<_, _, _, UpdateDatabaseInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<UpdateDatabaseInput>| async move {
use redis_cloud::databases::DatabaseUpdateRequest;
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let mut request = DatabaseUpdateRequest::builder().build();
request.name = input.name;
request.memory_limit_in_gb = input.memory_limit_in_gb;
request.replication = input.replication;
request.data_persistence = input.data_persistence;
request.data_eviction_policy = input.data_eviction_policy;
if request.name.is_none()
&& request.memory_limit_in_gb.is_none()
&& request.replication.is_none()
&& request.data_persistence.is_none()
&& request.data_eviction_policy.is_none()
{
return Err(McpError::tool(
"At least one update field is required",
));
}
let database = update_database_and_wait(
&client,
input.subscription_id,
input.database_id,
&request,
Duration::from_secs(input.timeout_seconds),
None,
)
.await
.tool_context("Failed to update database")?;
CallToolResult::from_serialize(&database)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct DeleteDatabaseInput {
pub subscription_id: i32,
pub database_id: i32,
#[serde(default = "default_timeout")]
pub timeout_seconds: u64,
#[serde(default)]
pub profile: Option<String>,
}
pub fn delete_database(state: Arc<AppState>) -> Tool {
ToolBuilder::new("delete_database")
.description(
"DANGEROUS: Permanently deletes a database and all its data. This action cannot be undone. \
Requires write permission.",
)
.destructive()
.extractor_handler_typed::<_, _, _, DeleteDatabaseInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<DeleteDatabaseInput>| async move {
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
delete_database_and_wait(
&client,
input.subscription_id,
input.database_id,
Duration::from_secs(input.timeout_seconds),
None,
)
.await
.tool_context("Failed to delete database")?;
CallToolResult::from_serialize(&serde_json::json!({
"message": "Database deleted successfully",
"subscription_id": input.subscription_id,
"database_id": input.database_id
}))
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct BackupDatabaseInput {
pub subscription_id: i32,
pub database_id: i32,
#[serde(default)]
pub region_name: Option<String>,
#[serde(default = "default_timeout")]
pub timeout_seconds: u64,
#[serde(default)]
pub profile: Option<String>,
}
pub fn backup_database(state: Arc<AppState>) -> Tool {
ToolBuilder::new("backup_database")
.description(
"Trigger a manual backup of a Redis Cloud database. \
Requires write permission.",
)
.non_destructive()
.extractor_handler_typed::<_, _, _, BackupDatabaseInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<BackupDatabaseInput>| async move {
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
backup_database_and_wait(
&client,
input.subscription_id,
input.database_id,
input.region_name.as_deref(),
Duration::from_secs(input.timeout_seconds),
None,
)
.await
.tool_context("Failed to backup database")?;
CallToolResult::from_serialize(&serde_json::json!({
"message": "Backup completed successfully",
"subscription_id": input.subscription_id,
"database_id": input.database_id
}))
},
)
.build()
}
fn default_import_timeout() -> u64 {
1800 }
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ImportDatabaseInput {
pub subscription_id: i32,
pub database_id: i32,
pub source_type: String,
pub import_from_uri: String,
#[serde(default = "default_import_timeout")]
pub timeout_seconds: u64,
#[serde(default)]
pub profile: Option<String>,
}
pub fn import_database(state: Arc<AppState>) -> Tool {
ToolBuilder::new("import_database")
.description(
"Import data into a Redis Cloud database from an external source. \
WARNING: This will overwrite existing data. Requires write permission.",
)
.non_destructive()
.extractor_handler_typed::<_, _, _, ImportDatabaseInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<ImportDatabaseInput>| async move {
use redis_cloud::databases::DatabaseImportRequest;
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let request = DatabaseImportRequest::builder()
.source_type(&input.source_type)
.import_from_uri(vec![input.import_from_uri.clone()])
.build();
import_database_and_wait(
&client,
input.subscription_id,
input.database_id,
&request,
Duration::from_secs(input.timeout_seconds),
None,
)
.await
.tool_context("Failed to import database")?;
CallToolResult::from_serialize(&serde_json::json!({
"message": "Import completed successfully",
"subscription_id": input.subscription_id,
"database_id": input.database_id
}))
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct DeleteSubscriptionInput {
pub subscription_id: i32,
#[serde(default = "default_timeout")]
pub timeout_seconds: u64,
#[serde(default)]
pub profile: Option<String>,
}
pub fn delete_subscription(state: Arc<AppState>) -> Tool {
ToolBuilder::new("delete_subscription")
.description(
"DANGEROUS: Permanently deletes a subscription. All databases must be deleted first. \
This action cannot be undone. Requires write permission.",
)
.destructive()
.extractor_handler_typed::<_, _, _, DeleteSubscriptionInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<DeleteSubscriptionInput>| async move {
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
delete_subscription_and_wait(
&client,
input.subscription_id,
Duration::from_secs(input.timeout_seconds),
None,
)
.await
.tool_context("Failed to delete subscription")?;
CallToolResult::from_serialize(&serde_json::json!({
"message": "Subscription deleted successfully",
"subscription_id": input.subscription_id
}))
},
)
.build()
}
fn default_flush_timeout() -> u64 {
300
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct FlushDatabaseInput {
pub subscription_id: i32,
pub database_id: i32,
#[serde(default = "default_flush_timeout")]
pub timeout_seconds: u64,
#[serde(default)]
pub profile: Option<String>,
}
pub fn flush_database(state: Arc<AppState>) -> Tool {
ToolBuilder::new("flush_database")
.description(
"DANGEROUS: Removes all data from a database. This action cannot be undone. \
Requires write permission.",
)
.destructive()
.extractor_handler_typed::<_, _, _, FlushDatabaseInput>(
state,
|State(state): State<Arc<AppState>>, Json(input): Json<FlushDatabaseInput>| async move {
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
flush_database_and_wait(
&client,
input.subscription_id,
input.database_id,
Duration::from_secs(input.timeout_seconds),
None,
)
.await
.tool_context("Failed to flush database")?;
CallToolResult::from_serialize(&serde_json::json!({
"message": "Database flushed successfully",
"subscription_id": input.subscription_id,
"database_id": input.database_id
}))
},
)
.build()
}
fn default_cloud_account_id() -> i32 {
1 }
fn default_subscription_timeout() -> u64 {
1800 }
#[derive(Debug, Deserialize, JsonSchema)]
pub struct CreateSubscriptionInput {
pub name: String,
pub cloud_provider: String,
pub region: String,
#[serde(default = "default_cloud_account_id")]
pub cloud_account_id: i32,
pub database_name: String,
pub memory_limit_in_gb: f64,
#[serde(default = "default_protocol")]
pub protocol: String,
#[serde(default = "default_replication")]
pub replication: bool,
#[serde(default = "default_subscription_timeout")]
pub timeout_seconds: u64,
#[serde(default)]
pub profile: Option<String>,
}
pub fn create_subscription(state: Arc<AppState>) -> Tool {
ToolBuilder::new("create_subscription")
.description(
"Create a new Redis Cloud Pro subscription with an initial database. \
This is a simplified interface for common subscription creation scenarios. \
Requires write permission.",
)
.non_destructive()
.extractor_handler_typed::<_, _, _, CreateSubscriptionInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<CreateSubscriptionInput>| async move {
use redis_cloud::flexible::subscriptions::{
SubscriptionCreateRequest, SubscriptionDatabaseSpec, SubscriptionRegionSpec,
SubscriptionSpec,
};
use redisctl_core::cloud::create_subscription_and_wait;
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let request = SubscriptionCreateRequest::builder()
.name(&input.name)
.cloud_providers(vec![SubscriptionSpec {
provider: Some(input.cloud_provider.clone()),
cloud_account_id: Some(input.cloud_account_id),
regions: vec![SubscriptionRegionSpec {
region: input.region.clone(),
multiple_availability_zones: None,
preferred_availability_zones: None,
networking: None,
}],
}])
.databases(vec![SubscriptionDatabaseSpec {
name: input.database_name.clone(),
protocol: input.protocol.clone(),
memory_limit_in_gb: Some(input.memory_limit_in_gb),
dataset_size_in_gb: None,
support_oss_cluster_api: None,
data_persistence: None,
replication: Some(input.replication),
throughput_measurement: None,
local_throughput_measurement: None,
modules: None,
quantity: None,
average_item_size_in_bytes: None,
resp_version: None,
redis_version: None,
sharding_type: None,
query_performance_factor: None,
}])
.build();
let subscription = create_subscription_and_wait(
&client,
&request,
Duration::from_secs(input.timeout_seconds),
None,
)
.await
.tool_context("Failed to create subscription")?;
CallToolResult::from_serialize(&subscription)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct UpdateSubscriptionInput {
pub subscription_id: i32,
#[serde(default)]
pub profile: Option<String>,
}
pub fn update_subscription(state: Arc<AppState>) -> Tool {
ToolBuilder::new("update_subscription")
.description(
"Update a Redis Cloud subscription. \
Requires write permission.",
)
.non_destructive()
.extractor_handler_typed::<_, _, _, UpdateSubscriptionInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<UpdateSubscriptionInput>| async move {
use redis_cloud::flexible::subscriptions::BaseSubscriptionUpdateRequest;
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let request = BaseSubscriptionUpdateRequest {
subscription_id: None,
command_type: None,
};
let handler = SubscriptionHandler::new(client);
let result = handler
.update_subscription(input.subscription_id, &request)
.await
.tool_context("Failed to update subscription")?;
CallToolResult::from_serialize(&result)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetSubscriptionPricingInput {
pub subscription_id: i32,
#[serde(default)]
pub profile: Option<String>,
}
pub fn get_subscription_pricing(state: Arc<AppState>) -> Tool {
ToolBuilder::new("get_subscription_pricing")
.description(
"Get pricing details for a Redis Cloud subscription.",
)
.read_only_safe()
.extractor_handler_typed::<_, _, _, GetSubscriptionPricingInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<GetSubscriptionPricingInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = SubscriptionHandler::new(client);
let pricing = handler
.get_subscription_pricing(input.subscription_id)
.await
.tool_context("Failed to get subscription pricing")?;
CallToolResult::from_serialize(&pricing)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetRedisVersionsInput {
#[serde(default)]
pub subscription_id: Option<i32>,
#[serde(default)]
pub profile: Option<String>,
}
pub fn get_redis_versions(state: Arc<AppState>) -> Tool {
ToolBuilder::new("get_redis_versions")
.description(
"Get available Redis versions. Optionally filter by subscription ID.",
)
.read_only_safe()
.extractor_handler_typed::<_, _, _, GetRedisVersionsInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<GetRedisVersionsInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = SubscriptionHandler::new(client);
let versions = handler
.get_redis_versions(input.subscription_id)
.await
.tool_context("Failed to get Redis versions")?;
CallToolResult::from_serialize(&versions)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetSubscriptionCidrAllowlistInput {
pub subscription_id: i32,
#[serde(default)]
pub profile: Option<String>,
}
pub fn get_subscription_cidr_allowlist(state: Arc<AppState>) -> Tool {
ToolBuilder::new("get_subscription_cidr_allowlist")
.description("Get the CIDR allowlist for a Redis Cloud subscription.")
.read_only_safe()
.extractor_handler_typed::<_, _, _, GetSubscriptionCidrAllowlistInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<GetSubscriptionCidrAllowlistInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = SubscriptionHandler::new(client);
let allowlist = handler
.get_cidr_allowlist(input.subscription_id)
.await
.tool_context("Failed to get subscription CIDR allowlist")?;
CallToolResult::from_serialize(&allowlist)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct UpdateSubscriptionCidrAllowlistInput {
pub subscription_id: i32,
#[serde(default)]
pub cidr_ips: Option<Vec<String>>,
#[serde(default)]
pub security_group_ids: Option<Vec<String>>,
#[serde(default)]
pub profile: Option<String>,
}
pub fn update_subscription_cidr_allowlist(state: Arc<AppState>) -> Tool {
ToolBuilder::new("update_subscription_cidr_allowlist")
.description(
"Update the CIDR allowlist for a Redis Cloud subscription. \
Requires write permission.",
)
.non_destructive()
.extractor_handler_typed::<_, _, _, UpdateSubscriptionCidrAllowlistInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<UpdateSubscriptionCidrAllowlistInput>| async move {
use redis_cloud::flexible::subscriptions::CidrAllowlistUpdateRequest;
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let request = CidrAllowlistUpdateRequest {
subscription_id: None,
cidr_ips: input.cidr_ips,
security_group_ids: input.security_group_ids,
command_type: None,
};
let handler = SubscriptionHandler::new(client);
let result = handler
.update_subscription_cidr_allowlist(input.subscription_id, &request)
.await
.tool_context("Failed to update subscription CIDR allowlist")?;
CallToolResult::from_serialize(&result)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetSubscriptionMaintenanceWindowsInput {
pub subscription_id: i32,
#[serde(default)]
pub profile: Option<String>,
}
pub fn get_subscription_maintenance_windows(state: Arc<AppState>) -> Tool {
ToolBuilder::new("get_subscription_maintenance_windows")
.description("Get maintenance windows for a Redis Cloud subscription.")
.read_only_safe()
.extractor_handler_typed::<_, _, _, GetSubscriptionMaintenanceWindowsInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<GetSubscriptionMaintenanceWindowsInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = SubscriptionHandler::new(client);
let windows = handler
.get_subscription_maintenance_windows(input.subscription_id)
.await
.tool_context("Failed to get subscription maintenance windows")?;
CallToolResult::from_serialize(&windows)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct MaintenanceWindowInput {
pub start_hour: i32,
pub duration_in_hours: i32,
pub days: Vec<String>,
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct UpdateSubscriptionMaintenanceWindowsInput {
pub subscription_id: i32,
pub mode: String,
#[serde(default)]
pub windows: Option<Vec<MaintenanceWindowInput>>,
#[serde(default)]
pub profile: Option<String>,
}
pub fn update_subscription_maintenance_windows(state: Arc<AppState>) -> Tool {
ToolBuilder::new("update_subscription_maintenance_windows")
.description(
"Update maintenance windows for a Redis Cloud subscription. \
Requires write permission.",
)
.non_destructive()
.extractor_handler_typed::<_, _, _, UpdateSubscriptionMaintenanceWindowsInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<UpdateSubscriptionMaintenanceWindowsInput>| async move {
use redis_cloud::flexible::subscriptions::{
MaintenanceWindowSpec, SubscriptionMaintenanceWindowsSpec,
};
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let windows = input.windows.map(|ws| {
ws.into_iter()
.map(|w| MaintenanceWindowSpec {
start_hour: w.start_hour,
duration_in_hours: w.duration_in_hours,
days: w.days,
})
.collect()
});
let request = SubscriptionMaintenanceWindowsSpec {
mode: input.mode,
windows,
};
let handler = SubscriptionHandler::new(client);
let result = handler
.update_subscription_maintenance_windows(input.subscription_id, &request)
.await
.tool_context("Failed to update subscription maintenance windows")?;
CallToolResult::from_serialize(&result)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetActiveActiveRegionsInput {
pub subscription_id: i32,
#[serde(default)]
pub profile: Option<String>,
}
pub fn get_active_active_regions(state: Arc<AppState>) -> Tool {
ToolBuilder::new("get_active_active_regions")
.description(
"Get regions from an Active-Active Redis Cloud subscription.",
)
.read_only_safe()
.extractor_handler_typed::<_, _, _, GetActiveActiveRegionsInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<GetActiveActiveRegionsInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = SubscriptionHandler::new(client);
let regions = handler
.get_regions_from_active_active_subscription(input.subscription_id)
.await
.tool_context("Failed to get Active-Active regions")?;
CallToolResult::from_serialize(®ions)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct AddActiveActiveRegionInput {
pub subscription_id: i32,
pub deployment_cidr: String,
#[serde(default)]
pub region: Option<String>,
#[serde(default)]
pub vpc_id: Option<String>,
#[serde(default)]
pub dry_run: Option<bool>,
#[serde(default)]
pub resp_version: Option<String>,
#[serde(default)]
pub profile: Option<String>,
}
pub fn add_active_active_region(state: Arc<AppState>) -> Tool {
ToolBuilder::new("add_active_active_region")
.description(
"Add a new region to an Active-Active Redis Cloud subscription. \
Requires write permission.",
)
.non_destructive()
.extractor_handler_typed::<_, _, _, AddActiveActiveRegionInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<AddActiveActiveRegionInput>| async move {
use redis_cloud::flexible::subscriptions::ActiveActiveRegionCreateRequest;
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let request = ActiveActiveRegionCreateRequest {
subscription_id: None,
region: input.region,
vpc_id: input.vpc_id,
deployment_cidr: input.deployment_cidr,
dry_run: input.dry_run,
databases: None,
resp_version: input.resp_version,
customer_managed_key_resource_name: None,
command_type: None,
};
let handler = SubscriptionHandler::new(client);
let result = handler
.add_new_region_to_active_active_subscription(
input.subscription_id,
&request,
)
.await
.tool_context("Failed to add Active-Active region")?;
CallToolResult::from_serialize(&result)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ActiveActiveRegionToDeleteInput {
pub region: String,
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct DeleteActiveActiveRegionsInput {
pub subscription_id: i32,
pub regions: Vec<ActiveActiveRegionToDeleteInput>,
#[serde(default)]
pub dry_run: Option<bool>,
#[serde(default)]
pub profile: Option<String>,
}
pub fn delete_active_active_regions(state: Arc<AppState>) -> Tool {
ToolBuilder::new("delete_active_active_regions")
.description(
"DANGEROUS: Permanently removes regions from an Active-Active subscription. \
This may cause data loss in the removed regions. Requires write permission.",
)
.destructive()
.extractor_handler_typed::<_, _, _, DeleteActiveActiveRegionsInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<DeleteActiveActiveRegionsInput>| async move {
use redis_cloud::flexible::subscriptions::{
ActiveActiveRegionDeleteRequest, ActiveActiveRegionToDelete,
};
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let regions = input
.regions
.into_iter()
.map(|r| ActiveActiveRegionToDelete {
region: Some(r.region),
})
.collect();
let request = ActiveActiveRegionDeleteRequest {
subscription_id: None,
regions: Some(regions),
dry_run: input.dry_run,
command_type: None,
};
let handler = SubscriptionHandler::new(client);
let result = handler
.delete_regions_from_active_active_subscription(input.subscription_id, &request)
.await
.tool_context("Failed to delete Active-Active regions")?;
CallToolResult::from_serialize(&result)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetAvailableDatabaseVersionsInput {
pub subscription_id: i32,
pub database_id: i32,
#[serde(default)]
pub profile: Option<String>,
}
pub fn get_available_database_versions(state: Arc<AppState>) -> Tool {
ToolBuilder::new("get_available_database_versions")
.description("Get available target Redis versions for upgrading a database.")
.read_only_safe()
.extractor_handler_typed::<_, _, _, GetAvailableDatabaseVersionsInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<GetAvailableDatabaseVersionsInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = DatabaseHandler::new(client);
let versions = handler
.get_available_target_versions(input.subscription_id, input.database_id)
.await
.tool_context("Failed to get available database versions")?;
CallToolResult::from_serialize(&versions)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct UpgradeDatabaseRedisVersionInput {
pub subscription_id: i32,
pub database_id: i32,
pub target_redis_version: String,
#[serde(default)]
pub profile: Option<String>,
}
pub fn upgrade_database_redis_version(state: Arc<AppState>) -> Tool {
ToolBuilder::new("upgrade_database_redis_version")
.description(
"Upgrade the Redis version of a database. \
Use get_available_database_versions to find valid target versions. \
Requires write permission.",
)
.non_destructive()
.extractor_handler_typed::<_, _, _, UpgradeDatabaseRedisVersionInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<UpgradeDatabaseRedisVersionInput>| async move {
use redis_cloud::databases::DatabaseUpgradeRedisVersionRequest;
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let request = DatabaseUpgradeRedisVersionRequest {
database_id: None,
subscription_id: None,
target_redis_version: input.target_redis_version,
command_type: None,
};
let handler = DatabaseHandler::new(client);
let result = handler
.upgrade_database_redis_version(
input.subscription_id,
input.database_id,
&request,
)
.await
.tool_context("Failed to upgrade database Redis version")?;
CallToolResult::from_serialize(&result)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetDatabaseUpgradeStatusInput {
pub subscription_id: i32,
pub database_id: i32,
#[serde(default)]
pub profile: Option<String>,
}
pub fn get_database_upgrade_status(state: Arc<AppState>) -> Tool {
ToolBuilder::new("get_database_upgrade_status")
.description("Get the Redis version upgrade status for a database.")
.read_only_safe()
.extractor_handler_typed::<_, _, _, GetDatabaseUpgradeStatusInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<GetDatabaseUpgradeStatusInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = DatabaseHandler::new(client);
let status = handler
.get_database_redis_version_upgrade_status(
input.subscription_id,
input.database_id,
)
.await
.tool_context("Failed to get database upgrade status")?;
CallToolResult::from_serialize(&status)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetDatabaseImportStatusInput {
pub subscription_id: i32,
pub database_id: i32,
#[serde(default)]
pub profile: Option<String>,
}
pub fn get_database_import_status(state: Arc<AppState>) -> Tool {
ToolBuilder::new("get_database_import_status")
.description("Get the import status for a Redis Cloud database.")
.read_only_safe()
.extractor_handler_typed::<_, _, _, GetDatabaseImportStatusInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<GetDatabaseImportStatusInput>| async move {
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = DatabaseHandler::new(client);
let status = handler
.get_database_import_status(input.subscription_id, input.database_id)
.await
.tool_context("Failed to get database import status")?;
CallToolResult::from_serialize(&status)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct CreateDatabaseTagInput {
pub subscription_id: i32,
pub database_id: i32,
pub key: String,
pub value: String,
#[serde(default)]
pub profile: Option<String>,
}
pub fn create_database_tag(state: Arc<AppState>) -> Tool {
ToolBuilder::new("create_database_tag")
.description(
"Create a tag on a Redis Cloud database. \
Requires write permission.",
)
.non_destructive()
.extractor_handler_typed::<_, _, _, CreateDatabaseTagInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<CreateDatabaseTagInput>| async move {
use redis_cloud::databases::DatabaseTagCreateRequest;
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let request = DatabaseTagCreateRequest {
key: input.key,
value: input.value,
subscription_id: None,
database_id: None,
command_type: None,
};
let handler = DatabaseHandler::new(client);
let tag = handler
.create_tag(input.subscription_id, input.database_id, &request)
.await
.tool_context("Failed to create database tag")?;
CallToolResult::from_serialize(&tag)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct UpdateDatabaseTagInput {
pub subscription_id: i32,
pub database_id: i32,
pub tag_key: String,
pub value: String,
#[serde(default)]
pub profile: Option<String>,
}
pub fn update_database_tag(state: Arc<AppState>) -> Tool {
ToolBuilder::new("update_database_tag")
.description(
"Update a tag on a Redis Cloud database. \
Requires write permission.",
)
.non_destructive()
.extractor_handler_typed::<_, _, _, UpdateDatabaseTagInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<UpdateDatabaseTagInput>| async move {
use redis_cloud::databases::DatabaseTagUpdateRequest;
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let request = DatabaseTagUpdateRequest {
subscription_id: None,
database_id: None,
key: None,
value: input.value,
command_type: None,
};
let handler = DatabaseHandler::new(client);
let tag = handler
.update_tag(
input.subscription_id,
input.database_id,
input.tag_key,
&request,
)
.await
.tool_context("Failed to update database tag")?;
CallToolResult::from_serialize(&tag)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct DeleteDatabaseTagInput {
pub subscription_id: i32,
pub database_id: i32,
pub tag_key: String,
#[serde(default)]
pub profile: Option<String>,
}
pub fn delete_database_tag(state: Arc<AppState>) -> Tool {
ToolBuilder::new("delete_database_tag")
.description(
"DANGEROUS: Permanently deletes a tag from a database. This action cannot be undone. \
Requires write permission.",
)
.destructive()
.extractor_handler_typed::<_, _, _, DeleteDatabaseTagInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<DeleteDatabaseTagInput>| async move {
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let handler = DatabaseHandler::new(client);
let result = handler
.delete_tag(input.subscription_id, input.database_id, input.tag_key)
.await
.tool_context("Failed to delete database tag")?;
CallToolResult::from_serialize(&result)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct TagInput {
pub key: String,
pub value: String,
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct UpdateDatabaseTagsInput {
pub subscription_id: i32,
pub database_id: i32,
pub tags: Vec<TagInput>,
#[serde(default)]
pub profile: Option<String>,
}
pub fn update_database_tags(state: Arc<AppState>) -> Tool {
ToolBuilder::new("update_database_tags")
.description(
"Update all tags on a Redis Cloud database (replaces existing tags). \
Requires write permission.",
)
.non_destructive()
.extractor_handler_typed::<_, _, _, UpdateDatabaseTagsInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<UpdateDatabaseTagsInput>| async move {
use redis_cloud::databases::{DatabaseTagsUpdateRequest, Tag};
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let tags = input
.tags
.into_iter()
.map(|t| Tag {
key: t.key,
value: t.value,
command_type: None,
})
.collect();
let request = DatabaseTagsUpdateRequest {
subscription_id: None,
database_id: None,
tags,
command_type: None,
};
let handler = DatabaseHandler::new(client);
let result = handler
.update_tags(input.subscription_id, input.database_id, &request)
.await
.tool_context("Failed to update database tags")?;
CallToolResult::from_serialize(&result)
},
)
.build()
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct UpdateCrdbLocalPropertiesInput {
pub subscription_id: i32,
pub database_id: i32,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub dry_run: Option<bool>,
#[serde(default)]
pub memory_limit_in_gb: Option<f64>,
#[serde(default)]
pub dataset_size_in_gb: Option<f64>,
#[serde(default)]
pub support_oss_cluster_api: Option<bool>,
#[serde(default)]
pub use_external_endpoint_for_oss_cluster_api: Option<bool>,
#[serde(default)]
pub enable_tls: Option<bool>,
#[serde(default)]
pub global_data_persistence: Option<String>,
#[serde(default)]
pub global_password: Option<String>,
#[serde(default)]
pub global_source_ip: Option<Vec<String>>,
#[serde(default)]
pub data_eviction_policy: Option<String>,
#[serde(default)]
pub profile: Option<String>,
}
pub fn update_crdb_local_properties(state: Arc<AppState>) -> Tool {
ToolBuilder::new("update_crdb_local_properties")
.description(
"Update local properties of an Active-Active (CRDB) database. \
Requires write permission.",
)
.non_destructive()
.extractor_handler_typed::<_, _, _, UpdateCrdbLocalPropertiesInput>(
state,
|State(state): State<Arc<AppState>>,
Json(input): Json<UpdateCrdbLocalPropertiesInput>| async move {
use redis_cloud::databases::CrdbUpdatePropertiesRequest;
if !state.is_write_allowed() {
return Err(McpError::tool(
"Write operations not allowed in read-only mode",
));
}
let client = state
.cloud_client_for_profile(input.profile.as_deref())
.await
.map_err(|e| crate::tools::credential_error("cloud", e))?;
let request = CrdbUpdatePropertiesRequest {
subscription_id: None,
database_id: None,
name: input.name,
dry_run: input.dry_run,
memory_limit_in_gb: input.memory_limit_in_gb,
dataset_size_in_gb: input.dataset_size_in_gb,
support_oss_cluster_api: input.support_oss_cluster_api,
use_external_endpoint_for_oss_cluster_api: input
.use_external_endpoint_for_oss_cluster_api,
client_ssl_certificate: None,
client_tls_certificates: None,
enable_tls: input.enable_tls,
global_data_persistence: input.global_data_persistence,
global_password: input.global_password,
global_source_ip: input.global_source_ip,
global_alerts: None,
regions: None,
data_eviction_policy: input.data_eviction_policy,
command_type: None,
};
let handler = DatabaseHandler::new(client);
let result = handler
.update_crdb_local_properties(
input.subscription_id,
input.database_id,
&request,
)
.await
.tool_context("Failed to update CRDB local properties")?;
CallToolResult::from_serialize(&result)
},
)
.build()
}
pub fn router(state: Arc<AppState>) -> McpRouter {
McpRouter::new()
.tool(list_subscriptions(state.clone()))
.tool(get_subscription(state.clone()))
.tool(get_subscription_pricing(state.clone()))
.tool(get_redis_versions(state.clone()))
.tool(get_subscription_cidr_allowlist(state.clone()))
.tool(get_subscription_maintenance_windows(state.clone()))
.tool(get_active_active_regions(state.clone()))
.tool(list_databases(state.clone()))
.tool(get_database(state.clone()))
.tool(get_backup_status(state.clone()))
.tool(get_slow_log(state.clone()))
.tool(get_tags(state.clone()))
.tool(get_database_certificate(state.clone()))
.tool(get_available_database_versions(state.clone()))
.tool(get_database_upgrade_status(state.clone()))
.tool(get_database_import_status(state.clone()))
.tool(create_database(state.clone()))
.tool(update_database(state.clone()))
.tool(delete_database(state.clone()))
.tool(backup_database(state.clone()))
.tool(import_database(state.clone()))
.tool(delete_subscription(state.clone()))
.tool(flush_database(state.clone()))
.tool(create_subscription(state.clone()))
.tool(update_subscription(state.clone()))
.tool(update_subscription_cidr_allowlist(state.clone()))
.tool(update_subscription_maintenance_windows(state.clone()))
.tool(add_active_active_region(state.clone()))
.tool(delete_active_active_regions(state.clone()))
.tool(upgrade_database_redis_version(state.clone()))
.tool(create_database_tag(state.clone()))
.tool(update_database_tag(state.clone()))
.tool(delete_database_tag(state.clone()))
.tool(update_database_tags(state.clone()))
.tool(update_crdb_local_properties(state.clone()))
}