Skip to content

Commit ea73c95

Browse files
feat(mcp): add Private Link, Transit Gateway, BDB Groups, OCSP, and Suffixes tools (#561)
Add 14 new MCP tools: Cloud: - cloud_private_link_get: Get AWS PrivateLink configuration - cloud_private_link_delete: Delete AWS PrivateLink configuration - cloud_transit_gateway_attachments_get: Get Transit Gateway attachments - cloud_transit_gateway_attachment_delete: Delete Transit Gateway attachment Enterprise: - enterprise_bdb_groups_list: List database groups - enterprise_bdb_group_get: Get a specific database group - enterprise_bdb_group_delete: Delete a database group - enterprise_ocsp_config_get: Get OCSP configuration - enterprise_ocsp_status_get: Get OCSP status - enterprise_ocsp_test: Test OCSP connectivity - enterprise_suffixes_list: List DNS suffixes - enterprise_suffix_get: Get a specific DNS suffix - enterprise_cluster_suffixes_get: Get cluster-level DNS suffixes - enterprise_suffix_delete: Delete a DNS suffix
1 parent e0263c1 commit ea73c95

File tree

3 files changed

+383
-7
lines changed

3 files changed

+383
-7
lines changed

crates/redisctl-mcp/src/cloud_tools.rs

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
use redis_cloud::fixed::subscriptions::FixedSubscriptionCreateRequest;
66
use redis_cloud::{
77
AccountHandler, CloudAccountHandler, CloudClient, DatabaseHandler, FixedDatabaseHandler,
8-
FixedSubscriptionHandler, SubscriptionHandler, TaskHandler, VpcPeeringHandler,
8+
FixedSubscriptionHandler, PrivateLinkHandler, SubscriptionHandler, TaskHandler,
9+
TransitGatewayHandler, VpcPeeringHandler,
910
};
1011
use redisctl_config::Config;
1112
use rmcp::{ErrorData as RmcpError, model::*};
@@ -390,4 +391,65 @@ impl CloudTools {
390391
.map_err(|e| self.to_error(e))?;
391392
self.to_result(serde_json::to_value(result).map_err(|e| self.to_error(e))?)
392393
}
394+
395+
// =========================================================================
396+
// Private Link Operations (AWS PrivateLink)
397+
// =========================================================================
398+
399+
/// Get Private Link configuration for a subscription
400+
pub async fn get_private_link(
401+
&self,
402+
subscription_id: i64,
403+
) -> Result<CallToolResult, RmcpError> {
404+
let handler = PrivateLinkHandler::new(self.client.clone());
405+
let result = handler
406+
.get(subscription_id as i32)
407+
.await
408+
.map_err(|e| self.to_error(e))?;
409+
self.to_result(result)
410+
}
411+
412+
/// Delete Private Link configuration for a subscription
413+
pub async fn delete_private_link(
414+
&self,
415+
subscription_id: i64,
416+
) -> Result<CallToolResult, RmcpError> {
417+
let handler = PrivateLinkHandler::new(self.client.clone());
418+
let result = handler
419+
.delete(subscription_id as i32)
420+
.await
421+
.map_err(|e| self.to_error(e))?;
422+
self.to_result(result)
423+
}
424+
425+
// =========================================================================
426+
// Transit Gateway Operations (AWS Transit Gateway)
427+
// =========================================================================
428+
429+
/// Get Transit Gateway attachments for a subscription
430+
pub async fn get_transit_gateway_attachments(
431+
&self,
432+
subscription_id: i64,
433+
) -> Result<CallToolResult, RmcpError> {
434+
let handler = TransitGatewayHandler::new(self.client.clone());
435+
let result = handler
436+
.get_attachments(subscription_id as i32)
437+
.await
438+
.map_err(|e| self.to_error(e))?;
439+
self.to_result(serde_json::to_value(result).map_err(|e| self.to_error(e))?)
440+
}
441+
442+
/// Delete a Transit Gateway attachment
443+
pub async fn delete_transit_gateway_attachment(
444+
&self,
445+
subscription_id: i64,
446+
attachment_id: &str,
447+
) -> Result<CallToolResult, RmcpError> {
448+
let handler = TransitGatewayHandler::new(self.client.clone());
449+
let result = handler
450+
.delete_attachment(subscription_id as i32, attachment_id.to_string())
451+
.await
452+
.map_err(|e| self.to_error(e))?;
453+
self.to_result(serde_json::to_value(result).map_err(|e| self.to_error(e))?)
454+
}
393455
}

