"""File and attachment management tools.""" import json import base64 from mcp.types import Tool from frappe_mcp.client.frappe_api import FrappeClient def tools() -> list[Tool]: return [ Tool( name="frappe_list_files", description="List files in Frappe's file manager, optionally filtered by attached document.", inputSchema={ "type": "object", "properties": { "attached_to_doctype": {"type": "string"}, "attached_to_name": {"type": "string"}, "folder": {"type": "string", "description": "e.g. 'Home/Attachments'"}, "is_private": {"type": "integer", "description": "1 = private, 0 = public"}, "limit": {"type": "integer", "default": 20}, }, }, ), Tool( name="frappe_get_file", description="Get details of a file by its name/ID.", inputSchema={ "type": "object", "required": ["name"], "properties": { "name": {"type": "string"}, }, }, ), Tool( name="frappe_upload_file_base64", description=( "Upload a file to Frappe by providing base64-encoded content. " "Optionally attach it to a document." ), inputSchema={ "type": "object", "required": ["filename", "content_base64"], "properties": { "filename": {"type": "string", "description": "File name with extension"}, "content_base64": {"type": "string", "description": "Base64-encoded file content"}, "attached_to_doctype": {"type": "string"}, "attached_to_name": {"type": "string"}, "attached_to_field": {"type": "string"}, "is_private": {"type": "integer", "default": 1}, "folder": {"type": "string", "default": "Home/Attachments"}, }, }, ), Tool( name="frappe_delete_file", description="Delete a file from Frappe.", inputSchema={ "type": "object", "required": ["name"], "properties": { "name": {"type": "string", "description": "File document name"}, }, }, ), Tool( name="frappe_move_file", description="Move a file to a different folder.", inputSchema={ "type": "object", "required": ["name", "folder"], "properties": { "name": {"type": "string"}, "folder": {"type": "string", "description": "Target folder path"}, }, }, ), Tool( name="frappe_create_folder", description="Create a new folder in the Frappe file manager.", inputSchema={ "type": "object", "required": ["folder_name"], "properties": { "folder_name": {"type": "string"}, "parent_folder": {"type": "string", "default": "Home"}, }, }, ), ] def handlers() -> dict: return { "frappe_list_files": _list_files, "frappe_get_file": _get_file, "frappe_upload_file_base64": _upload_file_base64, "frappe_delete_file": _delete_file, "frappe_move_file": _move_file, "frappe_create_folder": _create_folder, } async def _list_files(args: dict) -> str: client = FrappeClient() filters = [] if dt := args.get("attached_to_doctype"): filters.append(["attached_to_doctype", "=", dt]) if name := args.get("attached_to_name"): filters.append(["attached_to_name", "=", name]) if folder := args.get("folder"): filters.append(["folder", "=", folder]) if "is_private" in args: filters.append(["is_private", "=", args["is_private"]]) result = await client.get_list( "File", fields=["name", "file_name", "file_url", "file_size", "is_private", "folder", "attached_to_doctype", "attached_to_name"], filters=filters if filters else None, limit=args.get("limit", 20), ) return json.dumps(result, indent=2) async def _get_file(args: dict) -> str: client = FrappeClient() result = await client.get_doc("File", args["name"]) return json.dumps(result, indent=2) async def _upload_file_base64(args: dict) -> str: client = FrappeClient() payload = { "filename": args["filename"], "filedata": args["content_base64"], "is_private": args.get("is_private", 1), "folder": args.get("folder", "Home/Attachments"), } if dt := args.get("attached_to_doctype"): payload["doctype"] = dt if name := args.get("attached_to_name"): payload["docname"] = name if field := args.get("attached_to_field"): payload["fieldname"] = field result = await client.call_method("frappe.client.attach_file", **payload) return json.dumps(result, indent=2) async def _delete_file(args: dict) -> str: client = FrappeClient() result = await client.delete_doc("File", args["name"]) return json.dumps(result, indent=2) async def _move_file(args: dict) -> str: client = FrappeClient() result = await client.update_doc("File", args["name"], {"folder": args["folder"]}) return json.dumps(result, indent=2) async def _create_folder(args: dict) -> str: client = FrappeClient() result = await client.call_method( "frappe.core.doctype.file.file.create_new_folder", file_name=args["folder_name"], folder=args.get("parent_folder", "Home"), ) return json.dumps(result, indent=2)