op-mcp 0.1.0

MCP server providing LLM access to 1Password CLI
Documentation
//! User management tools for 1Password

use rust_mcp_schema::schema_utils::CallToolError;
use rust_mcp_schema::CallToolResult;
use rust_mcp_sdk::macros::{mcp_tool, JsonSchema};
use serde::{Deserialize, Serialize};

use crate::op::OpClient;
use crate::tools::{json_result, op_error_to_tool_error, text_result};

// ============================================================================
// user_list Tool
// ============================================================================

/// List users in the account.
#[mcp_tool(
    name = "user_list",
    description = "List all users in the 1Password account. Can filter by group membership. Returns user IDs, emails, names, and status."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct UserListTool {
    /// Filter users by group name or ID.
    #[serde(default)]
    pub group: Option<String>,

    /// Filter by vault access - only show users with access to this vault.
    #[serde(default)]
    pub vault: Option<String>,
}

impl UserListTool {
    pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
        let result = client
            .user_list(self.group.as_deref(), self.vault.as_deref())
            .await
            .map_err(op_error_to_tool_error)?;
        json_result(&result)
    }
}

// ============================================================================
// user_get Tool
// ============================================================================

/// Get details for a specific user.
#[mcp_tool(
    name = "user_get",
    description = "Get detailed information about a specific user including their name, email, state, role, and when they were created."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct UserGetTool {
    /// The user's email, name, or ID.
    pub user: String,
}

impl UserGetTool {
    pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
        let result = client
            .user_get(&self.user)
            .await
            .map_err(op_error_to_tool_error)?;
        json_result(&result)
    }
}

// ============================================================================
// user_provision Tool
// ============================================================================

/// Provision (create) a new user in the account.
#[mcp_tool(
    name = "user_provision",
    description = "Provision a new user in the 1Password account. An invitation will be sent to the user's email address."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct UserProvisionTool {
    /// The email address for the new user.
    pub email: String,

    /// The user's full name.
    pub name: String,

    /// The user's language preference (e.g., 'en', 'es', 'fr').
    #[serde(default)]
    pub language: Option<String>,
}

impl UserProvisionTool {
    pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
        let result = client
            .user_provision(&self.email, &self.name, self.language.as_deref())
            .await
            .map_err(op_error_to_tool_error)?;
        json_result(&result)
    }
}

// ============================================================================
// user_confirm Tool
// ============================================================================

/// Confirm a pending user.
#[mcp_tool(
    name = "user_confirm",
    description = "Confirm a pending user who has accepted their invitation. This activates their account."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct UserConfirmTool {
    /// The user's email, name, or ID to confirm.
    pub user: String,
}

impl UserConfirmTool {
    pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
        let result = client
            .user_confirm(&self.user)
            .await
            .map_err(op_error_to_tool_error)?;
        text_result(result)
    }
}

// ============================================================================
// user_edit Tool
// ============================================================================

/// Edit a user's properties.
#[mcp_tool(
    name = "user_edit",
    description = "Edit a user's name or travel mode setting."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct UserEditTool {
    /// The user's email, name, or ID.
    pub user: String,

    /// New name for the user.
    #[serde(default)]
    pub name: Option<String>,

    /// Enable or disable travel mode for the user.
    #[serde(default)]
    pub travel_mode: Option<bool>,
}

impl UserEditTool {
    pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
        let result = client
            .user_edit(&self.user, self.name.as_deref(), self.travel_mode)
            .await
            .map_err(op_error_to_tool_error)?;
        json_result(&result)
    }
}

// ============================================================================
// user_suspend Tool
// ============================================================================

/// Suspend a user.
#[mcp_tool(
    name = "user_suspend",
    description = "Suspend a user's access to the 1Password account. The user will not be able to sign in until reactivated."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct UserSuspendTool {
    /// The user's email, name, or ID to suspend.
    pub user: String,

    /// Number of seconds to suspend for. If not specified, suspension is indefinite.
    #[serde(default)]
    pub deauthorize_devices_after: Option<i64>,
}

impl UserSuspendTool {
    pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
        let result = client
            .user_suspend(&self.user, self.deauthorize_devices_after)
            .await
            .map_err(op_error_to_tool_error)?;
        text_result(result)
    }
}

// ============================================================================
// user_reactivate Tool
// ============================================================================

/// Reactivate a suspended user.
#[mcp_tool(
    name = "user_reactivate",
    description = "Reactivate a previously suspended user, restoring their access to the 1Password account."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct UserReactivateTool {
    /// The user's email, name, or ID to reactivate.
    pub user: String,
}

impl UserReactivateTool {
    pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
        let result = client
            .user_reactivate(&self.user)
            .await
            .map_err(op_error_to_tool_error)?;
        text_result(result)
    }
}

// ============================================================================
// user_delete Tool
// ============================================================================

/// Delete a user from the account.
#[mcp_tool(
    name = "user_delete",
    description = "Permanently remove a user from the 1Password account. This action cannot be undone. The user's personal vaults and items will be deleted."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct UserDeleteTool {
    /// The user's email, name, or ID to delete.
    pub user: String,
}

impl UserDeleteTool {
    pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
        let result = client
            .user_delete(&self.user)
            .await
            .map_err(op_error_to_tool_error)?;
        text_result(result)
    }
}