crates/redisctl-mcp/src/enterprise_tools.rs

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
//! Wraps Redis Enterprise API client operations for MCP tool invocation.
44
55
use redis_enterprise::{
6-
AlertHandler, BdbHandler, ClusterHandler, CrdbHandler, CrdbTasksHandler, CreateDatabaseRequest,
7-
CreateLdapMappingRequest, CreateRedisAclRequest, CreateRoleRequest, CreateUserRequest,
8-
DebugInfoHandler, DiagnosticRequest, DiagnosticsHandler, EndpointsHandler, EnterpriseClient,
9-
JobSchedulerHandler, LdapMappingHandler, LicenseHandler, LogsHandler, ModuleHandler,
10-
NodeHandler, ProxyHandler, RedisAclHandler, RolesHandler, ShardHandler, StatsHandler,
11-
UserHandler,
6+
AlertHandler, BdbGroupsHandler, BdbHandler, ClusterHandler, CrdbHandler, CrdbTasksHandler,
7+
CreateDatabaseRequest, CreateLdapMappingRequest, CreateRedisAclRequest, CreateRoleRequest,
8+
CreateUserRequest, DebugInfoHandler, DiagnosticRequest, DiagnosticsHandler, EndpointsHandler,
9+
EnterpriseClient, JobSchedulerHandler, LdapMappingHandler, LicenseHandler, LogsHandler,
10+
ModuleHandler, NodeHandler, OcspHandler, ProxyHandler, RedisAclHandler, RolesHandler,
11+
ShardHandler, StatsHandler, SuffixesHandler, UserHandler,
1212
};
1313
use redisctl_config::Config;
1414
use rmcp::{ErrorData as RmcpError, model::*};
@@ -897,4 +897,101 @@ impl EnterpriseTools {
897897
"message": format!("CRDB task {} cancelled", task_id)
898898
}))
899899
}
900+
901+
// =========================================================================
902+
// BDB Groups Operations
903+
// =========================================================================
904+
905+
/// List all BDB groups
906+
pub async fn list_bdb_groups(&self) -> Result<CallToolResult, RmcpError> {
907+
let handler = BdbGroupsHandler::new(self.client.clone());
908+
let groups = handler.list().await.map_err(|e| self.to_error(e))?;
909+
self.to_result(serde_json::to_value(groups).map_err(|e| self.to_error(e))?)
910+
}
911+
912+
/// Get a specific BDB group
913+
pub async fn get_bdb_group(&self, uid: u64) -> Result<CallToolResult, RmcpError> {
914+
let handler = BdbGroupsHandler::new(self.client.clone());
915+
let group = handler
916+
.get(uid as u32)
917+
.await
918+
.map_err(|e| self.to_error(e))?;
919+
self.to_result(serde_json::to_value(group).map_err(|e| self.to_error(e))?)
920+
}
921+
922+
/// Delete a BDB group
923+
pub async fn delete_bdb_group(&self, uid: u64) -> Result<CallToolResult, RmcpError> {
924+
let handler = BdbGroupsHandler::new(self.client.clone());
925+
handler
926+
.delete(uid as u32)
927+
.await
928+
.map_err(|e| self.to_error(e))?;
929+
self.to_result(serde_json::json!({
930+
"success": true,
931+
"message": format!("BDB group {} deleted successfully", uid)
932+
}))
933+
}
934+
935+
// =========================================================================
936+
// OCSP Operations
937+
// =========================================================================
938+
939+
/// Get OCSP configuration
940+
pub async fn get_ocsp_config(&self) -> Result<CallToolResult, RmcpError> {
941+
let handler = OcspHandler::new(self.client.clone());
942+
let config = handler.get_config().await.map_err(|e| self.to_error(e))?;
943+
self.to_result(serde_json::to_value(config).map_err(|e| self.to_error(e))?)
944+
}
945+
946+
/// Get OCSP status
947+
pub async fn get_ocsp_status(&self) -> Result<CallToolResult, RmcpError> {
948+
let handler = OcspHandler::new(self.client.clone());
949+
let status = handler.get_status().await.map_err(|e| self.to_error(e))?;
950+
self.to_result(serde_json::to_value(status).map_err(|e| self.to_error(e))?)
951+
}
952+
953+
/// Test OCSP connectivity
954+
pub async fn test_ocsp(&self) -> Result<CallToolResult, RmcpError> {
955+
let handler = OcspHandler::new(self.client.clone());
956+
let result = handler.test().await.map_err(|e| self.to_error(e))?;
957+
self.to_result(serde_json::to_value(result).map_err(|e| self.to_error(e))?)
958+
}
959+
960+
// =========================================================================
961+
// DNS Suffix Operations
962+
// =========================================================================
963+
964+
/// List all DNS suffixes
965+
pub async fn list_suffixes(&self) -> Result<CallToolResult, RmcpError> {
966+
let handler = SuffixesHandler::new(self.client.clone());
967+
let suffixes = handler.list().await.map_err(|e| self.to_error(e))?;
968+
self.to_result(serde_json::to_value(suffixes).map_err(|e| self.to_error(e))?)
969+
}
970+
971+
/// Get a specific DNS suffix
972+
pub async fn get_suffix(&self, name: &str) -> Result<CallToolResult, RmcpError> {
973+
let handler = SuffixesHandler::new(self.client.clone());
974+
let suffix = handler.get(name).await.map_err(|e| self.to_error(e))?;
975+
self.to_result(serde_json::to_value(suffix).map_err(|e| self.to_error(e))?)
976+
}
977+
978+
/// Get cluster DNS suffixes
979+
pub async fn get_cluster_suffixes(&self) -> Result<CallToolResult, RmcpError> {
980+
let handler = SuffixesHandler::new(self.client.clone());
981+
let suffixes = handler
982+
.cluster_suffixes()
983+
.await
984+
.map_err(|e| self.to_error(e))?;
985+
self.to_result(serde_json::to_value(suffixes).map_err(|e| self.to_error(e))?)
986+
}
987+
988+
/// Delete a DNS suffix
989+
pub async fn delete_suffix(&self, name: &str) -> Result<CallToolResult, RmcpError> {
990+
let handler = SuffixesHandler::new(self.client.clone());
991+
handler.delete(name).await.map_err(|e| self.to_error(e))?;
992+
self.to_result(serde_json::json!({
993+
"success": true,
994+
"message": format!("DNS suffix '{}' deleted successfully", name)
995+
}))
996+
}
900997
}

0 commit comments

Comments
 (0)