""" Activity, collaboration, and system log tools. Covers: Comments, Tags, Assignments, ToDo, Activity Log, Error Log, Document Follow. """ import json from mcp.types import Tool from frappe_mcp.client.frappe_api import FrappeClient def tools() -> list[Tool]: return [ # --- Comments --- Tool( name="frappe_add_comment", description="Add a comment to any document.", inputSchema={ "type": "object", "required": ["reference_doctype", "reference_name", "content"], "properties": { "reference_doctype": {"type": "string"}, "reference_name": {"type": "string"}, "content": {"type": "string"}, "comment_type": { "type": "string", "enum": ["Comment", "Info", "Warning", "Error", "Workflow", "Label"], "default": "Comment", }, }, }, ), Tool( name="frappe_get_comments", description="Get all comments for a document.", inputSchema={ "type": "object", "required": ["reference_doctype", "reference_name"], "properties": { "reference_doctype": {"type": "string"}, "reference_name": {"type": "string"}, }, }, ), # --- Tags --- Tool( name="frappe_add_tag", description="Add a tag to a document.", inputSchema={ "type": "object", "required": ["doctype", "name", "tag"], "properties": { "doctype": {"type": "string"}, "name": {"type": "string"}, "tag": {"type": "string"}, }, }, ), Tool( name="frappe_remove_tag", description="Remove a tag from a document.", inputSchema={ "type": "object", "required": ["doctype", "name", "tag"], "properties": { "doctype": {"type": "string"}, "name": {"type": "string"}, "tag": {"type": "string"}, }, }, ), Tool( name="frappe_get_tags", description="Get all tags on a document.", inputSchema={ "type": "object", "required": ["doctype", "name"], "properties": { "doctype": {"type": "string"}, "name": {"type": "string"}, }, }, ), # --- Assignments --- Tool( name="frappe_assign_document", description="Assign a document to one or more users.", inputSchema={ "type": "object", "required": ["doctype", "name", "assign_to"], "properties": { "doctype": {"type": "string"}, "name": {"type": "string"}, "assign_to": { "type": "array", "items": {"type": "string"}, "description": "List of user emails to assign", }, "description": {"type": "string"}, "due_date": {"type": "string", "description": "YYYY-MM-DD"}, "priority": {"type": "string", "enum": ["Low", "Medium", "High"], "default": "Medium"}, "notify": {"type": "integer", "default": 0}, }, }, ), Tool( name="frappe_remove_assignment", description="Remove assignment of a document from a user.", inputSchema={ "type": "object", "required": ["doctype", "name", "assign_to"], "properties": { "doctype": {"type": "string"}, "name": {"type": "string"}, "assign_to": {"type": "string", "description": "User email to unassign"}, }, }, ), Tool( name="frappe_get_assignments", description="Get all active assignments for a document.", inputSchema={ "type": "object", "required": ["doctype", "name"], "properties": { "doctype": {"type": "string"}, "name": {"type": "string"}, }, }, ), # --- ToDo --- Tool( name="frappe_create_todo", description="Create a ToDo item (task) optionally linked to a document.", inputSchema={ "type": "object", "required": ["description"], "properties": { "description": {"type": "string"}, "assigned_by": {"type": "string"}, "owner": {"type": "string", "description": "Assigned to user"}, "reference_type": {"type": "string"}, "reference_name": {"type": "string"}, "due_date": {"type": "string"}, "priority": {"type": "string", "enum": ["Low", "Medium", "High"], "default": "Medium"}, "status": {"type": "string", "enum": ["Open", "Closed"], "default": "Open"}, }, }, ), # --- Activity Log --- Tool( name="frappe_get_activity_log", description="Get the activity log for a specific document (who changed what and when).", inputSchema={ "type": "object", "required": ["doctype", "name"], "properties": { "doctype": {"type": "string"}, "name": {"type": "string"}, "limit": {"type": "integer", "default": 20}, }, }, ), # --- Error Logs --- Tool( name="frappe_get_error_logs", description="Get recent system error logs.", inputSchema={ "type": "object", "properties": { "limit": {"type": "integer", "default": 20}, "method": {"type": "string", "description": "Filter by method name"}, }, }, ), Tool( name="frappe_clear_error_logs", description="Clear all error logs.", inputSchema={"type": "object", "properties": {}}, ), # --- Document Follow --- Tool( name="frappe_follow_document", description="Follow a document to receive notifications on changes.", inputSchema={ "type": "object", "required": ["doctype", "name"], "properties": { "doctype": {"type": "string"}, "name": {"type": "string"}, }, }, ), Tool( name="frappe_unfollow_document", description="Unfollow a document.", inputSchema={ "type": "object", "required": ["doctype", "name"], "properties": { "doctype": {"type": "string"}, "name": {"type": "string"}, }, }, ), ] def handlers() -> dict: return { "frappe_add_comment": _add_comment, "frappe_get_comments": _get_comments, "frappe_add_tag": _add_tag, "frappe_remove_tag": _remove_tag, "frappe_get_tags": _get_tags, "frappe_assign_document": _assign_document, "frappe_remove_assignment": _remove_assignment, "frappe_get_assignments": _get_assignments, "frappe_create_todo": _create_todo, "frappe_get_activity_log": _get_activity_log, "frappe_get_error_logs": _get_error_logs, "frappe_clear_error_logs": _clear_error_logs, "frappe_follow_document": _follow_document, "frappe_unfollow_document": _unfollow_document, } async def _add_comment(args: dict) -> str: client = FrappeClient() result = await client.call_method( "frappe.client.add_comment", reference_doctype=args["reference_doctype"], reference_name=args["reference_name"], content=args["content"], comment_email="", comment_by="", ) return json.dumps(result, indent=2) async def _get_comments(args: dict) -> str: client = FrappeClient() result = await client.get_list( "Comment", fields=["name", "comment_type", "comment_by", "content", "creation"], filters=[ ["reference_doctype", "=", args["reference_doctype"]], ["reference_name", "=", args["reference_name"]], ], limit=50, order_by="creation asc", ) return json.dumps(result, indent=2) async def _add_tag(args: dict) -> str: client = FrappeClient() result = await client.call_method( "frappe.desk.doctype.tag.tag.add_tag", tag=args["tag"], dt=args["doctype"], dn=args["name"], ) return json.dumps(result, indent=2) async def _remove_tag(args: dict) -> str: client = FrappeClient() result = await client.call_method( "frappe.desk.doctype.tag.tag.remove_tag", tag=args["tag"], dt=args["doctype"], dn=args["name"], ) return json.dumps(result, indent=2) async def _get_tags(args: dict) -> str: client = FrappeClient() result = await client.call_method( "frappe.desk.doctype.tag.tag.get_tags", dt=args["doctype"], dn=args["name"], ) return json.dumps(result, indent=2) async def _assign_document(args: dict) -> str: client = FrappeClient() result = await client.call_method( "frappe.desk.form.assign_to.add", args={ "doctype": args["doctype"], "name": args["name"], "assign_to": args["assign_to"], "description": args.get("description", ""), "due_date": args.get("due_date", ""), "priority": args.get("priority", "Medium"), "notify": args.get("notify", 0), }, ) return json.dumps(result, indent=2) async def _remove_assignment(args: dict) -> str: client = FrappeClient() result = await client.call_method( "frappe.desk.form.assign_to.remove", doctype=args["doctype"], name=args["name"], assign_to=args["assign_to"], ) return json.dumps(result, indent=2) async def _get_assignments(args: dict) -> str: client = FrappeClient() result = await client.get_list( "ToDo", fields=["name", "owner", "description", "due_date", "priority", "status"], filters=[ ["reference_type", "=", args["doctype"]], ["reference_name", "=", args["name"]], ["status", "=", "Open"], ], limit=20, ) return json.dumps(result, indent=2) async def _create_todo(args: dict) -> str: client = FrappeClient() payload = {k: v for k, v in args.items()} result = await client.create_doc("ToDo", payload) return json.dumps(result, indent=2) async def _get_activity_log(args: dict) -> str: client = FrappeClient() result = await client.get_list( "Version", fields=["name", "owner", "creation", "data"], filters=[ ["ref_doctype", "=", args["doctype"]], ["docname", "=", args["name"]], ], limit=args.get("limit", 20), order_by="creation desc", ) return json.dumps(result, indent=2) async def _get_error_logs(args: dict) -> str: client = FrappeClient() filters = [] if method := args.get("method"): filters.append(["method", "like", f"%{method}%"]) result = await client.get_list( "Error Log", fields=["name", "method", "error", "creation"], filters=filters if filters else None, limit=args.get("limit", 20), order_by="creation desc", ) return json.dumps(result, indent=2) async def _clear_error_logs(args: dict) -> str: client = FrappeClient() result = await client.call_method("frappe.core.doctype.error_log.error_log.clear_error_logs") return json.dumps(result, indent=2) async def _follow_document(args: dict) -> str: client = FrappeClient() result = await client.call_method( "frappe.desk.form.document_follow.follow_document", doctype=args["doctype"], doc_name=args["name"], ) return json.dumps(result, indent=2) async def _unfollow_document(args: dict) -> str: client = FrappeClient() result = await client.call_method( "frappe.desk.form.document_follow.unfollow_document", doctype=args["doctype"], doc_name=args["name"], ) return json.dumps(result, indent=2)