MOHAN 2ee93048e1 feat: Add tools for managing server scripts, client scripts, translations, assignment rules, user permissions, webhooks, API keys, and workflows
- Implemented server and client script management tools in `frappe_mcp/tools/scripts.py`
- Added translation and user permission management tools in `frappe_mcp/tools/translations.py`
- Created user and role management tools in `frappe_mcp/tools/users.py`
- Developed webhook and API key management tools in `frappe_mcp/tools/webhooks.py`
- Introduced workflow management tools in `frappe_mcp/tools/workflow_tools.py`
- Added `pyproject.toml` for project metadata and dependencies
2026-04-21 20:26:45 +05:30

391 lines
13 KiB
Python

"""
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)