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

215 lines
7.4 KiB
Python

"""Webhook and API Key management tools."""
import json
from mcp.types import Tool
from frappe_mcp.client.frappe_api import FrappeClient
def tools() -> list[Tool]:
return [
Tool(
name="frappe_list_webhooks",
description="List all configured Webhooks.",
inputSchema={
"type": "object",
"properties": {
"webhook_doctype": {"type": "string"},
"enabled": {"type": "integer"},
"limit": {"type": "integer", "default": 20},
},
},
),
Tool(
name="frappe_get_webhook",
description="Get a Webhook configuration.",
inputSchema={
"type": "object",
"required": ["name"],
"properties": {"name": {"type": "string"}},
},
),
Tool(
name="frappe_create_webhook",
description="Create a new Webhook triggered by a DocType event.",
inputSchema={
"type": "object",
"required": ["webhook_doctype", "webhook_docevent", "request_url"],
"properties": {
"webhook_doctype": {"type": "string"},
"webhook_docevent": {
"type": "string",
"enum": ["after_insert", "on_update", "on_submit", "on_cancel", "on_trash"],
},
"request_url": {"type": "string"},
"request_method": {"type": "string", "enum": ["POST", "PUT", "GET"], "default": "POST"},
"enabled": {"type": "integer", "default": 1},
"condition": {"type": "string", "description": "Python expression e.g. doc.status == 'Submitted'"},
"webhook_headers": {
"type": "array",
"items": {"type": "object"},
"description": "List of {key, value} header objects",
},
"webhook_data": {
"type": "array",
"items": {"type": "object"},
"description": "List of {key, fieldname} objects for payload fields",
},
},
},
),
Tool(
name="frappe_update_webhook",
description="Enable, disable, or update a Webhook.",
inputSchema={
"type": "object",
"required": ["name"],
"properties": {
"name": {"type": "string"},
"enabled": {"type": "integer"},
"updates": {"type": "object"},
},
},
),
Tool(
name="frappe_delete_webhook",
description="Delete a Webhook.",
inputSchema={
"type": "object",
"required": ["name"],
"properties": {"name": {"type": "string"}},
},
),
Tool(
name="frappe_list_webhook_logs",
description="Get recent Webhook request logs.",
inputSchema={
"type": "object",
"properties": {
"webhook": {"type": "string"},
"limit": {"type": "integer", "default": 20},
},
},
),
# --- API Keys ---
Tool(
name="frappe_list_api_keys",
description="List API Key records (does not expose secrets).",
inputSchema={
"type": "object",
"properties": {"limit": {"type": "integer", "default": 20}},
},
),
Tool(
name="frappe_generate_api_key",
description="Generate a new API key + secret for a Frappe user.",
inputSchema={
"type": "object",
"required": ["user"],
"properties": {
"user": {"type": "string", "description": "User email"},
},
},
),
Tool(
name="frappe_revoke_api_key",
description="Revoke (delete) API key for a user.",
inputSchema={
"type": "object",
"required": ["user"],
"properties": {
"user": {"type": "string"},
},
},
),
]
def handlers() -> dict:
return {
"frappe_list_webhooks": _list_webhooks,
"frappe_get_webhook": _get_webhook,
"frappe_create_webhook": _create_webhook,
"frappe_update_webhook": _update_webhook,
"frappe_delete_webhook": _delete_webhook,
"frappe_list_webhook_logs": _list_webhook_logs,
"frappe_list_api_keys": _list_api_keys,
"frappe_generate_api_key": _generate_api_key,
"frappe_revoke_api_key": _revoke_api_key,
}
async def _list_webhooks(args: dict) -> str:
client = FrappeClient()
filters = []
if dt := args.get("webhook_doctype"):
filters.append(["webhook_doctype", "=", dt])
if "enabled" in args:
filters.append(["enabled", "=", args["enabled"]])
result = await client.get_list(
"Webhook",
fields=["name", "webhook_doctype", "webhook_docevent", "request_url", "enabled"],
filters=filters if filters else None,
limit=args.get("limit", 20),
)
return json.dumps(result, indent=2)
async def _get_webhook(args: dict) -> str:
client = FrappeClient()
result = await client.get_doc("Webhook", args["name"])
return json.dumps(result, indent=2)
async def _create_webhook(args: dict) -> str:
client = FrappeClient()
result = await client.create_doc("Webhook", args)
return json.dumps(result, indent=2)
async def _update_webhook(args: dict) -> str:
client = FrappeClient()
updates = args.get("updates", {})
if "enabled" in args:
updates["enabled"] = args["enabled"]
result = await client.update_doc("Webhook", args["name"], updates)
return json.dumps(result, indent=2)
async def _delete_webhook(args: dict) -> str:
client = FrappeClient()
result = await client.delete_doc("Webhook", args["name"])
return json.dumps(result, indent=2)
async def _list_webhook_logs(args: dict) -> str:
client = FrappeClient()
filters = []
if wh := args.get("webhook"):
filters.append(["webhook", "=", wh])
result = await client.get_list(
"Webhook Log",
fields=["name", "webhook", "status", "request_headers", "data", "response", "creation"],
filters=filters if filters else None,
limit=args.get("limit", 20),
order_by="creation desc",
)
return json.dumps(result, indent=2)
async def _list_api_keys(args: dict) -> str:
client = FrappeClient()
result = await client.get_list(
"User",
fields=["name", "api_key", "modified"],
filters=[["api_key", "!=", ""]],
limit=args.get("limit", 20),
)
return json.dumps(result, indent=2)
async def _generate_api_key(args: dict) -> str:
client = FrappeClient()
result = await client.call_method(
"frappe.core.doctype.user.user.generate_keys",
user=args["user"],
)
return json.dumps(result, indent=2)
async def _revoke_api_key(args: dict) -> str:
client = FrappeClient()
result = await client.update_doc("User", args["user"], {"api_key": "", "api_secret": ""})
return json.dumps(result, indent=2)