use database_mcp_server::types::{CreateDatabaseRequest, GetTableSchemaRequest, ListTablesRequest, QueryRequest};
use rmcp::handler::server::tool::ToolRouter;
use rmcp::handler::server::wrapper::Parameters;
use rmcp::model::{CallToolResult, Content, ErrorData};
use rmcp::tool;
use database_mcp_sql::validation::validate_read_only_with_dialect;
use super::MysqlAdapter;
impl MysqlAdapter {
const WRITE_TOOL_NAMES: &[&str] = &["write_query", "create_database"];
#[must_use]
pub fn build_tool_router(&self) -> ToolRouter<Self> {
let mut router = Self::tool_router();
if self.config.read_only {
for name in Self::WRITE_TOOL_NAMES {
router.remove_route(name);
}
}
router
}
}
#[rmcp::tool_router]
impl MysqlAdapter {
#[tool(
name = "list_databases",
annotations(
read_only_hint = true,
destructive_hint = false,
idempotent_hint = true,
open_world_hint = false
)
)]
pub async fn tool_list_databases(&self) -> Result<CallToolResult, ErrorData> {
let result = self.list_databases().await?;
Ok(CallToolResult::success(vec![Content::json(result)?]))
}
#[tool(
name = "list_tables",
annotations(
read_only_hint = true,
destructive_hint = false,
idempotent_hint = true,
open_world_hint = false
)
)]
pub async fn tool_list_tables(
&self,
Parameters(request): Parameters<ListTablesRequest>,
) -> Result<CallToolResult, ErrorData> {
let result = self.list_tables(&request.database_name).await?;
Ok(CallToolResult::success(vec![Content::json(result)?]))
}
#[tool(
name = "get_table_schema",
annotations(
read_only_hint = true,
destructive_hint = false,
idempotent_hint = true,
open_world_hint = false
)
)]
pub async fn tool_get_table_schema(
&self,
Parameters(request): Parameters<GetTableSchemaRequest>,
) -> Result<CallToolResult, ErrorData> {
let result = self
.get_table_schema(&request.database_name, &request.table_name)
.await?;
Ok(CallToolResult::success(vec![Content::json(result)?]))
}
#[tool(
name = "read_query",
annotations(
read_only_hint = true,
destructive_hint = false,
idempotent_hint = true,
open_world_hint = true
)
)]
pub async fn tool_read_query(
&self,
Parameters(request): Parameters<QueryRequest>,
) -> Result<CallToolResult, ErrorData> {
validate_read_only_with_dialect(&request.query, &sqlparser::dialect::MySqlDialect {})?;
let db = Some(request.database_name.trim()).filter(|s| !s.is_empty());
let result = self.execute_query(&request.query, db).await?;
Ok(CallToolResult::success(vec![Content::json(result)?]))
}
#[tool(
name = "write_query",
annotations(
read_only_hint = false,
destructive_hint = true,
idempotent_hint = false,
open_world_hint = true
)
)]
pub async fn tool_write_query(
&self,
Parameters(request): Parameters<QueryRequest>,
) -> Result<CallToolResult, ErrorData> {
let db = Some(request.database_name.trim()).filter(|s| !s.is_empty());
let result = self.execute_query(&request.query, db).await?;
Ok(CallToolResult::success(vec![Content::json(result)?]))
}
#[tool(
name = "create_database",
annotations(
read_only_hint = false,
destructive_hint = false,
idempotent_hint = false,
open_world_hint = false
)
)]
pub async fn tool_create_database(
&self,
Parameters(request): Parameters<CreateDatabaseRequest>,
) -> Result<CallToolResult, ErrorData> {
let result = self.create_database(&request.database_name).await?;
Ok(CallToolResult::success(vec![Content::json(result)?]))
}
}