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::{ItemCategory, ShareExpiry};
use crate::tools::{json_result, op_error_to_tool_error, text_result};
#[mcp_tool(
name = "item_list",
description = "List items in 1Password. Can filter by vault, categories, tags, or favorite status. Returns item summaries without sensitive field values."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ItemListTool {
#[serde(default)]
pub vault: Option<String>,
#[serde(default)]
pub categories: Option<Vec<ItemCategory>>,
#[serde(default)]
pub tags: Option<Vec<String>>,
#[serde(default)]
pub favorite: Option<bool>,
}
impl ItemListTool {
pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
let categories_strs: Option<Vec<String>> = self
.categories
.as_ref()
.map(|c| c.iter().map(|cat| cat.to_string()).collect());
let categories_refs: Option<Vec<&str>> = categories_strs
.as_ref()
.map(|c| c.iter().map(|s| s.as_str()).collect());
let tags_refs: Option<Vec<&str>> = self
.tags
.as_ref()
.map(|t| t.iter().map(|s| s.as_str()).collect());
let result = client
.item_list(
self.vault.as_deref(),
categories_refs.as_deref(),
tags_refs.as_deref(),
self.favorite,
)
.await
.map_err(op_error_to_tool_error)?;
json_result(&result)
}
}
#[mcp_tool(
name = "item_get",
description = "Get detailed information about a specific item by name or ID. By default, sensitive fields like passwords are hidden. Set reveal=true to show all field values."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ItemGetTool {
pub item: String,
#[serde(default)]
pub vault: Option<String>,
#[serde(default)]
pub reveal: bool,
}
impl ItemGetTool {
pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
let result = client
.item_get(&self.item, self.vault.as_deref(), self.reveal)
.await
.map_err(op_error_to_tool_error)?;
json_result(&result)
}
}
#[mcp_tool(
name = "item_create",
description = "Create a new item in 1Password. Specify a category, title, vault, and fields. Fields are specified as 'field=value' or 'section.field=value' pairs."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ItemCreateTool {
pub category: ItemCategory,
pub title: String,
#[serde(default)]
pub vault: Option<String>,
#[serde(default)]
pub generate_password: Option<String>,
#[serde(default)]
pub url: Option<String>,
#[serde(default)]
pub tags: Option<Vec<String>>,
#[serde(default)]
pub fields: Option<Vec<String>>,
#[serde(default)]
pub favorite: bool,
}
impl ItemCreateTool {
pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
let category_str = self.category.to_string();
let tags_refs: Option<Vec<&str>> = self
.tags
.as_ref()
.map(|t| t.iter().map(|s| s.as_str()).collect());
let fields_refs: Option<Vec<&str>> = self
.fields
.as_ref()
.map(|f| f.iter().map(|s| s.as_str()).collect());
let result = client
.item_create(
&category_str,
&self.title,
self.vault.as_deref(),
self.generate_password.as_deref(),
self.url.as_deref(),
tags_refs.as_deref(),
fields_refs.as_deref(),
self.favorite,
)
.await
.map_err(op_error_to_tool_error)?;
json_result(&result)
}
}
#[mcp_tool(
name = "item_edit",
description = "Edit an existing item's fields, title, tags, or other properties. Fields are specified as 'field=value' or 'section.field=value' pairs."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ItemEditTool {
pub item: String,
#[serde(default)]
pub vault: Option<String>,
#[serde(default)]
pub title: Option<String>,
#[serde(default)]
pub url: Option<String>,
#[serde(default)]
pub generate_password: Option<String>,
#[serde(default)]
pub tags: Option<Vec<String>>,
#[serde(default)]
pub fields: Option<Vec<String>>,
#[serde(default)]
pub favorite: Option<bool>,
}
impl ItemEditTool {
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 fields_refs: Option<Vec<&str>> = self
.fields
.as_ref()
.map(|f| f.iter().map(|s| s.as_str()).collect());
let result = client
.item_edit(
&self.item,
self.vault.as_deref(),
self.title.as_deref(),
self.url.as_deref(),
self.generate_password.as_deref(),
tags_refs.as_deref(),
fields_refs.as_deref(),
self.favorite,
)
.await
.map_err(op_error_to_tool_error)?;
json_result(&result)
}
}
#[mcp_tool(
name = "item_delete",
description = "Delete or archive an item. By default, items are archived (moved to trash) and can be recovered. Use archive=false to permanently delete."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ItemDeleteTool {
pub item: String,
#[serde(default)]
pub vault: Option<String>,
#[serde(default = "default_true")]
pub archive: bool,
}
fn default_true() -> bool {
true
}
impl ItemDeleteTool {
pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
let result = client
.item_delete(&self.item, self.vault.as_deref(), self.archive)
.await
.map_err(op_error_to_tool_error)?;
text_result(result)
}
}
#[mcp_tool(
name = "item_move",
description = "Move an item from one vault to another. Requires appropriate permissions in both vaults."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ItemMoveTool {
pub item: String,
#[serde(default)]
pub current_vault: Option<String>,
pub destination_vault: String,
}
impl ItemMoveTool {
pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
let result = client
.item_move(&self.item, self.current_vault.as_deref(), &self.destination_vault)
.await
.map_err(op_error_to_tool_error)?;
text_result(result)
}
}
#[mcp_tool(
name = "item_share",
description = "Create a secure, time-limited link to share an item with someone. The link expires after the specified duration and can be limited to specific email addresses."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ItemShareTool {
pub item: String,
#[serde(default)]
pub vault: Option<String>,
#[serde(default)]
pub expiry: Option<ShareExpiry>,
#[serde(default)]
pub emails: Option<Vec<String>>,
#[serde(default)]
pub view_once: bool,
}
impl ItemShareTool {
pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
let expiry_str = self.expiry.as_ref().map(|e| e.to_string());
let emails_refs: Option<Vec<&str>> = self
.emails
.as_ref()
.map(|e| e.iter().map(|s| s.as_str()).collect());
let result = client
.item_share(
&self.item,
self.vault.as_deref(),
expiry_str.as_deref(),
emails_refs.as_deref(),
self.view_once,
)
.await
.map_err(op_error_to_tool_error)?;
text_result(result)
}
}
#[mcp_tool(
name = "item_template_list",
description = "List all available item templates/categories that can be used when creating new items."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ItemTemplateListTool {}
impl ItemTemplateListTool {
pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
let result = client
.item_template_list()
.await
.map_err(op_error_to_tool_error)?;
json_result(&result)
}
}
#[mcp_tool(
name = "item_template_get",
description = "Get the field structure and details for a specific item template/category. Useful for understanding what fields are available when creating items."
)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ItemTemplateGetTool {
pub template: String,
}
impl ItemTemplateGetTool {
pub async fn call(&self, client: &OpClient) -> Result<CallToolResult, CallToolError> {
let result = client
.item_template_get(&self.template)
.await
.map_err(op_error_to_tool_error)?;
json_result(&result)
}
}