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::{json_result, op_error_to_tool_error, text_result};
#[mcp_tool(
name = "document_list",
description = "List all documents stored in 1Password. Can filter by vault. Returns document names, IDs, and metadata."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct DocumentListTool {
#[serde(default)]
pub vault: Option<String>,
}
impl DocumentListTool {
pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
let result = client
.document_list(self.vault.as_deref())
.await
.map_err(op_error_to_tool_error)?;
json_result(&result)
}
}
#[mcp_tool(
name = "document_get",
description = "Download a document from 1Password. Returns the document content as base64-encoded data or saves to a file path if specified."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct DocumentGetTool {
pub document: String,
#[serde(default)]
pub vault: Option<String>,
#[serde(default)]
pub output: Option<String>,
}
impl DocumentGetTool {
pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
let result = client
.document_get(&self.document, self.vault.as_deref(), self.output.as_deref())
.await
.map_err(op_error_to_tool_error)?;
text_result(result)
}
}
#[mcp_tool(
name = "document_create",
description = "Upload a new document to 1Password from a local file."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct DocumentCreateTool {
pub file_path: String,
#[serde(default)]
pub vault: Option<String>,
#[serde(default)]
pub title: Option<String>,
#[serde(default)]
pub tags: Option<Vec<String>>,
}
impl DocumentCreateTool {
pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
let tags_refs: Option<Vec<&str>> = self
.tags
.as_ref()
.map(|t| t.iter().map(|s| s.as_str()).collect());
let result = client
.document_create(
&self.file_path,
self.vault.as_deref(),
self.title.as_deref(),
tags_refs.as_deref(),
)
.await
.map_err(op_error_to_tool_error)?;
json_result(&result)
}
}
#[mcp_tool(
name = "document_edit",
description = "Replace an existing document's content with a new file. The document title and metadata are preserved unless explicitly changed."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct DocumentEditTool {
pub document: String,
pub file_path: String,
#[serde(default)]
pub vault: Option<String>,
#[serde(default)]
pub title: Option<String>,
}
impl DocumentEditTool {
pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
let result = client
.document_edit(
&self.document,
&self.file_path,
self.vault.as_deref(),
self.title.as_deref(),
)
.await
.map_err(op_error_to_tool_error)?;
json_result(&result)
}
}
#[mcp_tool(
name = "document_delete",
description = "Delete a document from 1Password. By default, moves to archive (can be recovered). Use archive=false for permanent deletion."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct DocumentDeleteTool {
pub document: String,
#[serde(default)]
pub vault: Option<String>,
#[serde(default = "default_true")]
pub archive: bool,
}
fn default_true() -> bool {
true
}
impl DocumentDeleteTool {
pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
let result = client
.document_delete(&self.document, self.vault.as_deref(), self.archive)
.await
.map_err(op_error_to_tool_error)?;
text_result(result)
}
}