slave 0.13.1

CLI for slave, a Laravel-inspired Rust framework for MCP-first apps: scaffold an MCP backend with auth, roles, teams and SurrealDB migrations, add server-rendered web apps, and route them by domain or path
//! 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()))
    }
}