op-mcp 0.1.0

MCP server providing LLM access to 1Password CLI
Documentation
//! Group 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::enums::GroupUserRole;
use crate::tools::{json_result, op_error_to_tool_error, text_result};

// ============================================================================
// group_list Tool
// ============================================================================

/// List groups in the account.
#[mcp_tool(
    name = "group_list",
    description = "List all groups in the 1Password account. Can filter by user membership or vault access."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GroupListTool {
    /// Filter groups by user - only show groups this user belongs to.
    #[serde(default)]
    pub user: Option<String>,

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

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

// ============================================================================
// group_get Tool
// ============================================================================

/// Get details for a specific group.
#[mcp_tool(
    name = "group_get",
    description = "Get detailed information about a specific group including member count and permissions."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GroupGetTool {
    /// The group's name or ID.
    pub group: String,
}

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

// ============================================================================
// group_create Tool
// ============================================================================

/// Create a new group.
#[mcp_tool(
    name = "group_create",
    description = "Create a new group in the 1Password account. Groups can be used to manage vault access for multiple users."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GroupCreateTool {
    /// The name for the new group.
    pub name: String,

    /// Optional description for the group.
    #[serde(default)]
    pub description: Option<String>,
}

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

// ============================================================================
// group_edit Tool
// ============================================================================

/// Edit a group.
#[mcp_tool(
    name = "group_edit",
    description = "Edit a group's name or description."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GroupEditTool {
    /// The group's name or ID.
    pub group: String,

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

    /// New description for the group.
    #[serde(default)]
    pub description: Option<String>,
}

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

// ============================================================================
// group_delete Tool
// ============================================================================

/// Delete a group.
#[mcp_tool(
    name = "group_delete",
    description = "Delete a group from the 1Password account. Users in the group will lose any vault access granted through the group."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GroupDeleteTool {
    /// The group's name or ID to delete.
    pub group: String,
}

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

// ============================================================================
// group_user_list Tool
// ============================================================================

/// List users in a group.
#[mcp_tool(
    name = "group_user_list",
    description = "List all users who are members of a specific group."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GroupUserListTool {
    /// The group's name or ID.
    pub group: String,
}

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

// ============================================================================
// group_user_grant Tool
// ============================================================================

/// Add a user to a group.
#[mcp_tool(
    name = "group_user_grant",
    description = "Add a user to a group. The user will inherit any vault permissions granted to the group."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GroupUserGrantTool {
    /// The group's name or ID.
    pub group: String,

    /// The user's email, name, or ID to add.
    pub user: String,

    /// Role for the user in the group.
    #[serde(default)]
    pub role: Option<GroupUserRole>,
}

impl GroupUserGrantTool {
    pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
        let role_str = self.role.as_ref().map(|r| r.to_string());
        let result = client
            .group_user_grant(&self.group, &self.user, role_str.as_deref())
            .await
            .map_err(op_error_to_tool_error)?;
        text_result(result)
    }
}

// ============================================================================
// group_user_revoke Tool
// ============================================================================

/// Remove a user from a group.
#[mcp_tool(
    name = "group_user_revoke",
    description = "Remove a user from a group. The user will lose any vault access granted through this group membership."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GroupUserRevokeTool {
    /// The group's name or ID.
    pub group: String,

    /// The user's email, name, or ID to remove.
    pub user: String,
}

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