//! MCP tools for roles. Business rules live in `RoleService`.
use axum::extract::FromRef;
use axum::http::request::Parts;
use rmcp::handler::server::router::tool::ToolRouter;
use rmcp::handler::server::tool::Extension;
use rmcp::handler::server::wrapper::{Json, Parameters};
use rmcp::{tool, tool_router};
use crate::base::error::AppResult;
use crate::base::mcp::McpServer;
use crate::modules::role::dto::role::{RoleAssignmentRequest, RoleListResponse, UserRolesRequest};
use crate::modules::role::services::role_service::RoleService;
pub fn tools() -> ToolRouter<McpServer> {
McpServer::role_tools()
}
#[tool_router(router = role_tools, vis = "pub(crate)")]
impl McpServer {
/// List every role (super admin only).
#[tool(annotations(read_only_hint = true, open_world_hint = false))]
async fn role_list(
&self,
Extension(parts): Extension<Parts>,
) -> AppResult<Json<RoleListResponse>> {
self.authenticate_super_admin(&parts).await?;
Ok(Json(
RoleService::from_ref(&self.state).list().await?.into(),
))
}
/// A user's roles. Super admins can view anyone's, other users only their own.
#[tool(annotations(read_only_hint = true, open_world_hint = false))]
async fn role_list_for_user(
&self,
Extension(parts): Extension<Parts>,
Parameters(input): Parameters<UserRolesRequest>,
) -> AppResult<Json<RoleListResponse>> {
let user = self.authenticate(&parts).await?;
user.require_self_or_super_admin(input.user_id)?;
let roles = RoleService::from_ref(&self.state)
.roles_of(input.user_id)
.await?;
Ok(Json(roles.into()))
}
/// Grant a role to a user and return their roles (super admin only).
#[tool(annotations(idempotent_hint = true, open_world_hint = false))]
async fn role_assign(
&self,
Extension(parts): Extension<Parts>,
Parameters(input): Parameters<RoleAssignmentRequest>,
) -> AppResult<Json<RoleListResponse>> {
self.authenticate_super_admin(&parts).await?;
let roles = RoleService::from_ref(&self.state)
.assign(input.user_id, input.role.trim())
.await?;
Ok(Json(roles.into()))
}
/// Remove a role from a user and return their remaining roles (super admin only).
/// The last super admin can never be removed.
#[tool(annotations(destructive_hint = true, open_world_hint = false))]
async fn role_revoke(
&self,
Extension(parts): Extension<Parts>,
Parameters(input): Parameters<RoleAssignmentRequest>,
) -> AppResult<Json<RoleListResponse>> {
self.authenticate_super_admin(&parts).await?;
let roles = RoleService::from_ref(&self.state)
.revoke(input.user_id, input.role.trim())
.await?;
Ok(Json(roles.into()))
}
}