op-mcp 0.1.0

MCP server providing LLM access to 1Password CLI
Documentation
//! Error types for op CLI operations

use thiserror::Error;

/// Errors that can occur when interacting with the op CLI
#[derive(Error, Debug)]
pub enum OpError {
    /// The op CLI binary was not found in PATH
    #[error("1Password CLI (op) not found. Please install it from https://1password.com/downloads/command-line/")]
    NotFound,

    /// Not signed in to 1Password
    #[error("Not signed in to 1Password. Please sign in using the 1Password app or run `op signin`")]
    NotSignedIn,

    /// The specified vault was not found
    #[error("Vault not found: {0}")]
    VaultNotFound(String),

    /// The specified item was not found
    #[error("Item not found: {0}")]
    ItemNotFound(String),

    /// The specified account was not found
    #[error("Account not found: {0}")]
    AccountNotFound(String),

    /// The specified user was not found
    #[error("User not found: {0}")]
    UserNotFound(String),

    /// The specified group was not found
    #[error("Group not found: {0}")]
    GroupNotFound(String),

    /// The specified document was not found
    #[error("Document not found: {0}")]
    DocumentNotFound(String),

    /// The specified Connect server was not found
    #[error("Connect server not found: {0}")]
    ConnectServerNotFound(String),

    /// Invalid secret reference format
    #[error("Invalid secret reference: {0}. Expected format: op://vault/item/field")]
    InvalidSecretReference(String),

    /// Permission denied for the requested operation
    #[error("Permission denied: {0}")]
    PermissionDenied(String),

    /// Rate limit exceeded
    #[error("Rate limit exceeded. Please wait before making more requests.")]
    RateLimitExceeded,

    /// Feature not available (requires specific plan)
    #[error("Feature not available: {0}")]
    FeatureNotAvailable(String),

    /// The op CLI returned an error
    #[error("1Password CLI error: {0}")]
    CliError(String),

    /// Failed to parse JSON output from op CLI
    #[error("Failed to parse op CLI output: {0}")]
    ParseError(String),

    /// IO error when executing op CLI
    #[error("IO error: {0}")]
    IoError(#[from] std::io::Error),

    /// JSON serialization/deserialization error
    #[error("JSON error: {0}")]
    JsonError(#[from] serde_json::Error),
}

impl OpError {
    /// Create an OpError from op CLI stderr output
    pub fn from_stderr(stderr: &str) -> Self {
        let stderr_lower = stderr.to_lowercase();

        if stderr_lower.contains("not signed in")
            || stderr_lower.contains("sign in")
            || stderr_lower.contains("authorization")
            || stderr_lower.contains("session expired")
        {
            return OpError::NotSignedIn;
        }

        if stderr_lower.contains("isn't a vault")
            || (stderr_lower.contains("vault") && stderr_lower.contains("not found"))
        {
            return OpError::VaultNotFound(stderr.to_string());
        }

        if stderr_lower.contains("isn't an item")
            || (stderr_lower.contains("item") && stderr_lower.contains("not found"))
            || stderr_lower.contains("no item found")
        {
            return OpError::ItemNotFound(stderr.to_string());
        }

        if stderr_lower.contains("isn't a user")
            || (stderr_lower.contains("user") && stderr_lower.contains("not found"))
        {
            return OpError::UserNotFound(stderr.to_string());
        }

        if stderr_lower.contains("isn't a group")
            || (stderr_lower.contains("group") && stderr_lower.contains("not found"))
        {
            return OpError::GroupNotFound(stderr.to_string());
        }

        if stderr_lower.contains("isn't a document")
            || (stderr_lower.contains("document") && stderr_lower.contains("not found"))
        {
            return OpError::DocumentNotFound(stderr.to_string());
        }

        if stderr_lower.contains("connect server")
            && (stderr_lower.contains("not found") || stderr_lower.contains("isn't"))
        {
            return OpError::ConnectServerNotFound(stderr.to_string());
        }

        if stderr_lower.contains("permission denied")
            || stderr_lower.contains("access denied")
            || stderr_lower.contains("not authorized")
        {
            return OpError::PermissionDenied(stderr.to_string());
        }

        if stderr_lower.contains("rate limit") || stderr_lower.contains("too many requests") {
            return OpError::RateLimitExceeded;
        }

        if stderr_lower.contains("not available")
            || stderr_lower.contains("upgrade")
            || stderr_lower.contains("requires")
        {
            return OpError::FeatureNotAvailable(stderr.to_string());
        }

        OpError::CliError(stderr.to_string())
    }
}