op-mcp 0.1.0

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

// ============================================================================
// vault_list Tool
// ============================================================================

/// List all accessible vaults.
#[mcp_tool(
    name = "vault_list",
    description = "List all vaults accessible to the current user. Returns vault ID, name, and description for each vault."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct VaultListTool {}

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

// ============================================================================
// vault_get Tool
// ============================================================================

/// Get details for a specific vault.
#[mcp_tool(
    name = "vault_get",
    description = "Get detailed information about a specific vault by name or ID. Returns vault metadata including item count, creation date, and permissions."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct VaultGetTool {
    /// The name or ID of the vault to retrieve.
    pub vault: String,
}

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

// ============================================================================
// vault_create Tool
// ============================================================================

/// Create a new vault.
#[mcp_tool(
    name = "vault_create",
    description = "Create a new vault in 1Password. Requires appropriate permissions in the account."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct VaultCreateTool {
    /// The name for the new vault.
    pub name: String,

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

    /// Optional icon for the vault (e.g., 'airplane', 'bank', 'cloud').
    #[serde(default)]
    pub icon: Option<String>,

    /// If true, allow admins to manage the vault.
    #[serde(default)]
    pub allow_admins_to_manage: Option<bool>,
}

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

// ============================================================================
// vault_edit Tool
// ============================================================================

/// Edit an existing vault.
#[mcp_tool(
    name = "vault_edit",
    description = "Edit an existing vault's name, description, or icon. Requires appropriate permissions."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct VaultEditTool {
    /// The name or ID of the vault to edit.
    pub vault: String,

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

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

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

    /// Whether to enable travel mode for the vault.
    #[serde(default)]
    pub travel_mode: Option<bool>,
}

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

// ============================================================================
// vault_delete Tool
// ============================================================================

/// Delete a vault.
#[mcp_tool(
    name = "vault_delete",
    description = "Delete a vault and all its contents. WARNING: This action is irreversible. All items in the vault will be permanently deleted."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct VaultDeleteTool {
    /// The name or ID of the vault to delete.
    pub vault: String,
}

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

// ============================================================================
// vault_user_list Tool
// ============================================================================

/// List users with access to a vault.
#[mcp_tool(
    name = "vault_user_list",
    description = "List all users who have access to a specific vault and their permission levels."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct VaultUserListTool {
    /// The name or ID of the vault.
    pub vault: String,
}

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

// ============================================================================
// vault_user_grant Tool
// ============================================================================

/// Grant a user access to a vault.
#[mcp_tool(
    name = "vault_user_grant",
    description = "Grant a user access to a vault with specified permissions. Multiple permissions can be combined."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct VaultUserGrantTool {
    /// The name or ID of the vault.
    pub vault: String,

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

    /// Permissions to grant. Multiple permissions can be specified.
    #[serde(default)]
    pub permissions: Option<Vec<VaultPermission>>,
}

impl VaultUserGrantTool {
    pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
        let permissions_str = self.permissions.as_ref().map(|p| permissions_to_string(p));
        let result = client
            .vault_user_grant(&self.vault, &self.user, permissions_str.as_deref())
            .await
            .map_err(op_error_to_tool_error)?;
        text_result(result)
    }
}

// ============================================================================
// vault_user_revoke Tool
// ============================================================================

/// Revoke a user's access to a vault.
#[mcp_tool(
    name = "vault_user_revoke",
    description = "Revoke a user's access to a vault. The user will no longer be able to view or modify items in this vault."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct VaultUserRevokeTool {
    /// The name or ID of the vault.
    pub vault: String,

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

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

// ============================================================================
// vault_group_list Tool
// ============================================================================

/// List groups with access to a vault.
#[mcp_tool(
    name = "vault_group_list",
    description = "List all groups that have access to a specific vault and their permission levels."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct VaultGroupListTool {
    /// The name or ID of the vault.
    pub vault: String,
}

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

// ============================================================================
// vault_group_grant Tool
// ============================================================================

/// Grant a group access to a vault.
#[mcp_tool(
    name = "vault_group_grant",
    description = "Grant a group access to a vault with specified permissions. Multiple permissions can be combined."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct VaultGroupGrantTool {
    /// The name or ID of the vault.
    pub vault: String,

    /// The group's name or ID.
    pub group: String,

    /// Permissions to grant. Multiple permissions can be specified.
    #[serde(default)]
    pub permissions: Option<Vec<VaultPermission>>,
}

impl VaultGroupGrantTool {
    pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
        let permissions_str = self.permissions.as_ref().map(|p| permissions_to_string(p));
        let result = client
            .vault_group_grant(&self.vault, &self.group, permissions_str.as_deref())
            .await
            .map_err(op_error_to_tool_error)?;
        text_result(result)
    }
}

// ============================================================================
// vault_group_revoke Tool
// ============================================================================

/// Revoke a group's access to a vault.
#[mcp_tool(
    name = "vault_group_revoke",
    description = "Revoke a group's access to a vault. Members of the group will no longer be able to access this vault through group membership."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct VaultGroupRevokeTool {
    /// The name or ID of the vault.
    pub vault: String,

    /// The group's name or ID.
    pub group: String,
}

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