- 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
215 lines
7.4 KiB
Python
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)
|