//! MCP tools for users. Business rules live in `UserService`.
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::user::dto::user::{
DeletedUserResponse, UserIdRequest, UserListResponse, UserResponse,
};
use crate::modules::user::services::user_service::UserService;
pub fn tools() -> ToolRouter<McpServer> {
McpServer::user_tools()
}
#[tool_router(router = user_tools, vis = "pub(crate)")]
impl McpServer {
/// List all users (admin only).
#[tool(annotations(read_only_hint = true, open_world_hint = false))]
async fn user_list(
&self,
Extension(parts): Extension<Parts>,
) -> AppResult<Json<UserListResponse>> {
self.authenticate_admin(&parts).await?;
let users = UserService::from_ref(&self.state).list().await?;
Ok(Json(UserListResponse {
users: users.into_iter().map(UserResponse::from).collect(),
}))
}
/// Show a user. Admins can view anyone, other users only themselves.
#[tool(annotations(read_only_hint = true, open_world_hint = false))]
async fn user_get(
&self,
Extension(parts): Extension<Parts>,
Parameters(input): Parameters<UserIdRequest>,
) -> AppResult<Json<UserResponse>> {
let user = self.authenticate(&parts).await?;
user.require_self_or_admin(input.user_id)?;
let found = UserService::from_ref(&self.state)
.find(input.user_id)
.await?;
Ok(Json(found.into()))
}
/// Delete a user and revoke their tokens (admin only; never yourself).
#[tool(annotations(destructive_hint = true, open_world_hint = false))]
async fn user_delete(
&self,
Extension(parts): Extension<Parts>,
Parameters(input): Parameters<UserIdRequest>,
) -> AppResult<Json<DeletedUserResponse>> {
let admin = self.authenticate_admin(&parts).await?;
UserService::from_ref(&self.state)
.delete(input.user_id, admin.id)
.await?;
Ok(Json(DeletedUserResponse {
deleted: true,
id: input.user_id,
}))
}
}