- 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
245 lines
9.6 KiB
Python
245 lines
9.6 KiB
Python
"""Translation, Assignment Rules, and User Permission Restriction tools."""
|
|
|
|
import json
|
|
from mcp.types import Tool
|
|
from frappe_mcp.client.frappe_api import FrappeClient
|
|
|
|
|
|
def tools() -> list[Tool]:
|
|
return [
|
|
# --- Translations ---
|
|
Tool(
|
|
name="frappe_list_translations",
|
|
description="List custom translations for a language.",
|
|
inputSchema={
|
|
"type": "object",
|
|
"properties": {
|
|
"language": {"type": "string", "description": "Language code e.g. 'ar', 'fr', 'de'"},
|
|
"source_text": {"type": "string"},
|
|
"limit": {"type": "integer", "default": 50},
|
|
},
|
|
},
|
|
),
|
|
Tool(
|
|
name="frappe_add_translation",
|
|
description="Add or update a custom translation string.",
|
|
inputSchema={
|
|
"type": "object",
|
|
"required": ["language", "source_text", "translated_text"],
|
|
"properties": {
|
|
"language": {"type": "string"},
|
|
"source_text": {"type": "string"},
|
|
"translated_text": {"type": "string"},
|
|
"context": {"type": "string"},
|
|
},
|
|
},
|
|
),
|
|
Tool(
|
|
name="frappe_remove_translation",
|
|
description="Remove a custom translation entry.",
|
|
inputSchema={
|
|
"type": "object",
|
|
"required": ["name"],
|
|
"properties": {"name": {"type": "string"}},
|
|
},
|
|
),
|
|
# --- Assignment Rules ---
|
|
Tool(
|
|
name="frappe_list_assignment_rules",
|
|
description="List all Assignment Rules.",
|
|
inputSchema={
|
|
"type": "object",
|
|
"properties": {
|
|
"document_type": {"type": "string"},
|
|
"disabled": {"type": "integer"},
|
|
"limit": {"type": "integer", "default": 20},
|
|
},
|
|
},
|
|
),
|
|
Tool(
|
|
name="frappe_create_assignment_rule",
|
|
description=(
|
|
"Create an Assignment Rule — automatically assigns documents to users "
|
|
"based on conditions (load balancing or rule-based)."
|
|
),
|
|
inputSchema={
|
|
"type": "object",
|
|
"required": ["name", "document_type", "assign_condition"],
|
|
"properties": {
|
|
"name": {"type": "string"},
|
|
"document_type": {"type": "string"},
|
|
"assign_condition": {"type": "string", "description": "Python expression e.g. doc.status == 'Open'"},
|
|
"unassign_condition": {"type": "string"},
|
|
"close_condition": {"type": "string"},
|
|
"assignment_days": {
|
|
"type": "array",
|
|
"items": {"type": "object"},
|
|
"description": "Days config for round-robin assignment",
|
|
},
|
|
"users": {
|
|
"type": "array",
|
|
"items": {"type": "object"},
|
|
"description": "List of {user} objects",
|
|
},
|
|
"due_date_based_on": {"type": "string"},
|
|
"priority": {"type": "string", "enum": ["Low", "Medium", "High"]},
|
|
"disabled": {"type": "integer", "default": 0},
|
|
},
|
|
},
|
|
),
|
|
# --- User Permissions (Row-Level Security) ---
|
|
Tool(
|
|
name="frappe_list_user_permissions",
|
|
description="List User Permissions — row-level security rules that restrict which documents a user can see.",
|
|
inputSchema={
|
|
"type": "object",
|
|
"properties": {
|
|
"user": {"type": "string"},
|
|
"allow": {"type": "string", "description": "DocType being restricted e.g. 'Company'"},
|
|
"limit": {"type": "integer", "default": 30},
|
|
},
|
|
},
|
|
),
|
|
Tool(
|
|
name="frappe_add_user_permission",
|
|
description=(
|
|
"Add a User Permission — restricts a user to only see documents "
|
|
"linked to a specific value. e.g. restrict user to one Company."
|
|
),
|
|
inputSchema={
|
|
"type": "object",
|
|
"required": ["user", "allow", "for_value"],
|
|
"properties": {
|
|
"user": {"type": "string"},
|
|
"allow": {"type": "string", "description": "DocType e.g. 'Company'"},
|
|
"for_value": {"type": "string", "description": "Specific document name"},
|
|
"apply_to_all_doctypes": {"type": "integer", "default": 1},
|
|
"applicable_for": {"type": "string", "description": "Restrict to specific DocType if not global"},
|
|
"hide_descendants": {"type": "integer", "default": 0},
|
|
},
|
|
},
|
|
),
|
|
Tool(
|
|
name="frappe_remove_user_permission",
|
|
description="Remove a User Permission.",
|
|
inputSchema={
|
|
"type": "object",
|
|
"required": ["name"],
|
|
"properties": {"name": {"type": "string"}},
|
|
},
|
|
),
|
|
# --- Role Permission for Page/Report ---
|
|
Tool(
|
|
name="frappe_set_role_permission",
|
|
description="Grant or revoke a role's access to a Page or Report.",
|
|
inputSchema={
|
|
"type": "object",
|
|
"required": ["doctype", "name", "role", "action"],
|
|
"properties": {
|
|
"doctype": {"type": "string", "enum": ["Page", "Report"]},
|
|
"name": {"type": "string"},
|
|
"role": {"type": "string"},
|
|
"action": {"type": "string", "enum": ["add", "remove"]},
|
|
},
|
|
},
|
|
),
|
|
]
|
|
|
|
|
|
def handlers() -> dict:
|
|
return {
|
|
"frappe_list_translations": _list_translations,
|
|
"frappe_add_translation": _add_translation,
|
|
"frappe_remove_translation": _remove_translation,
|
|
"frappe_list_assignment_rules": _list_assignment_rules,
|
|
"frappe_create_assignment_rule": _create_assignment_rule,
|
|
"frappe_list_user_permissions": _list_user_permissions,
|
|
"frappe_add_user_permission": _add_user_permission,
|
|
"frappe_remove_user_permission": _remove_user_permission,
|
|
"frappe_set_role_permission": _set_role_permission,
|
|
}
|
|
|
|
|
|
async def _list_translations(args: dict) -> str:
|
|
client = FrappeClient()
|
|
filters = []
|
|
if lang := args.get("language"):
|
|
filters.append(["language", "=", lang])
|
|
if src := args.get("source_text"):
|
|
filters.append(["source_text", "like", f"%{src}%"])
|
|
result = await client.get_list(
|
|
"Translation",
|
|
fields=["name", "language", "source_text", "translated_text", "context"],
|
|
filters=filters if filters else None,
|
|
limit=args.get("limit", 50),
|
|
)
|
|
return json.dumps(result, indent=2)
|
|
|
|
async def _add_translation(args: dict) -> str:
|
|
client = FrappeClient()
|
|
result = await client.create_doc("Translation", args)
|
|
return json.dumps(result, indent=2)
|
|
|
|
async def _remove_translation(args: dict) -> str:
|
|
client = FrappeClient()
|
|
result = await client.delete_doc("Translation", args["name"])
|
|
return json.dumps(result, indent=2)
|
|
|
|
async def _list_assignment_rules(args: dict) -> str:
|
|
client = FrappeClient()
|
|
filters = []
|
|
if dt := args.get("document_type"):
|
|
filters.append(["document_type", "=", dt])
|
|
if "disabled" in args:
|
|
filters.append(["disabled", "=", args["disabled"]])
|
|
result = await client.get_list(
|
|
"Assignment Rule",
|
|
fields=["name", "document_type", "disabled", "priority"],
|
|
filters=filters if filters else None,
|
|
limit=args.get("limit", 20),
|
|
)
|
|
return json.dumps(result, indent=2)
|
|
|
|
async def _create_assignment_rule(args: dict) -> str:
|
|
client = FrappeClient()
|
|
result = await client.create_doc("Assignment Rule", args)
|
|
return json.dumps(result, indent=2)
|
|
|
|
async def _list_user_permissions(args: dict) -> str:
|
|
client = FrappeClient()
|
|
filters = []
|
|
if user := args.get("user"):
|
|
filters.append(["user", "=", user])
|
|
if allow := args.get("allow"):
|
|
filters.append(["allow", "=", allow])
|
|
result = await client.get_list(
|
|
"User Permission",
|
|
fields=["name", "user", "allow", "for_value", "applicable_for", "apply_to_all_doctypes"],
|
|
filters=filters if filters else None,
|
|
limit=args.get("limit", 30),
|
|
)
|
|
return json.dumps(result, indent=2)
|
|
|
|
async def _add_user_permission(args: dict) -> str:
|
|
client = FrappeClient()
|
|
result = await client.create_doc("User Permission", args)
|
|
return json.dumps(result, indent=2)
|
|
|
|
async def _remove_user_permission(args: dict) -> str:
|
|
client = FrappeClient()
|
|
result = await client.delete_doc("User Permission", args["name"])
|
|
return json.dumps(result, indent=2)
|
|
|
|
async def _set_role_permission(args: dict) -> str:
|
|
client = FrappeClient()
|
|
method = "frappe.desk.doctype.desktop_icon.desktop_icon.set_hidden"
|
|
doc = await client.get_doc(args["doctype"], args["name"])
|
|
roles = doc.get("roles", [])
|
|
if args["action"] == "add":
|
|
if args["role"] not in [r.get("role") for r in roles]:
|
|
roles.append({"role": args["role"]})
|
|
else:
|
|
roles = [r for r in roles if r.get("role") != args["role"]]
|
|
result = await client.update_doc(args["doctype"], args["name"], {"roles": roles})
|
|
return json.dumps(result, indent=2)
|