op-mcp 0.1.0

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

// ============================================================================
// connect_server_list Tool
// ============================================================================

/// List Connect servers.
#[mcp_tool(
    name = "connect_server_list",
    description = "List all 1Password Connect servers configured in the account."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ConnectServerListTool {}

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

// ============================================================================
// connect_server_get Tool
// ============================================================================

/// Get details for a Connect server.
#[mcp_tool(
    name = "connect_server_get",
    description = "Get detailed information about a specific 1Password Connect server."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ConnectServerGetTool {
    /// The server name or ID.
    pub server: String,
}

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

// ============================================================================
// connect_server_create Tool
// ============================================================================

/// Create a new Connect server.
#[mcp_tool(
    name = "connect_server_create",
    description = "Create a new 1Password Connect server. Returns credentials that must be saved securely - they cannot be retrieved later."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ConnectServerCreateTool {
    /// The name for the new Connect server.
    pub name: String,

    /// Vaults to grant the server access to (names or IDs).
    #[serde(default)]
    pub vaults: Option<Vec<String>>,
}

impl ConnectServerCreateTool {
    pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
        let vaults_refs: Option<Vec<&str>> = self
            .vaults
            .as_ref()
            .map(|v| v.iter().map(|s| s.as_str()).collect());

        let result = client
            .connect_server_create(&self.name, vaults_refs.as_deref())
            .await
            .map_err(op_error_to_tool_error)?;
        json_result(&result)
    }
}

// ============================================================================
// connect_server_edit Tool
// ============================================================================

/// Edit a Connect server.
#[mcp_tool(
    name = "connect_server_edit",
    description = "Edit a 1Password Connect server's name."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ConnectServerEditTool {
    /// The server name or ID to edit.
    pub server: String,

    /// New name for the server.
    pub name: String,
}

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

// ============================================================================
// connect_server_delete Tool
// ============================================================================

/// Delete a Connect server.
#[mcp_tool(
    name = "connect_server_delete",
    description = "Delete a 1Password Connect server. All tokens for this server will be invalidated."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ConnectServerDeleteTool {
    /// The server name or ID to delete.
    pub server: String,
}

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

// ============================================================================
// connect_token_list Tool
// ============================================================================

/// List tokens for a Connect server.
#[mcp_tool(
    name = "connect_token_list",
    description = "List all tokens for a 1Password Connect server."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ConnectTokenListTool {
    /// The server name or ID.
    pub server: String,
}

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

// ============================================================================
// connect_token_create Tool
// ============================================================================

/// Create a new token for a Connect server.
#[mcp_tool(
    name = "connect_token_create",
    description = "Create a new access token for a 1Password Connect server. The token value is only shown once and cannot be retrieved later."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ConnectTokenCreateTool {
    /// The server name or ID.
    pub server: String,

    /// Name for the new token.
    pub name: String,

    /// Token expiration duration.
    #[serde(default)]
    pub expires_in: Option<TokenExpiry>,
}

impl ConnectTokenCreateTool {
    pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
        let expires_in_str = self.expires_in.as_ref().map(|e| e.to_string());
        let result = client
            .connect_token_create(&self.server, &self.name, expires_in_str.as_deref())
            .await
            .map_err(op_error_to_tool_error)?;
        // Return as text since it contains the sensitive token value
        text_result(result)
    }
}

// ============================================================================
// connect_token_edit Tool
// ============================================================================

/// Edit a Connect token.
#[mcp_tool(
    name = "connect_token_edit",
    description = "Edit a 1Password Connect token's name."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ConnectTokenEditTool {
    /// The server name or ID.
    pub server: String,

    /// The token name or ID to edit.
    pub token: String,

    /// New name for the token.
    pub name: String,
}

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

// ============================================================================
// connect_token_delete Tool
// ============================================================================

/// Delete a Connect token.
#[mcp_tool(
    name = "connect_token_delete",
    description = "Delete/revoke a 1Password Connect token. The token will immediately stop working."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ConnectTokenDeleteTool {
    /// The server name or ID.
    pub server: String,

    /// The token name or ID to delete.
    pub token: String,
}

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

// ============================================================================
// connect_vault_grant Tool
// ============================================================================

/// Grant a Connect server access to a vault.
#[mcp_tool(
    name = "connect_vault_grant",
    description = "Grant a 1Password Connect server access to a vault."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ConnectVaultGrantTool {
    /// The server name or ID.
    pub server: String,

    /// The vault name or ID to grant access to.
    pub vault: String,
}

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

// ============================================================================
// connect_vault_revoke Tool
// ============================================================================

/// Revoke a Connect server's access to a vault.
#[mcp_tool(
    name = "connect_vault_revoke",
    description = "Revoke a 1Password Connect server's access to a vault."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ConnectVaultRevokeTool {
    /// The server name or ID.
    pub server: String,

    /// The vault name or ID to revoke access from.
    pub vault: String,
}

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