op-mcp 0.1.0

MCP server providing LLM access to 1Password CLI
Documentation
//! 1Password MCP Server
//!
//! An MCP server that provides secure access to 1Password vaults and secrets.

use rust_mcp_schema::{
    Implementation, InitializeResult, ServerCapabilities, ServerCapabilitiesTools,
    LATEST_PROTOCOL_VERSION,
};
use rust_mcp_sdk::mcp_server::{server_runtime, ServerRuntime};
use rust_mcp_sdk::{McpServer, StdioTransport, TransportOptions};
use tracing::{error, info};
use tracing_subscriber::EnvFilter;

use op_mcp::{OnePasswordHandler, OpClient};

/// Server name and version
const SERVER_NAME: &str = "op-mcp";
const SERVER_VERSION: &str = env!("CARGO_PKG_VERSION");

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize logging to stderr (stdout is reserved for MCP communication)
    init_logging();

    info!(
        name = SERVER_NAME,
        version = SERVER_VERSION,
        "Starting 1Password MCP Server"
    );

    // Create the op CLI client
    let op_client = match OpClient::new() {
        Ok(client) => {
            info!("Found op CLI");
            client
        }
        Err(e) => {
            error!("Failed to find op CLI: {}", e);
            eprintln!("Error: {}", e);
            eprintln!(
                "Please install the 1Password CLI from: https://1password.com/downloads/command-line/"
            );
            std::process::exit(1);
        }
    };

    // Check if we're authenticated
    if !op_client.is_authenticated().await {
        error!("Not signed in to 1Password");
        eprintln!("Error: Not signed in to 1Password");
        eprintln!("Please sign in using the 1Password app or run `op signin`");
        // Don't exit - let the server start and return proper errors
    }

    // Define server capabilities
    let server_details = InitializeResult {
        server_info: Implementation {
            name: SERVER_NAME.to_string(),
            version: SERVER_VERSION.to_string(),
        },
        capabilities: ServerCapabilities {
            tools: Some(ServerCapabilitiesTools { list_changed: None }),
            ..Default::default()
        },
        meta: None,
        instructions: Some(
            "This server provides secure access to 1Password vaults and secrets. \
            Use the available tools to list vaults, view items, and read secrets. \
            By default, sensitive fields are hidden - use reveal=true to show passwords."
                .to_string(),
        ),
        protocol_version: LATEST_PROTOCOL_VERSION.to_string(),
    };

    // Create stdio transport
    let transport = StdioTransport::new(TransportOptions::default())?;

    // Create the handler
    let handler = OnePasswordHandler::new(op_client);

    // Create and start the server
    let server: ServerRuntime = server_runtime::create_server(server_details, transport, handler);

    info!("Server initialized, waiting for connections");

    server.start().await?;

    info!("Server shutting down");
    Ok(())
}

/// Initialize logging to stderr with environment-based filtering
fn init_logging() {
    let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
        // Default to info level for our crate, warn for others
        EnvFilter::new("warn,op_mcp=info")
    });

    tracing_subscriber::fmt()
        .with_env_filter(filter)
        .with_writer(std::io::stderr)
        .with_target(true)
        .with_level(true)
        .init();
}