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