op-mcp 0.1.0

MCP server providing LLM access to 1Password CLI
Documentation
//! MCP Server implementation for 1Password

use async_trait::async_trait;
use rust_mcp_schema::{
    schema_utils::CallToolError, CallToolRequest, CallToolResult, ListToolsRequest,
    ListToolsResult, RpcError,
};
use rust_mcp_sdk::mcp_server::ServerHandler;
use rust_mcp_sdk::McpServer;
use std::sync::Arc;
use tracing::{debug, info, instrument};

use crate::op::OpClient;
use crate::tools::OnePasswordTools;

/// Custom error for tool parameter parsing
#[derive(Debug)]
struct ToolParseError(String);

impl std::fmt::Display for ToolParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Failed to parse tool parameters: {}", self.0)
    }
}

impl std::error::Error for ToolParseError {}

/// MCP Server handler for 1Password operations
pub struct OnePasswordHandler {
    /// The op CLI client
    op_client: Arc<OpClient>,
}

impl OnePasswordHandler {
    /// Create a new handler with the given op client
    pub fn new(op_client: OpClient) -> Self {
        Self {
            op_client: Arc::new(op_client),
        }
    }

    /// Execute a tool by name with the given parameters
    #[instrument(skip(self, tool_params))]
    async fn execute_tool(
        &self,
        tool_params: OnePasswordTools,
    ) -> Result<CallToolResult, CallToolError> {
        match tool_params {
            // Auth tools
            OnePasswordTools::WhoamiTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::SigninTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::SignoutTool(tool) => tool.call(&self.op_client).await,

            // Account tools
            OnePasswordTools::AccountListTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::AccountGetTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::AccountAddTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::AccountForgetTool(tool) => tool.call(&self.op_client).await,

            // Vault tools
            OnePasswordTools::VaultListTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::VaultGetTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::VaultCreateTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::VaultEditTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::VaultDeleteTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::VaultUserListTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::VaultUserGrantTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::VaultUserRevokeTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::VaultGroupListTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::VaultGroupGrantTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::VaultGroupRevokeTool(tool) => tool.call(&self.op_client).await,

            // Item tools
            OnePasswordTools::ItemListTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ItemGetTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ItemCreateTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ItemEditTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ItemDeleteTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ItemMoveTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ItemShareTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ItemTemplateListTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ItemTemplateGetTool(tool) => tool.call(&self.op_client).await,

            // Document tools
            OnePasswordTools::DocumentListTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::DocumentGetTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::DocumentCreateTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::DocumentEditTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::DocumentDeleteTool(tool) => tool.call(&self.op_client).await,

            // User tools
            OnePasswordTools::UserListTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::UserGetTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::UserProvisionTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::UserConfirmTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::UserEditTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::UserSuspendTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::UserReactivateTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::UserDeleteTool(tool) => tool.call(&self.op_client).await,

            // Group tools
            OnePasswordTools::GroupListTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::GroupGetTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::GroupCreateTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::GroupEditTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::GroupDeleteTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::GroupUserListTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::GroupUserGrantTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::GroupUserRevokeTool(tool) => tool.call(&self.op_client).await,

            // Connect tools
            OnePasswordTools::ConnectServerListTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ConnectServerGetTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ConnectServerCreateTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ConnectServerEditTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ConnectServerDeleteTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ConnectTokenListTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ConnectTokenCreateTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ConnectTokenEditTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ConnectTokenDeleteTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ConnectVaultGrantTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ConnectVaultRevokeTool(tool) => tool.call(&self.op_client).await,

            // Service account tools
            OnePasswordTools::ServiceAccountCreateTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::ServiceAccountRatelimitTool(tool) => tool.call(&self.op_client).await,

            // Events API tools
            OnePasswordTools::EventsApiCreateTool(tool) => tool.call(&self.op_client).await,

            // Secret tools
            OnePasswordTools::SecretReadTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::SecretInjectTool(tool) => tool.call(&self.op_client).await,
            OnePasswordTools::SecretRunTool(tool) => tool.call(&self.op_client).await,
        }
    }
}

#[async_trait]
impl ServerHandler for OnePasswordHandler {
    /// Handle requests to list available tools
    async fn handle_list_tools_request(
        &self,
        _request: ListToolsRequest,
        _runtime: &dyn McpServer,
    ) -> Result<ListToolsResult, RpcError> {
        debug!("Listing available tools");

        Ok(ListToolsResult {
            tools: OnePasswordTools::tools(),
            meta: None,
            next_cursor: None,
        })
    }

    /// Handle requests to call a specific tool
    async fn handle_call_tool_request(
        &self,
        request: CallToolRequest,
        _runtime: &dyn McpServer,
    ) -> Result<CallToolResult, CallToolError> {
        let tool_name = &request.params.name;
        info!(tool = %tool_name, "Executing tool");

        // Parse the request into the appropriate tool type
        let tool_params: OnePasswordTools =
            OnePasswordTools::try_from(request.params).map_err(|e| {
                CallToolError::new(ToolParseError(e.to_string()))
            })?;

        // Execute the tool
        self.execute_tool(tool_params).await
    }
}