MCP-Frappe/frappe_mcp/tools/translations.py
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

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)