- 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
391 lines
13 KiB
Python
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)
|